Merge "Increase the setup-sdp delay to cover for all the devices." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index 1a694a8..2c02347 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -6519,6 +6519,7 @@
     field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
     field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
     field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+    field public static final java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
     field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -6794,7 +6795,7 @@
     field public static final int NAVIGATION_WHEEL = 4; // 0x4
     field public static final int ORIENTATION_LANDSCAPE = 2; // 0x2
     field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
-    field public static final int ORIENTATION_SQUARE = 3; // 0x3
+    field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
     field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
     field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
@@ -6811,7 +6812,7 @@
     field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
     field public static final int TOUCHSCREEN_FINGER = 3; // 0x3
     field public static final int TOUCHSCREEN_NOTOUCH = 1; // 0x1
-    field public static final int TOUCHSCREEN_STYLUS = 2; // 0x2
+    field public static final deprecated int TOUCHSCREEN_STYLUS = 2; // 0x2
     field public static final int TOUCHSCREEN_UNDEFINED = 0; // 0x0
     field public static final int UI_MODE_NIGHT_MASK = 48; // 0x30
     field public static final int UI_MODE_NIGHT_NO = 16; // 0x10
@@ -19690,6 +19691,7 @@
     method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
     field public static final java.lang.String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
     field public static final java.lang.String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
+    field public static final java.lang.String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
     field public static final java.lang.String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
     field public static final java.lang.String DETAILS_META_DATA = "android.speech.DETAILS";
     field public static final java.lang.String EXTRA_CALLING_PACKAGE = "calling_package";
@@ -19705,6 +19707,7 @@
     field public static final java.lang.String EXTRA_RESULTS = "android.speech.extra.RESULTS";
     field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
     field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
+    field public static final java.lang.String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
diff --git a/api/current.txt b/api/current.txt
index 1a694a8..2c02347 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6519,6 +6519,7 @@
     field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
     field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
     field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+    field public static final java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
     field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
     field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -6794,7 +6795,7 @@
     field public static final int NAVIGATION_WHEEL = 4; // 0x4
     field public static final int ORIENTATION_LANDSCAPE = 2; // 0x2
     field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
-    field public static final int ORIENTATION_SQUARE = 3; // 0x3
+    field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
     field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
     field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
@@ -6811,7 +6812,7 @@
     field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
     field public static final int TOUCHSCREEN_FINGER = 3; // 0x3
     field public static final int TOUCHSCREEN_NOTOUCH = 1; // 0x1
-    field public static final int TOUCHSCREEN_STYLUS = 2; // 0x2
+    field public static final deprecated int TOUCHSCREEN_STYLUS = 2; // 0x2
     field public static final int TOUCHSCREEN_UNDEFINED = 0; // 0x0
     field public static final int UI_MODE_NIGHT_MASK = 48; // 0x30
     field public static final int UI_MODE_NIGHT_NO = 16; // 0x10
@@ -19690,6 +19691,7 @@
     method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
     field public static final java.lang.String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
     field public static final java.lang.String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
+    field public static final java.lang.String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
     field public static final java.lang.String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
     field public static final java.lang.String DETAILS_META_DATA = "android.speech.DETAILS";
     field public static final java.lang.String EXTRA_CALLING_PACKAGE = "calling_package";
@@ -19705,6 +19707,7 @@
     field public static final java.lang.String EXTRA_RESULTS = "android.speech.extra.RESULTS";
     field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
     field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
+    field public static final java.lang.String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
     field public static final java.lang.String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2baad62..bcdd012 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1050,6 +1050,17 @@
     public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+     * on a television.  Television here is defined to be a typical living
+     * room television experience: displayed on a big screen, where the user
+     * is sitting far away from it, and the dominant form of input will be
+     * something like a DPAD, not through touch or mouse.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEVISION = "android.hardware.type.television";
+
+    /**
      * Action to external storage service to clean out removed apps.
      * @hide
      */
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 079f739..423b9af 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -42,17 +42,23 @@
     public float fontScale;
 
     /**
-     * IMSI MCC (Mobile Country Code).  0 if undefined.
+     * IMSI MCC (Mobile Country Code), corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+     * resource qualifier.  0 if undefined.
      */
     public int mcc;
     
     /**
-     * IMSI MNC (Mobile Network Code).  0 if undefined.
+     * IMSI MNC (Mobile Network Code), corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+     * resource qualifier.  0 if undefined.
      */
     public int mnc;
     
     /**
-     * Current user preference for the locale.
+     * Current user preference for the locale, corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+     * resource qualifier.
      */
     public Locale locale;
 
@@ -69,29 +75,52 @@
      * value indicating that no size has been set. */
     public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
     /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
-     * value indicating the screen is at least approximately 320x426 dp units.
+     * value indicating the screen is at least approximately 320x426 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+     * resource qualifier.
      * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
      * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
     /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
-     * value indicating the screen is at least approximately 320x470 dp units.
+     * value indicating the screen is at least approximately 320x470 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+     * resource qualifier.
      * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
      * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
     /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
-     * value indicating the screen is at least approximately 480x640 dp units.
+     * value indicating the screen is at least approximately 480x640 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+     * resource qualifier.
      * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
      * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
     /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
-     * value indicating the screen is at least approximately 720x960 dp units.
+     * value indicating the screen is at least approximately 720x960 dp units,
+     * corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+     * resource qualifier.
      * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
      * Multiple Screens</a> for more information.*/
     public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
-    
+
+    /** Constant for {@link #screenLayout}: bits that encode the aspect ratio. */
     public static final int SCREENLAYOUT_LONG_MASK = 0x30;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value indicating that no size has been set. */
     public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+     * resource qualifier. */
     public static final int SCREENLAYOUT_LONG_NO = 0x10;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+     * resource qualifier. */
     public static final int SCREENLAYOUT_LONG_YES = 0x20;
     
     /**
@@ -135,21 +164,38 @@
         return cur >= size;
     }
 
+    /** Constant for {@link #touchscreen}: a value indicating that no value has been set. */
     public static final int TOUCHSCREEN_UNDEFINED = 0;
+    /** Constant for {@link #touchscreen}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+     * resource qualifier. */
     public static final int TOUCHSCREEN_NOTOUCH = 1;
-    public static final int TOUCHSCREEN_STYLUS = 2;
+    /** @deprecated Not currently supported or used. */
+    @Deprecated public static final int TOUCHSCREEN_STYLUS = 2;
+    /** Constant for {@link #touchscreen}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+     * resource qualifier. */
     public static final int TOUCHSCREEN_FINGER = 3;
     
     /**
      * The kind of touch screen attached to the device.
-     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS}, 
-     * {@link #TOUCHSCREEN_FINGER}. 
+     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
      */
     public int touchscreen;
-    
+
+    /** Constant for {@link #keyboard}: a value indicating that no value has been set. */
     public static final int KEYBOARD_UNDEFINED = 0;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+     * resource qualifier. */
     public static final int KEYBOARD_NOKEYS = 1;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+     * resource qualifier. */
     public static final int KEYBOARD_QWERTY = 2;
+    /** Constant for {@link #keyboard}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+     * resource qualifier. */
     public static final int KEYBOARD_12KEY = 3;
     
     /**
@@ -158,9 +204,16 @@
      * {@link #KEYBOARD_12KEY}.
      */
     public int keyboard;
-    
+
+    /** Constant for {@link #keyboardHidden}: a value indicating that no value has been set. */
     public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #keyboardHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+     * resource qualifier. */
     public static final int KEYBOARDHIDDEN_NO = 1;
+    /** Constant for {@link #keyboardHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+     * resource qualifier. */
     public static final int KEYBOARDHIDDEN_YES = 2;
     /** Constant matching actual resource implementation. {@hide} */
     public static final int KEYBOARDHIDDEN_SOFT = 3;
@@ -174,8 +227,13 @@
      */
     public int keyboardHidden;
     
+    /** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
     public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+     * physical keyboard being exposed. */
     public static final int HARDKEYBOARDHIDDEN_NO = 1;
+    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+     * physical keyboard being hidden. */
     public static final int HARDKEYBOARDHIDDEN_YES = 2;
     
     /**
@@ -186,10 +244,23 @@
      */
     public int hardKeyboardHidden;
     
+    /** Constant for {@link #navigation}: a value indicating that no value has been set. */
     public static final int NAVIGATION_UNDEFINED = 0;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+     * resource qualifier. */
     public static final int NAVIGATION_NONAV = 1;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+     * resource qualifier. */
     public static final int NAVIGATION_DPAD = 2;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+     * resource qualifier. */
     public static final int NAVIGATION_TRACKBALL = 3;
+    /** Constant for {@link #navigation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+     * resource qualifier. */
     public static final int NAVIGATION_WHEEL = 4;
     
     /**
@@ -199,8 +270,15 @@
      */
     public int navigation;
     
+    /** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
     public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
+    /** Constant for {@link #navigationHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+     * resource qualifier. */
     public static final int NAVIGATIONHIDDEN_NO = 1;
+    /** Constant for {@link #navigationHidden}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+     * resource qualifier. */
     public static final int NAVIGATIONHIDDEN_YES = 2;
     
     /**
@@ -211,29 +289,70 @@
      */
     public int navigationHidden;
     
+    /** Constant for {@link #orientation}: a value indicating that no value has been set. */
     public static final int ORIENTATION_UNDEFINED = 0;
+    /** Constant for {@link #orientation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+     * resource qualifier. */
     public static final int ORIENTATION_PORTRAIT = 1;
+    /** Constant for {@link #orientation}, value corresponding to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+     * resource qualifier. */
     public static final int ORIENTATION_LANDSCAPE = 2;
-    public static final int ORIENTATION_SQUARE = 3;
+    /** @deprecated Not currently supported or used. */
+    @Deprecated public static final int ORIENTATION_SQUARE = 3;
     
     /**
      * Overall orientation of the screen.  May be one of
-     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
-     * or {@link #ORIENTATION_SQUARE}.
+     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
      */
     public int orientation;
 
+    /** Constant for {@link #uiMode}: bits that encode the mode type. */
     public static final int UI_MODE_TYPE_MASK = 0x0f;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value indicating that no mode type has been set. */
     public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">no
+     * UI mode</a> resource qualifier specified. */
     public static final int UI_MODE_TYPE_NORMAL = 0x01;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a>
+     * resource qualifier. */
     public static final int UI_MODE_TYPE_DESK = 0x02;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">car</a>
+     * resource qualifier. */
     public static final int UI_MODE_TYPE_CAR = 0x03;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">television</a>
+     * resource qualifier. */
     public static final int UI_MODE_TYPE_TELEVISION = 0x04;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a>
+     * resource qualifier. */
     public static final int UI_MODE_TYPE_APPLIANCE = 0x05;
 
+    /** Constant for {@link #uiMode}: bits that encode the night mode. */
     public static final int UI_MODE_NIGHT_MASK = 0x30;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value indicating that no mode type has been set. */
     public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">notnight</a>
+     * resource qualifier. */
     public static final int UI_MODE_NIGHT_NO = 0x10;
+    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+     * value that corresponds to the
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">night</a>
+     * resource qualifier. */
     public static final int UI_MODE_NIGHT_YES = 0x20;
 
     /**
@@ -253,21 +372,30 @@
     public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
 
     /**
-     * The current width of the available screen space, in dp units.
+     * The current width of the available screen space, in dp units,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenWidthQualifier">screen
+     * width</a> resource qualifier.
      */
     public int screenWidthDp;
 
     public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
 
     /**
-     * The current height of the available screen space, in dp units.
+     * The current height of the available screen space, in dp units,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenHeightQualifier">screen
+     * height</a> resource qualifier.
      */
     public int screenHeightDp;
 
     public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
 
     /**
-     * The smallest screen size an application will see in normal operation.
+     * The smallest screen size an application will see in normal operation,
+     * corresponding to
+     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest
+     * screen width</a> resource qualifier.
      * This is the smallest value of both screenWidthDp and screenHeightDp
      * in both portrait and landscape.
      */
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6448b55..dfd35e1 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -77,7 +77,8 @@
      * The meta-data specifies a resource that contains a description of each keyboard
      * layout that is provided by the application.
      * <pre><code>
-     * &lt;receiver android:name=".InputDeviceReceiver">
+     * &lt;receiver android:name=".InputDeviceReceiver"
+     *         android:label="@string/keyboard_layouts_label">
      *     &lt;intent-filter>
      *         &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
      *     &lt;/intent-filter>
@@ -90,7 +91,9 @@
      * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
      * contains zero or more <code>&lt;keyboard-layout></code> elements.
      * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
-     * of a key character map for a particular keyboard layout.
+     * of a key character map for a particular keyboard layout.  The label on the receiver
+     * is used to name the collection of keyboard layouts provided by this receiver in the
+     * keyboard layout settings.
      * <pre></code>
      * &lt;?xml version="1.0" encoding="utf-8"?>
      * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index e75a6dc..5402e75 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -28,6 +28,7 @@
         Comparable<KeyboardLayout> {
     private final String mDescriptor;
     private final String mLabel;
+    private final String mCollection;
 
     public static final Parcelable.Creator<KeyboardLayout> CREATOR =
             new Parcelable.Creator<KeyboardLayout>() {
@@ -39,14 +40,16 @@
         }
     };
 
-    public KeyboardLayout(String descriptor, String label) {
+    public KeyboardLayout(String descriptor, String label, String collection) {
         mDescriptor = descriptor;
         mLabel = label;
+        mCollection = collection;
     }
 
     private KeyboardLayout(Parcel source) {
         mDescriptor = source.readString();
         mLabel = source.readString();
+        mCollection = source.readString();
     }
 
     /**
@@ -68,6 +71,15 @@
         return mLabel;
     }
 
+    /**
+     * Gets the name of the collection to which the keyboard layout belongs.  This is
+     * the label of the broadcast receiver or application that provided the keyboard layout.
+     * @return The keyboard layout collection name.
+     */
+    public String getCollection() {
+        return mCollection;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -77,15 +89,23 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mDescriptor);
         dest.writeString(mLabel);
+        dest.writeString(mCollection);
     }
 
     @Override
     public int compareTo(KeyboardLayout another) {
-        return mLabel.compareToIgnoreCase(another.mLabel);
+        int result = mLabel.compareToIgnoreCase(another.mLabel);
+        if (result == 0) {
+            result = mCollection.compareToIgnoreCase(another.mCollection);
+        }
+        return result;
     }
 
     @Override
     public String toString() {
-        return mLabel;
+        if (mCollection.isEmpty()) {
+            return mLabel;
+        }
+        return mLabel + " - " + mCollection;
     }
 }
\ No newline at end of file
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 7ffa575..53b41d5 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -299,7 +299,23 @@
             callback = state.uriCallback;
         }
         if (callback != null) {
-            return callback.createBeamUris(mDefaultEvent);
+            uris = callback.createBeamUris(mDefaultEvent);
+            if (uris != null) {
+                for (Uri uri : uris) {
+                    if (uri == null) {
+                        Log.e(TAG, "Uri not allowed to be null.");
+                        return null;
+                    }
+                    String scheme = uri.getScheme();
+                    if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+                            !scheme.equalsIgnoreCase("content"))) {
+                        Log.e(TAG, "Uri needs to have " +
+                                "either scheme file or scheme content");
+                        return null;
+                    }
+                }
+            }
+            return uris;
         } else {
             return uris;
         }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 7bf9feb..4464d58 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -584,17 +584,138 @@
         }
     }
 
-    //TODO: make sure NFC service has permission for URI
-    //TODO: see if we will eventually support multiple URIs
-    //TODO: javadoc
+    /**
+     * Set one or more {@link Uri}s to send using Android Beam (TM). Every
+     * Uri you provide must have either scheme 'file' or scheme 'content'.
+     *
+     * <p>For the data provided through this method, Android Beam tries to
+     * switch to alternate transports such as Bluetooth to achieve a fast
+     * transfer speed. Hence this method is very suitable
+     * for transferring large files such as pictures or songs.
+     *
+     * <p>The receiving side will store the content of each Uri in
+     * a file and present a notification to the user to open the file
+     * with a {@link android.content.Intent} with action
+     * {@link android.content.Intent#ACTION_VIEW}.
+     * If multiple URIs are sent, the {@link android.content.Intent} will refer
+     * to the first of the stored files.
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the URI(s) are only made available for Android Beam when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+     * have priority over both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback}.
+     *
+     * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+     * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+     * then the Uri push will be completely disabled for the specified activity(s).
+     *
+     * <p>Code example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setBeamPushUris(new Uri[] {uri1, uri2}, this);
+     * }
+     * </pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the Uri(s) and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p>If your Activity wants to dynamically supply Uri(s),
+     * then set a callback using {@link #setBeamPushUrisCallback} instead
+     * of using this method.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param uris an array of Uri(s) to push over Android Beam
+     * @param activity activity for which the Uri(s) will be pushed
+     */
     public void setBeamPushUris(Uri[] uris, Activity activity) {
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
         }
+        if (uris != null) {
+            for (Uri uri : uris) {
+                if (uri == null) throw new NullPointerException("Uri not " +
+                        "allowed to be null");
+                String scheme = uri.getScheme();
+                if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+                        !scheme.equalsIgnoreCase("content"))) {
+                    throw new IllegalArgumentException("URI needs to have " +
+                            "either scheme file or scheme content");
+                }
+            }
+        }
         mNfcActivityManager.setNdefPushContentUri(activity, uris);
     }
 
-    // TODO javadoc
+    /**
+     * Set a callback that will dynamically generate one or more {@link Uri}s
+     * to send using Android Beam (TM). Every Uri the callback provides
+     * must have either scheme 'file' or scheme 'content'.
+     *
+     * <p>For the data provided through this callback, Android Beam tries to
+     * switch to alternate transports such as Bluetooth to achieve a fast
+     * transfer speed. Hence this method is very suitable
+     * for transferring large files such as pictures or songs.
+     *
+     * <p>The receiving side will store the content of each Uri in
+     * a file and present a notification to the user to open the file
+     * with a {@link android.content.Intent} with action
+     * {@link android.content.Intent#ACTION_VIEW}.
+     * If multiple URIs are sent, the {@link android.content.Intent} will refer
+     * to the first of the stored files.
+     *
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the URI(s) are only made available for Android Beam when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
+     *
+     * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+     * have priority over both {@link #setNdefPushMessage} and
+     * {@link #setNdefPushMessageCallback}.
+     *
+     * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+     * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+     * then the Uri push will be completely disabled for the specified activity(s).
+     *
+     * <p>Code example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setBeamPushUrisCallback(callback, this);
+     * }
+     * </pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the Uri(s) and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param callback callback, or null to disable
+     * @param activity activity for which the Uri(s) will be pushed
+     */
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
@@ -663,6 +784,10 @@
      * {@link Activity#onDestroy}. This is guaranteed if you call this API
      * during {@link Activity#onCreate}.
      *
+     * <p class="note">For sending large content such as pictures and songs,
+     * consider using {@link #setBeamPushUris}, which switches to alternate transports
+     * such as Bluetooth to achieve a fast transfer rate.
+     *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param message NDEF message to push over NFC, or null to disable
@@ -753,7 +878,9 @@
      * <p class="note">Do not pass in an Activity that has already been through
      * {@link Activity#onDestroy}. This is guaranteed if you call this API
      * during {@link Activity#onCreate}.
-     *
+     * <p class="note">For sending large content such as pictures and songs,
+     * consider using {@link #setBeamPushUris}, which switches to alternate transports
+     * such as Bluetooth to achieve a fast transfer rate.
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param callback callback, or null to disable
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index f4abda6..ab64866 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1360,7 +1360,14 @@
      */
     public Parcelable[] getVolumeList() throws RemoteException;
 
-    public String getSecureContainerFilesystemPath(String id) throws RemoteException;
+    /**
+     * Gets the path on the filesystem for the ASEC container itself.
+     * 
+     * @param cid ASEC container ID
+     * @return path to filesystem or {@code null} if it's not found
+     * @throws RemoteException
+     */
+    public String getSecureContainerFilesystemPath(String cid) throws RemoteException;
 
     /*
      * Fix permissions in a container which has just been created and populated.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ea3cab4..4c86d9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4266,6 +4266,13 @@
                 "contacts_preauth_uri_expiration";
 
         /**
+         * Prefix for SMS short code regex patterns (country code is appended).
+         * @see com.android.internal.telephony.SmsUsageMonitor
+         * @hide
+         */
+        public static final String SMS_SHORT_CODES_PREFIX = "sms_short_codes_";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index fd709f2..457e66c 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -115,6 +115,45 @@
     public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
 
     /**
+     * Starts an activity that will prompt the user for speech without requiring the user's
+     * visual attention or touch input. It will send it through a speech recognizer,
+     * and either synthesize speech for a web search result or trigger
+     * another type of action based on the user's speech.
+     *
+     * This activity may be launched while device is locked in a secure mode.
+     * Special care must be taken to ensure that the voice actions that are performed while
+     * hands free cannot compromise the device's security.
+     * The activity should check the value of the {@link #EXTRA_SECURE} extra to determine
+     * whether the device has been securely locked. If so, the activity should either restrict
+     * the set of voice actions that are permitted or require some form of secure
+     * authentication before proceeding.
+     *
+     * To ensure that the activity's user interface is visible while the lock screen is showing,
+     * the activity should set the
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} window flag.
+     * Otherwise the activity's user interface may be hidden by the lock screen. The activity
+     * should take care not to leak private information when the device is securely locked.
+     *
+     * <p>Optional extras:
+     * <ul>
+     *   <li>{@link #EXTRA_SECURE}
+     * </ul>
+     */
+    public static final String ACTION_VOICE_SEARCH_HANDS_FREE =
+            "android.speech.action.VOICE_SEARCH_HANDS_FREE";
+
+    /**
+     * Optional boolean to indicate that a "hands free" voice search was performed while the device
+     * was in a secure mode. An example of secure mode is when the device's screen lock is active,
+     * and it requires some form of authentication to be unlocked.
+     *
+     * When the device is securely locked, the voice search activity should either restrict
+     * the set of voice actions that are permitted, or require some form of secure authentication
+     * before proceeding.
+     */
+    public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
+
+    /**
      * The minimum length of an utterance. We will not stop recording before this amount of time.
      * 
      * Note that it is extremely rare you'd want to specify this value in an intent. If you don't
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 386c866d..cd54f24 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -287,12 +287,12 @@
                     }
                 }
             }
-            while (start < textLength && mText.charAt(start) == '\n') {
-                start++;
-            }
             if (start < 0) {
                 return null;
             }
+            while (start < textLength && mText.charAt(start) == '\n') {
+                start++;
+            }
             int end = start;
             for (int i = end + 1; i < textLength; i++) {
                 end = i;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 2048de2..411aed3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,6 +23,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.Log;
 
@@ -353,7 +354,12 @@
                     synchronized (mLock) {
                         mUpdateLayer = true;
                     }
-                    postInvalidate();
+
+                    if (Looper.myLooper() == Looper.getMainLooper()) {
+                        invalidate();
+                    } else {
+                        postInvalidate();
+                    }
                 }
             };
             mSurface.setOnFrameAvailableListener(mUpdateListener);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d62f513..d94275b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -691,13 +691,6 @@
          */
         public static final int FLAG_NEEDS_MENU_KEY = 0x08000000;
 
-        /** Window flag: *sigh* The lock screen wants to continue running its
-         * animation while it is fading.  A kind-of hack to allow this.  Maybe
-         * in the future we just make this the default behavior.
-         *
-         * {@hide} */
-        public static final int FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000;
-        
         /** Window flag: special flag to limit the size of the window to be
          * original size ([320x480] x density). Used to create window for applications
          * running under compatibility mode.
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 915e2dc..cc490bd 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -17,25 +17,37 @@
 package android.webkit;
 
 import android.content.Context;
-import android.os.Vibrator;
+import android.os.Bundle;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.view.KeyEvent;
+import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.webkit.WebViewCore.EventHub;
 
 import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Handles injecting accessibility JavaScript and related JavaScript -> Java
  * 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;
 
@@ -47,10 +59,12 @@
 
     // The Java objects that are exposed to JavaScript.
     private TextToSpeech mTextToSpeech;
+    private CallbackHandler mCallback;
 
     // Lazily loaded helper objects.
     private AccessibilityManager mAccessibilityManager;
-    private AccessibilityInjectorFallback mAccessibilityInjector;
+    private AccessibilityInjectorFallback mAccessibilityInjectorFallback;
+    private JSONObject mAccessibilityJSONObject;
 
     // Whether the accessibility script has been injected into the current page.
     private boolean mAccessibilityScriptInjected;
@@ -61,8 +75,11 @@
     @SuppressWarnings("unused")
     private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
 
-    // Aliases for Java objects exposed to JavaScript.
-    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
+    // Alias for TTS API exposed to JavaScript.
+    private static final String ALIAS_TTS_JS_INTERFACE = "accessibility";
+
+    // Alias for traversal callback exposed to JavaScript.
+    private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal";
 
     // Template for JavaScript that injects a screen-reader.
     private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
@@ -73,6 +90,10 @@
                     "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
                     "  })();";
 
+    // Template for JavaScript that performs AndroidVox actions.
+    private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
+            "cvox.AndroidVox.performAction('%1s')";
+
     /**
      * Creates an instance of the AccessibilityInjector based on
      * {@code webViewClassic}.
@@ -99,6 +120,7 @@
         }
 
         addTtsApis();
+        addCallbackApis();
     }
 
     /**
@@ -109,6 +131,81 @@
      */
     public void removeAccessibilityApisIfNecessary() {
         removeTtsApis();
+        removeCallbackApis();
+    }
+
+    /**
+     * Initializes an {@link AccessibilityNodeInfo} with the actions and
+     * movement granularity levels supported by this
+     * {@link AccessibilityInjector}.
+     * <p>
+     * If an action identifier is added in this method, this
+     * {@link AccessibilityInjector} should also return {@code true} from
+     * {@link #supportsAccessibilityAction(int)}.
+     * </p>
+     *
+     * @param info The info to initialize.
+     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
+     */
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+        info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
+        info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
+        info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
+        info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
+        info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+        info.setClickable(true);
+    }
+
+    /**
+     * Returns {@code true} if this {@link AccessibilityInjector} should handle
+     * the specified action.
+     *
+     * @param action An accessibility action identifier.
+     * @return {@code true} if this {@link AccessibilityInjector} should handle
+     *         the specified action.
+     */
+    public boolean supportsAccessibilityAction(int action) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
+            case AccessibilityNodeInfo.ACTION_CLICK:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Performs the specified accessibility action.
+     *
+     * @param action The identifier of the action to perform.
+     * @param arguments The action arguments, or {@code null} if no arguments.
+     * @return {@code true} if the action was successful.
+     * @see View#performAccessibilityAction(int, Bundle)
+     */
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (!isAccessibilityEnabled()) {
+            mAccessibilityScriptInjected = false;
+            toggleFallbackAccessibilityInjector(false);
+            return false;
+        }
+
+        if (mAccessibilityScriptInjected) {
+            return sendActionToAndroidVox(action, arguments);
+        }
+        
+        if (mAccessibilityInjectorFallback != null) {
+            return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments);
+        }
+
+        return false;
     }
 
     /**
@@ -140,11 +237,11 @@
             return true;
         }
 
-        if (mAccessibilityInjector != null) {
+        if (mAccessibilityInjectorFallback != null) {
             // if an accessibility injector is present (no JavaScript enabled or
             // the site opts out injecting our JavaScript screen reader) we let
             // it decide whether to act on and consume the event.
-            return mAccessibilityInjector.onKeyEvent(event);
+            return mAccessibilityInjectorFallback.onKeyEvent(event);
         }
 
         return false;
@@ -157,8 +254,8 @@
      * @param selectionString The selection string.
      */
     public void handleSelectionChangedIfNecessary(String selectionString) {
-        if (mAccessibilityInjector != null) {
-            mAccessibilityInjector.onSelectionStringChange(selectionString);
+        if (mAccessibilityInjectorFallback != null) {
+            mAccessibilityInjectorFallback.onSelectionStringChange(selectionString);
         }
     }
 
@@ -206,10 +303,10 @@
      *            {@code false} to disable it.
      */
     private void toggleFallbackAccessibilityInjector(boolean enabled) {
-        if (enabled && (mAccessibilityInjector == null)) {
-            mAccessibilityInjector = new AccessibilityInjectorFallback(mWebViewClassic);
+        if (enabled && (mAccessibilityInjectorFallback == null)) {
+            mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic);
         } else {
-            mAccessibilityInjector = null;
+            mAccessibilityInjectorFallback = null;
         }
     }
 
@@ -261,7 +358,7 @@
         final String pkgName = mContext.getPackageName();
 
         mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
-        mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+        mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
     }
 
     /**
@@ -273,12 +370,30 @@
             return;
         }
 
-        mWebView.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
+        mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE);
         mTextToSpeech.stop();
         mTextToSpeech.shutdown();
         mTextToSpeech = null;
     }
 
+    private void addCallbackApis() {
+        if (mCallback != null) {
+            return;
+        }
+
+        mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
+        mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE);
+    }
+
+    private void removeCallbackApis() {
+        if (mCallback == null) {
+            return;
+        }
+
+        mWebView.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE);
+        mCallback = null;
+    }
+
     /**
      * Returns the script injection preference requested by the URL, or
      * {@link #ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED} if the page has no
@@ -347,4 +462,169 @@
     private boolean isAccessibilityEnabled() {
         return mAccessibilityManager.isEnabled();
     }
+
+    /**
+     * Packs an accessibility action into a JSON object and sends it to AndroidVox.
+     *
+     * @param action The action identifier.
+     * @param arguments The action arguments, if applicable.
+     * @return The result of the action.
+     */
+    private boolean sendActionToAndroidVox(int action, Bundle arguments) {
+        if (mAccessibilityJSONObject == null) {
+            mAccessibilityJSONObject = new JSONObject();
+        } else {
+            // Remove all keys from the object.
+            final Iterator<?> keys = mAccessibilityJSONObject.keys();
+            while (keys.hasNext()) {
+                keys.next();
+                keys.remove();
+            }
+        }
+
+        try {
+            mAccessibilityJSONObject.accumulate("action", action);
+
+            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);
+                    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);
+                    break;
+            }
+        } catch (JSONException e) {
+            return false;
+        }
+
+        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));
+    }
+
+    /**
+     * Exposes result interface to JavaScript.
+     */
+    private static class CallbackHandler {
+        private static final String JAVASCRIPT_ACTION_TEMPLATE =
+                "javascript:(function() { %s.onResult(%d, %s); })();";
+
+        // Time in milliseconds to wait for a result before failing.
+        private static final long RESULT_TIMEOUT = 200;
+
+        private final AtomicInteger mResultIdCounter = new AtomicInteger();
+        private final Object mResultLock = new Object();
+        private final String mInterfaceName;
+
+        private String mResult = null;
+        private long mResultId = -1;
+
+        private CallbackHandler(String interfaceName) {
+            mInterfaceName = interfaceName;
+        }
+
+        /**
+         * Performs an action and attempts to wait for a result.
+         *
+         * @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) {
+            final int resultId = mResultIdCounter.getAndIncrement();
+            final String url = String.format(
+                    JAVASCRIPT_ACTION_TEMPLATE, mInterfaceName, resultId, code);
+            webView.loadUrl(url);
+
+            return getResultAndClear(resultId, defaultResult);
+        }
+
+        /**
+         * 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) {
+            synchronized (mResultLock) {
+                final boolean success = waitForResultTimedLocked(resultId);
+                final String result = success ? mResult : defaultResult;
+                clearResultLocked();
+                return result;
+            }
+        }
+
+        /**
+         * Clears the result state.
+         */
+        private void clearResultLocked() {
+            mResultId = -1;
+            mResult = null;
+        }
+
+        /**
+         * Waits up to a given bound for a result of a request and returns it.
+         *
+         * @param resultId The result id to match the result with the request.
+         * @return Whether the result was received.
+         */
+        private boolean waitForResultTimedLocked(int resultId) {
+            long waitTimeMillis = RESULT_TIMEOUT;
+            final long startTimeMillis = SystemClock.uptimeMillis();
+            while (true) {
+                try {
+                    if (mResultId == resultId) {
+                        return true;
+                    }
+                    if (mResultId > resultId) {
+                        return false;
+                    }
+                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                    waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
+                    if (waitTimeMillis <= 0) {
+                        return false;
+                    }
+                    mResultLock.wait(waitTimeMillis);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
+
+        /**
+         * Callback exposed to JavaScript. Handles returning the result of a
+         * request to a waiting (or potentially timed out) thread.
+         *
+         * @param id The result id of the request as a {@link String}.
+         * @param result The result of the request as a {@link String}.
+         */
+        @SuppressWarnings("unused")
+        public void onResult(String id, String result) {
+            final long resultId;
+
+            try {
+                resultId = Long.parseLong(id);
+            } catch (NumberFormatException e) {
+                return;
+            }
+
+            synchronized (mResultLock) {
+                if (resultId > mResultId) {
+                    mResult = result;
+                    mResultId = resultId;
+                }
+                mResultLock.notifyAll();
+            }
+        }
+    }
 }
diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java
index 9bf52ca..4d9c26c 100644
--- a/core/java/android/webkit/AccessibilityInjectorFallback.java
+++ b/core/java/android/webkit/AccessibilityInjectorFallback.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
@@ -23,6 +24,7 @@
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.webkit.WebViewCore.EventHub;
 
 import java.util.ArrayList;
@@ -48,7 +50,7 @@
  * {@link #setCurrentAxis(int, boolean, String)}, or
  * {@link #traverseCurrentAxis(int, boolean, String)}
  * {@link #traverseGivenAxis(int, int, boolean, String)}
- * {@link #prefromAxisTransition(int, int, boolean, String)}
+ * {@link #performAxisTransition(int, int, boolean, String)}
  * referred via the values of:
  * {@link #ACTION_SET_CURRENT_AXIS},
  * {@link #ACTION_TRAVERSE_CURRENT_AXIS},
@@ -72,15 +74,30 @@
     private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
     private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4;
 
-    // the default WebView behavior abstracted as a navigation axis
+    // WebView navigation axes from WebViewCore.h, plus an additional axis for
+    // the default behavior.
+    private static final int NAVIGATION_AXIS_CHARACTER = 0;
+    private static final int NAVIGATION_AXIS_WORD = 1;
+    private static final int NAVIGATION_AXIS_SENTENCE = 2;
+    @SuppressWarnings("unused")
+    private static final int NAVIGATION_AXIS_HEADING = 3;
+    private static final int NAVIGATION_AXIS_SIBLING = 5;
+    @SuppressWarnings("unused")
+    private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5;
+    private static final int NAVIGATION_AXIS_DOCUMENT = 6;
     private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;
 
+    // WebView navigation directions from WebViewCore.h.
+    private static final int NAVIGATION_DIRECTION_BACKWARD = 0;
+    private static final int NAVIGATION_DIRECTION_FORWARD = 1;
+
     // these are the same for all instances so make them process wide
     private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
         new ArrayList<AccessibilityWebContentKeyBinding>();
 
     // handle to the WebViewClassic this injector is associated with.
     private final WebViewClassic mWebView;
+    private final WebView mWebViewInternal;
 
     // events scheduled for sending as soon as we receive the selected text
     private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
@@ -104,6 +121,7 @@
      */
     public AccessibilityInjectorFallback(WebViewClassic webView) {
         mWebView = webView;
+        mWebViewInternal = mWebView.getWebView();
         ensureWebContentKeyBindings();
     }
 
@@ -176,7 +194,7 @@
                     int fromAxis = binding.getFirstArgument(i);
                     int toAxis = binding.getSecondArgument(i);
                     sendEvent = (binding.getThirdArgument(i) == 1);
-                    prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
+                    performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
                     mLastDownEventHandled = true;
                     break;
                 case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS:
@@ -214,7 +232,8 @@
     private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
         mCurrentAxis = axis;
         if (sendEvent) {
-            AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent();
+            final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(
+                    AccessibilityEvent.TYPE_ANNOUNCEMENT);
             event.getText().add(String.valueOf(axis));
             event.setContentDescription(contentDescription);
             sendAccessibilityEvent(event);
@@ -229,7 +248,7 @@
      * @param sendEvent Flag if to send an event to announce successful transition.
      * @param contentDescription A description of the performed action.
      */
-    private void prefromAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
+    private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
             String contentDescription) {
         if (mCurrentAxis == fromAxis) {
             setCurrentAxis(toAxis, sendEvent, contentDescription);
@@ -249,6 +268,62 @@
             String contentDescription) {
         return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription);
     }
+    
+    boolean performAccessibilityAction(int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_NEXT_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);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Returns the {@link WebView}-defined direction for the given
+     * {@link AccessibilityNodeInfo}-defined action.
+     * 
+     * @param action An accessibility action identifier.
+     * @return A web view navigation direction.
+     */
+    private static int getDirectionForAction(int action) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+                return NAVIGATION_DIRECTION_FORWARD;
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+                return NAVIGATION_DIRECTION_BACKWARD;
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * Returns the {@link WebView}-defined axis for the given
+     * {@link AccessibilityNodeInfo}-defined granularity.
+     * 
+     * @param granularity An accessibility granularity identifier.
+     * @return A web view navigation axis.
+     */
+    private static int getAxisForGranularity(int granularity) {
+        switch (granularity) {
+            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER:
+                return NAVIGATION_AXIS_CHARACTER;
+            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD:
+                return NAVIGATION_AXIS_WORD;
+            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;
+            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE:
+                return NAVIGATION_AXIS_DOCUMENT;
+            default:
+                return -1;
+        }
+    }
 
     /**
      * Traverse the document along the given navigation axis.
@@ -268,7 +343,8 @@
 
         AccessibilityEvent event = null;
         if (sendEvent) {
-            event = getPartialyPopulatedAccessibilityEvent();
+            event = getPartialyPopulatedAccessibilityEvent(
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
             // the text will be set upon receiving the selection string
             event.setContentDescription(contentDescription);
         }
@@ -296,8 +372,10 @@
             return;
         }
         AccessibilityEvent event = mScheduledEventStack.pop();
-        if (event != null) {
+        if ((event != null) && (selectionString != null)) {
             event.getText().add(selectionString);
+            event.setFromIndex(0);
+            event.setToIndex(selectionString.length());
             sendAccessibilityEvent(event);
         }
     }
@@ -323,11 +401,9 @@
      * @return An accessibility event whose members are populated except its
      *         text and content description.
      */
-    private AccessibilityEvent getPartialyPopulatedAccessibilityEvent() {
-        AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED);
-        event.setClassName(mWebView.getClass().getName());
-        event.setPackageName(mWebView.getContext().getPackageName());
-        event.setEnabled(mWebView.getWebView().isEnabled());
+    private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) {
+        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        mWebViewInternal.onInitializeAccessibilityEvent(event);
         return event;
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ba5a417..cbb3011 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1686,6 +1686,10 @@
             WebView.super.computeScroll();
         }
 
+        public boolean super_performAccessibilityAction(int action, Bundle arguments) {
+            return WebView.super.performAccessibilityAction(action, arguments);
+        }
+
         public boolean super_performLongClick() {
             return WebView.super.performLongClick();
         }
@@ -1938,6 +1942,11 @@
         mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
     }
 
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        return mProvider.getViewDelegate().performAccessibilityAction(action, arguments);
+    }
+
     /** @hide */
     @Override
     protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index ee24e5e..7f43552 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -100,7 +100,6 @@
 import android.webkit.WebViewCore.EventHub;
 import android.webkit.WebViewCore.TextFieldInitData;
 import android.webkit.WebViewCore.TextSelectionData;
-import android.webkit.WebViewCore.TouchHighlightData;
 import android.webkit.WebViewCore.WebKitHitTest;
 import android.widget.AbsoluteLayout;
 import android.widget.Adapter;
@@ -274,6 +273,7 @@
                 newCursorPosition -= text.length() - limitedText.length();
             }
             super.setComposingText(limitedText, newCursorPosition);
+            updateSelection();
             if (limitedText != text) {
                 restartInput();
                 int lastCaret = start + limitedText.length();
@@ -286,19 +286,44 @@
         @Override
         public boolean commitText(CharSequence text, int newCursorPosition) {
             setComposingText(text, newCursorPosition);
-            int cursorPosition = Selection.getSelectionEnd(getEditable());
-            setComposingRegion(cursorPosition, cursorPosition);
+            finishComposingText();
             return true;
         }
 
         @Override
         public boolean deleteSurroundingText(int leftLength, int rightLength) {
-            Editable editable = getEditable();
-            int cursorPosition = Selection.getSelectionEnd(editable);
-            int startDelete = Math.max(0, cursorPosition - leftLength);
-            int endDelete = Math.min(editable.length(),
-                    cursorPosition + rightLength);
-            setNewText(startDelete, endDelete, "");
+            // This code is from BaseInputConnection#deleteSurroundText.
+            // We have to delete the same text in webkit.
+            Editable content = getEditable();
+            int a = Selection.getSelectionStart(content);
+            int b = Selection.getSelectionEnd(content);
+
+            if (a > b) {
+                int tmp = a;
+                a = b;
+                b = tmp;
+            }
+
+            int ca = getComposingSpanStart(content);
+            int cb = getComposingSpanEnd(content);
+            if (cb < ca) {
+                int tmp = ca;
+                ca = cb;
+                cb = tmp;
+            }
+            if (ca != -1 && cb != -1) {
+                if (ca < a) a = ca;
+                if (cb > b) b = cb;
+            }
+
+            int endDelete = Math.min(content.length(), b + rightLength);
+            if (endDelete > b) {
+                setNewText(b, endDelete, "");
+            }
+            int startDelete = Math.max(0, a - leftLength);
+            if (startDelete < a) {
+                setNewText(startDelete, a, "");
+            }
             return super.deleteSurroundingText(leftLength, rightLength);
         }
 
@@ -411,6 +436,46 @@
             outAttrs.imeOptions = mImeOptions;
             outAttrs.hintText = mHint;
             outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
+
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            if (selectionStart < 0 || selectionEnd < 0) {
+                selectionStart = editable.length();
+                selectionEnd = selectionStart;
+            }
+            outAttrs.initialSelStart = selectionStart;
+            outAttrs.initialSelEnd = selectionEnd;
+        }
+
+        @Override
+        public boolean setSelection(int start, int end) {
+            boolean result = super.setSelection(start, end);
+            updateSelection();
+            return result;
+        }
+
+        @Override
+        public boolean setComposingRegion(int start, int end) {
+            boolean result = super.setComposingRegion(start, end);
+            updateSelection();
+            return result;
+        }
+
+        /**
+         * Send the selection and composing spans to the IME.
+         */
+        private void updateSelection() {
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            int composingStart = getComposingSpanStart(editable);
+            int composingEnd = getComposingSpanEnd(editable);
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                imm.updateSelection(mWebView, selectionStart, selectionEnd,
+                        composingStart, composingEnd);
+            }
         }
 
         /**
@@ -1599,8 +1664,65 @@
     }
 
     @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (!mWebView.isEnabled()) {
+            // Only default actions are supported while disabled.
+            return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
+        }
+
+        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
+            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
+        }
+
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                final int convertedContentHeight = contentToViewY(getContentHeight());
+                final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+                        - mWebView.getPaddingBottom();
+                final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
+                final boolean canScrollBackward = (getScrollY() > 0);
+                final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
+                if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
+                    mWebView.scrollBy(0, adjustedViewHeight);
+                    return true;
+                }
+                if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
+                    mWebView.scrollBy(0, -adjustedViewHeight);
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        if (!mWebView.isEnabled()) {
+            // Only default actions are supported while disabled.
+            return;
+        }
+
         info.setScrollable(isScrollableForAccessibility());
+
+        final int convertedContentHeight = contentToViewY(getContentHeight());
+        final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+                - mWebView.getPaddingBottom();
+        final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
+        final boolean canScrollBackward = (getScrollY() > 0);
+        final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
+
+        if (canScrollForward) {
+            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        }
+
+        if (canScrollForward) {
+            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+        }
+
+        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
     }
 
     @Override
@@ -4965,6 +5087,7 @@
 
     private void adjustSelectionCursors() {
         if (mIsCaretSelection) {
+            syncSelectionCursors();
             return; // no need to swap left and right handles.
         }
 
@@ -7410,6 +7533,11 @@
                 invalidate();
             }
         }
+
+        @Override
+        public void clearPreviousHitTest() {
+            setHitTestResult(null);
+        }
     }
 
     private void setHitTestTypeFromUrl(String url) {
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index c5a86d8..feff16e 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -399,7 +399,6 @@
         unscheduleHideTapHighlightLocked();
         unscheduleShowTapHighlightLocked();
         mUiCallbacks.showTapHighlight(true);
-        scheduleHideTapHighlightLocked();
     }
 
     private void scheduleShowTapHighlightLocked() {
@@ -466,13 +465,13 @@
                 return;
             }
             mPostClickScheduled = false;
-            showTapCandidateLocked();
 
             MotionEvent event = mPostTouchStream.getLastEvent();
             if (event == null || event.getAction() != MotionEvent.ACTION_UP) {
                 return;
             }
 
+            showTapCandidateLocked();
             MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
             DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_CLICK, 0,
                     mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
@@ -511,6 +510,7 @@
     }
 
     private void enqueueHitTestLocked(MotionEvent event) {
+        mUiCallbacks.clearPreviousHitTest();
         MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
         DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0,
                 mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
@@ -553,12 +553,17 @@
             mIsTapCandidate = true;
             mInitialDownX = event.getX();
             mInitialDownY = event.getY();
-            scheduleShowTapHighlightLocked();
             enqueueHitTestLocked(event);
+            if (mIsDoubleTapCandidate) {
+                hideTapCandidateLocked();
+            } else {
+                scheduleShowTapHighlightLocked();
+            }
         } else if (action == MotionEvent.ACTION_UP) {
             unscheduleLongPressLocked();
             if (isClickCandidateLocked(event)) {
                 if (mIsDoubleTapCandidate) {
+                    hideTapCandidateLocked();
                     enqueueDoubleTapLocked(event);
                 } else {
                     scheduleClickLocked();
@@ -666,6 +671,10 @@
                 if (event != null && recycleEvent) {
                     event.recycle();
                 }
+
+                if (eventType == EVENT_TYPE_CLICK) {
+                    scheduleHideTapHighlightLocked();
+                }
             }
         }
     }
@@ -802,6 +811,10 @@
                     d.mEvent = null; // retain ownership of event, don't recycle it yet
                 }
                 recycleDispatchEventLocked(d);
+
+                if (eventType == EVENT_TYPE_CLICK) {
+                    scheduleHideTapHighlightLocked();
+                }
             }
 
             // Handle the event.
@@ -1050,6 +1063,12 @@
          * @param show True if it should show the highlight, false if it should hide it
          */
         public void showTapHighlight(boolean show);
+
+        /**
+         * Called when we are sending a new EVENT_TYPE_HIT_TEST to WebKit, so
+         * previous hit tests should be cleared as they are obsolete.
+         */
+        public void clearPreviousHitTest();
     }
 
     /* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 74a215c..867ee54 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -276,6 +276,8 @@
 
         public void onInitializeAccessibilityEvent(AccessibilityEvent event);
 
+        public boolean performAccessibilityAction(int action, Bundle arguments);
+
         public void setOverScrollMode(int mode);
 
         public void setScrollBarStyle(int style);
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index 9ae61bc..f2aaab7 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -16,6 +16,7 @@
 extern jfieldID gOptions_heightFieldID;
 extern jfieldID gOptions_mimeFieldID;
 extern jfieldID gOptions_mCancelID;
+extern jfieldID gOptions_bitmapFieldID;
 
 jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index dd8e84f..b218bcd 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -29,6 +29,7 @@
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "Utils.h"
 #include "JNIHelp.h"
+#include "SkTScopedPtr.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include "android_util_Binder.h"
@@ -180,7 +181,8 @@
  * reportSizeToVM not supported
  */
 static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
-        int start_x, int start_y, int width, int height, jobject options) {
+                                int start_x, int start_y, int width, int height, jobject options) {
+    jobject tileBitmap = NULL;
     SkImageDecoder *decoder = brd->getDecoder();
     int sampleSize = 1;
     SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
@@ -199,12 +201,12 @@
         doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
         preferQualityOverSpeed = env->GetBooleanField(options,
                 gOptions_preferQualityOverSpeedFieldID);
+        // Get the bitmap for re-use if it exists.
+        tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
     }
 
     decoder->setDitherImage(doDither);
     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
-    SkBitmap*           bitmap = new SkBitmap;
-    SkAutoTDelete<SkBitmap>       adb(bitmap);
     AutoDecoderCancel   adc(options, decoder);
 
     // To fix the race condition in case "requestCancelDecode"
@@ -219,6 +221,17 @@
     region.fTop = start_y;
     region.fRight = start_x + width;
     region.fBottom = start_y + height;
+    SkBitmap* bitmap = NULL;
+    SkTScopedPtr<SkBitmap> adb;
+
+    if (tileBitmap != NULL) {
+        // Re-use bitmap.
+        bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
+    }
+    if (bitmap == NULL) {
+        bitmap = new SkBitmap;
+        adb.reset(bitmap);
+    }
 
     if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
         return nullObjectReturn("decoder->decodeRegion returned false");
@@ -235,12 +248,12 @@
                             getMimeTypeString(env, decoder->getFormat()));
     }
 
-    // detach bitmap from its autodeleter, since we want to own it now
-    adb.detach();
+    if (tileBitmap != NULL) {
+        return tileBitmap;
+    }
 
-    SkPixelRef* pr = bitmap->pixelRef();
-    // promise we will never change our pixels (great for sharing and pictures)
-    pr->setImmutable();
+    // detach bitmap from its autodeleter, since we want to own it now
+    adb.release();
 
     JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
     jbyteArray buff = allocator->getStorageObjAndReset();
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..e591a7b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..ea27290d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
index 2fc475b..9c5147e 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
index 5adecf1..9c5147e 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
index 457fa84..a257e26 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
index c3cfc29..a257e26 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
index d0e1806..dd999d6 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
index c30506d..dd999d6 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
index 9106687..ea54380 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
index 2bdda56..ea54380 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..7dfea4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..99b0279
--- /dev/null
+++ b/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
index 0787d16..3d7c236 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
index 0157e68..3d7c236 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
index 51b14d0..82f05d6 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
index d68568a..82f05d6 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
index 6bf153a..9bc7a68 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
index 0d98983..9bc7a68 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
index 3cee7b8..670dc2e 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
index 43a7c4c..670dc2e 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..f01a79e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..7bea197
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
index a0e6b20..ca48bd8 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
index 88235fe..ca48bd8 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
index 04fb9a1..c3d80f0 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
index 06a14f3..c3d80f0 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
index af7d631..df236df 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
index d6ab3ea..df236df 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
index 5a8e807..4acb32b 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
index 392f3dc..4acb32b 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/activity_picker_bg.xml b/core/res/res/drawable/activity_picker_bg.xml
new file mode 100644
index 0000000..a471c94
--- /dev/null
+++ b/core/res/res/drawable/activity_picker_bg.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/activity_picker_bg_focused" />
+    <item android:state_activated="true"                                                             android:drawable="@drawable/activity_picker_bg_activated" />
+    <item android:drawable="@color/transparent" />
+
+</selector>
diff --git a/core/res/res/drawable/switch_track_holo_dark.xml b/core/res/res/drawable/switch_track_holo_dark.xml
index c9a940d..5f796c1 100644
--- a/core/res/res/drawable/switch_track_holo_dark.xml
+++ b/core/res/res/drawable/switch_track_holo_dark.xml
@@ -15,7 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:drawable="@drawable/switch_bg_disabled_holo_dark" />
     <item android:state_focused="true"  android:drawable="@drawable/switch_bg_focused_holo_dark" />
     <item                               android:drawable="@drawable/switch_bg_holo_dark" />
 </selector>
diff --git a/core/res/res/drawable/switch_track_holo_light.xml b/core/res/res/drawable/switch_track_holo_light.xml
index 98e53b5..39bee37 100644
--- a/core/res/res/drawable/switch_track_holo_light.xml
+++ b/core/res/res/drawable/switch_track_holo_light.xml
@@ -15,7 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:drawable="@drawable/switch_bg_disabled_holo_light" />
     <item android:state_focused="true"  android:drawable="@drawable/switch_bg_focused_holo_light" />
     <item                               android:drawable="@drawable/switch_bg_holo_light" />
 </selector>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
index efd406d..e6829a9 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
@@ -57,8 +57,6 @@
                 android:layout_height="wrap_content"
                 android:layout_marginLeft="50dip"
                 android:layout_marginTop="50dip"
-                android:layout_marginBottom="100dip"
-                android:layout_marginRight="64dip"
                 android:layout_alignParentTop="true"
                 android:layout_alignParentLeft="true"/>
 
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index abeb7ba..1d464aa 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,7 +22,7 @@
               android:orientation="vertical"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
-              android:background="?android:attr/activatedBackgroundIndicator"
+              android:background="@android:drawable/activity_picker_bg"
               android:padding="16dp">
 
     <!-- Extended activity info to distinguish between duplicate activity names -->
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 847681b..82b5e29 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -533,13 +533,19 @@
       <td>
         <code>car</code><br/>
         <code>desk</code><br/>
-        <code>television</code>
+        <code>television<br/>
+        <code>appliance</code>
       </td>
       <td>
         <ul class="nolist">
           <li>{@code car}: Device is displaying in a car dock</li>
           <li>{@code desk}: Device is displaying in a desk dock</li>
-          <li>{@code television}: Device is displaying on a television</li>
+          <li>{@code television}: Device is displaying on a television, providing
+          a "ten foot" experience where its UI is on a large screen that the
+          user is far away from, primarily oriented around DPAD or other
+          non-pointer interaction</li>
+          <li>{@code appliance}: Device is serving as an appliance, with
+          no display</li>
         </ul>
         <p><em>Added in API level 8, television added in API 13.</em></p>
         <p>This can change during the life of your application if the user places the device in a
@@ -610,15 +616,13 @@
       <td>Touchscreen type</td>
       <td>
         <code>notouch</code><br/>
-        <code>stylus</code><br/>
         <code>finger</code>
       </td>
       <td>
         <ul class="nolist">
           <li>{@code notouch}: Device does not have a touchscreen.</li>
-          <li>{@code stylus}: Device has a resistive touchscreen that's suited for use with a
-stylus.</li>
-          <li>{@code finger}: Device has a touchscreen.</li>
+          <li>{@code finger}: Device has a touchscreen that is intended to
+          be used through direction interaction of the user's finger.</li>
         </ul>
         <p>Also see the {@link android.content.res.Configuration#touchscreen} configuration field,
 which indicates the type of touchscreen on the device.</p>
@@ -694,7 +698,7 @@
 field, which indicates whether navigation keys are hidden.</p>
       </td>
     </tr>
-    <tr id="TouchQualifier">
+    <tr id="NavigationQualifier">
       <td>Primary non-touch navigation method</td>
       <td>
         <code>nonav</code><br/>
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 496e0c7..c1d3407 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -180,9 +180,9 @@
      */
     public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
         checkRecycled("decodeRegion called on recycled region decoder");
-        if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
-                || rect.bottom > getHeight())
-            throw new IllegalArgumentException("rectangle is not inside the image");
+        if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
+                || rect.top >= getHeight())
+            throw new IllegalArgumentException("rectangle is outside the image");
         return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
                 rect.right - rect.left, rect.bottom - rect.top, options);
     }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 91d8bc1..1299574 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -403,7 +403,7 @@
      * the named provider.  Periodically, the supplied LocationListener will
      * be called with the current Location or with status updates.
      *
-     * <p> It may take a while to receive the most recent location. If
+     * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
@@ -413,32 +413,61 @@
      * the {@link LocationListener#onProviderEnabled(String)} method will
      * be called and location updates will start again.
      *
-     * <p> The frequency of notification may be controlled using the
-     * minTime and minDistance parameters. If minTime is greater than 0,
-     * the LocationManager could potentially rest for minTime milliseconds
-     * between location updates to conserve power. If minDistance is greater than 0,
-     * a location will only be broadcasted if the device moves by minDistance meters.
-     * To obtain notifications as frequently as possible, set both parameters to 0.
+     * <p> The update interval can be controlled using the minTime parameter.
+     * The elapsed time between location updates will never be less than
+     * minTime, although it can be more depending on the Location Provider
+     * implementation and the update interval requested by other applications.
      *
-     * <p> Background services should be careful about setting a sufficiently high
-     * minTime so that the device doesn't consume too much power by keeping the
-     * GPS or wireless radios on all the time. In particular, values under 60000ms
-     * are not recommended.
+     * <p> Choosing a sensible value for minTime is important to conserve
+     * battery life. Each location update requires power from
+     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+     * possible while still providing a reasonable user experience.
+     * If your application is not in the foreground and showing
+     * location to the user then your application should avoid using an active
+     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+     * or greater. If your application is in the foreground and showing
+     * location to the user then it is appropriate to select a faster
+     * update interval.
+     *
+     * <p> The minDistance parameter can also be used to control the
+     * frequency of location updates. If it is greater than 0 then the
+     * location provider will only send your application an update when
+     * the location has changed by at least minDistance meters, AND
+     * at least minTime milliseconds have passed. However it is more
+     * difficult for location providers to save power using the minDistance
+     * parameter, so minTime should be the primary tool to conserving battery
+     * life.
+     *
+     * <p> If your application wants to passively observe location
+     * updates triggered by other applications, but not consume
+     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+     * This provider does not actively turn on or modify active location
+     * providers, so you do not need to be as careful about minTime and
+     * minDistance. However if your application performs heavy work
+     * on a location update (such as network activity) then you should
+     * select non-zero values for minTime and/or minDistance to rate-limit
+     * your update frequency in the case another application enables a
+     * location provider with extremely fast updates.
      *
      * <p> The calling thread must be a {@link android.os.Looper} thread such as
      * the main thread of the calling Activity.
      *
+     * <p class="note"> Prior to Jellybean, the minTime parameter was
+     * only a hint, and some location provider implementations ignored it.
+     * From Jellybean and onwards it is mandatory for Android compatible
+     * devices to observe both the minTime and minDistance parameters.
+     *
      * @param provider the name of the provider with which to register
-     * @param minTime the minimum time interval for notifications, in
-     * milliseconds. This field is only used as a hint to conserve power, and actual
-     * time between location updates may be greater or lesser than this value.
-     * @param minDistance the minimum distance interval for notifications,
-     * in meters
+     * @param minTime minimum time interval between location updates, in milliseconds
+     * @param minDistance minimum distance between location updates, in meters
      * @param listener a {#link LocationListener} whose
      * {@link LocationListener#onLocationChanged} method will be called for
      * each location update
      *
-     * @throws IllegalArgumentException if provider or listener is null
+     * @throws IllegalArgumentException if provider is null or doesn't exist
+     * on this device
+     * @throws IllegalArgumentException if listener is null
      * @throws RuntimeException if the calling thread has no Looper
      * @throws SecurityException if no suitable permission is present for the provider.
      */
@@ -458,7 +487,7 @@
      * the named provider.  Periodically, the supplied LocationListener will
      * be called with the current Location or with status updates.
      *
-     * <p> It may take a while to receive the most recent location. If
+     * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
@@ -468,32 +497,59 @@
      * the {@link LocationListener#onProviderEnabled(String)} method will
      * be called and location updates will start again.
      *
-     * <p> The frequency of notification may be controlled using the
-     * minTime and minDistance parameters. If minTime is greater than 0,
-     * the LocationManager could potentially rest for minTime milliseconds
-     * between location updates to conserve power. If minDistance is greater than 0,
-     * a location will only be broadcasted if the device moves by minDistance meters.
-     * To obtain notifications as frequently as possible, set both parameters to 0.
+     * <p> The update interval can be controlled using the minTime parameter.
+     * The elapsed time between location updates will never be less than
+     * minTime, although it can be more depending on the Location Provider
+     * implementation and the update interval requested by other applications.
      *
-     * <p> Background services should be careful about setting a sufficiently high
-     * minTime so that the device doesn't consume too much power by keeping the
-     * GPS or wireless radios on all the time. In particular, values under 60000ms
-     * are not recommended.
+     * <p> Choosing a sensible value for minTime is important to conserve
+     * battery life. Each location update requires power from
+     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+     * possible while still providing a reasonable user experience.
+     * If your application is not in the foreground and showing
+     * location to the user then your application should avoid using an active
+     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+     * or greater. If your application is in the foreground and showing
+     * location to the user then it is appropriate to select a faster
+     * update interval.
+     *
+     * <p> The minDistance parameter can also be used to control the
+     * frequency of location updates. If it is greater than 0 then the
+     * location provider will only send your application an update when
+     * the location has changed by at least minDistance meters, AND
+     * at least minTime milliseconds have passed. However it is more
+     * difficult for location providers to save power using the minDistance
+     * parameter, so minTime should be the primary tool to conserving battery
+     * life.
+     *
+     * <p> If your application wants to passively observe location
+     * updates triggered by other applications, but not consume
+     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+     * This provider does not actively turn on or modify active location
+     * providers, so you do not need to be as careful about minTime and
+     * minDistance. However if your application performs heavy work
+     * on a location update (such as network activity) then you should
+     * select non-zero values for minTime and/or minDistance to rate-limit
+     * your update frequency in the case another application enables a
+     * location provider with extremely fast updates.
      *
      * <p> The supplied Looper is used to implement the callback mechanism.
      *
+     * <p class="note"> Prior to Jellybean, the minTime parameter was
+     * only a hint, and some location provider implementations ignored it.
+     * From Jellybean and onwards it is mandatory for Android compatible
+     * devices to observe both the minTime and minDistance parameters.
+     *
      * @param provider the name of the provider with which to register
-     * @param minTime the minimum time interval for notifications, in
-     * milliseconds. This field is only used as a hint to conserve power, and actual
-     * time between location updates may be greater or lesser than this value.
-     * @param minDistance the minimum distance interval for notifications,
-     * in meters
+     * @param minTime minimum time interval between location updates, in milliseconds
+     * @param minDistance minimum distance between location updates, in meters
      * @param listener a {#link LocationListener} whose
      * {@link LocationListener#onLocationChanged} method will be called for
      * each location update
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism.
-     * If looper is null then the callbacks will be called on the main thread.
+     * implement the callback mechanism, or null to make callbacks on the
+     * main thread
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if listener is null
@@ -513,10 +569,10 @@
 
     /**
      * Registers the current activity to be notified periodically based on
-     * the specified criteria.  Periodically, the supplied LocationListener will
+     * the supplied criteria.  Periodically, the supplied LocationListener will
      * be called with the current Location or with status updates.
      *
-     * <p> It may take a while to receive the most recent location. If
+     * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
@@ -526,38 +582,53 @@
      * the {@link LocationListener#onProviderEnabled(String)} method will
      * be called and location updates will start again.
      *
-     * <p> The frequency of notification may be controlled using the
-     * minTime and minDistance parameters. If minTime is greater than 0,
-     * the LocationManager could potentially rest for minTime milliseconds
-     * between location updates to conserve power. If minDistance is greater than 0,
-     * a location will only be broadcasted if the device moves by minDistance meters.
-     * To obtain notifications as frequently as possible, set both parameters to 0.
+     * <p> The update interval can be controlled using the minTime parameter.
+     * The elapsed time between location updates will never be less than
+     * minTime, although it can be more depending on the Location Provider
+     * implementation and the update interval requested by other applications.
      *
-     * <p> Background services should be careful about setting a sufficiently high
-     * minTime so that the device doesn't consume too much power by keeping the
-     * GPS or wireless radios on all the time. In particular, values under 60000ms
-     * are not recommended.
+     * <p> Choosing a sensible value for minTime is important to conserve
+     * battery life. Each location update requires power from
+     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+     * possible while still providing a reasonable user experience.
+     * If your application is not in the foreground and showing
+     * location to the user then your application should avoid using an active
+     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+     * or greater. If your application is in the foreground and showing
+     * location to the user then it is appropriate to select a faster
+     * update interval.
+     *
+     * <p> The minDistance parameter can also be used to control the
+     * frequency of location updates. If it is greater than 0 then the
+     * location provider will only send your application an update when
+     * the location has changed by at least minDistance meters, AND
+     * at least minTime milliseconds have passed. However it is more
+     * difficult for location providers to save power using the minDistance
+     * parameter, so minTime should be the primary tool to conserving battery
+     * life.
      *
      * <p> The supplied Looper is used to implement the callback mechanism.
      *
-     * @param minTime the minimum time interval for notifications, in
-     * milliseconds. This field is only used as a hint to conserve power, and actual
-     * time between location updates may be greater or lesser than this value.
-     * @param minDistance the minimum distance interval for notifications,
-     * in meters
+     * <p class="note"> Prior to Jellybean, the minTime parameter was
+     * only a hint, and some location provider implementations ignored it.
+     * From Jellybean and onwards it is mandatory for Android compatible
+     * devices to observe both the minTime and minDistance parameters.
+     *
+     * @param minTime minimum time interval between location updates, in milliseconds
+     * @param minDistance minimum distance between location updates, in meters
      * @param criteria contains parameters for the location manager to choose the
      * appropriate provider and parameters to compute the location
      * @param listener a {#link LocationListener} whose
      * {@link LocationListener#onLocationChanged} method will be called for
      * each location update
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism.
-     * If looper is null then the callbacks will be called on the main thread.
+     * implement the callback mechanism, or null to make callbacks on the
+     * main thread.
      *
      * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present to access
-     * the location services.
+     * @throws SecurityException if no suitable permission is present for the provider.
      */
     public void requestLocationUpdates(long minTime, float minDistance,
             Criteria criteria, LocationListener listener, Looper looper) {
@@ -598,43 +669,74 @@
      * the named provider.  Periodically, the supplied PendingIntent will
      * be broadcast with the current Location or with status updates.
      *
-     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+     * <p> Location updates are sent with a key of
+     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
      *
-     * <p> It may take a while to receive the most recent location. If
+     * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
-     * <p> The frequency of notification or new locations may be controlled using the
-     * minTime and minDistance parameters. If minTime is greater than 0,
-     * the LocationManager could potentially rest for minTime milliseconds
-     * between location updates to conserve power. If minDistance is greater than 0,
-     * a location will only be broadcast if the device moves by minDistance meters.
-     * To obtain notifications as frequently as possible, set both parameters to 0.
+     * <p> The update interval can be controlled using the minTime parameter.
+     * The elapsed time between location updates will never be less than
+     * minTime, although it can be more depending on the Location Provider
+     * implementation and the update interval requested by other applications.
      *
-     * <p> Background services should be careful about setting a sufficiently high
-     * minTime so that the device doesn't consume too much power by keeping the
-     * GPS or wireless radios on all the time. In particular, values under 60000ms
-     * are not recommended.
+     * <p> Choosing a sensible value for minTime is important to conserve
+     * battery life. Each location update requires power from
+     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+     * possible while still providing a reasonable user experience.
+     * If your application is not in the foreground and showing
+     * location to the user then your application should avoid using an active
+     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+     * or greater. If your application is in the foreground and showing
+     * location to the user then it is appropriate to select a faster
+     * update interval.
      *
-     * <p> In case the provider is disabled by the user, updates will stop,
-     * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
-     * of false.  If the provider is re-enabled, an intent will be sent with an
-     * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
-     * start again.
+     * <p> The minDistance parameter can also be used to control the
+     * frequency of location updates. If it is greater than 0 then the
+     * location provider will only send your application an update when
+     * the location has changed by at least minDistance meters, AND
+     * at least minTime milliseconds have passed. However it is more
+     * difficult for location providers to save power using the minDistance
+     * parameter, so minTime should be the primary tool to conserving battery
+     * life.
      *
-     * <p> If the provider's status changes, an intent will be sent with an extra with key
-     * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
-     * with the status update will be sent as well.
+     * <p> If your application wants to passively observe location
+     * updates triggered by other applications, but not consume
+     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+     * This provider does not actively turn on or modify active location
+     * providers, so you do not need to be as careful about minTime and
+     * minDistance. However if your application performs heavy work
+     * on a location update (such as network activity) then you should
+     * select non-zero values for minTime and/or minDistance to rate-limit
+     * your update frequency in the case another application enables a
+     * location provider with extremely fast updates.
+     *
+     * <p> If the provider is disabled by the user, updates will stop,
+     * and an intent will be sent with an extra with key
+     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+     * If the provider is re-enabled, an intent will be sent with an
+     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+     * true and location updates will start again.
+     *
+     * <p> If the provider's status changes, an intent will be sent with
+     * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
+     * indicating the new status.  Any extras associated with the status
+     * update will be sent as well.
+     *
+     * <p class="note"> Prior to Jellybean, the minTime parameter was
+     * only a hint, and some location provider implementations ignored it.
+     * From Jellybean and onwards it is mandatory for Android compatible
+     * devices to observe both the minTime and minDistance parameters.
      *
      * @param provider the name of the provider with which to register
-     * @param minTime the minimum time interval for notifications, in
-     * milliseconds. This field is only used as a hint to conserve power, and actual
-     * time between location updates may be greater or lesser than this value.
-     * @param minDistance the minimum distance interval for notifications,
-     * in meters
+     * @param minTime minimum time interval between location updates, in milliseconds
+     * @param minDistance minimum distance between location updates, in meters
      * @param intent a {#link PendingIntent} to be sent for each location update
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
+     * on this device
      * @throws IllegalArgumentException if intent is null
      * @throws SecurityException if no suitable permission is present for the provider.
      */
@@ -651,51 +753,71 @@
 
     /**
      * Registers the current activity to be notified periodically based on
-     * the specified criteria.  Periodically, the supplied PendingIntent will
+     * the supplied criteria.  Periodically, the supplied PendingIntent will
      * be broadcast with the current Location or with status updates.
      *
-     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+     * <p> Location updates are sent with a key of
+     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
      *
-     * <p> It may take a while to receive the most recent location. If
+     * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
-     * <p> The frequency of notification or new locations may be controlled using the
-     * minTime and minDistance parameters. If minTime is greater than 0,
-     * the LocationManager could potentially rest for minTime milliseconds
-     * between location updates to conserve power. If minDistance is greater than 0,
-     * a location will only be broadcast if the device moves by minDistance meters.
-     * To obtain notifications as frequently as possible, set both parameters to 0.
+     * <p> The update interval can be controlled using the minTime parameter.
+     * The elapsed time between location updates will never be less than
+     * minTime, although it can be more depending on the Location Provider
+     * implementation and the update interval requested by other applications.
      *
-     * <p> Background services should be careful about setting a sufficiently high
-     * minTime so that the device doesn't consume too much power by keeping the
-     * GPS or wireless radios on all the time. In particular, values under 60000ms
-     * are not recommended.
+     * <p> Choosing a sensible value for minTime is important to conserve
+     * battery life. Each location update requires power from
+     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+     * possible while still providing a reasonable user experience.
+     * If your application is not in the foreground and showing
+     * location to the user then your application should avoid using an active
+     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+     * or greater. If your application is in the foreground and showing
+     * location to the user then it is appropriate to select a faster
+     * update interval.
      *
-     * <p> In case the provider is disabled by the user, updates will stop,
-     * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
-     * of false.  If the provider is re-enabled, an intent will be sent with an
-     * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
-     * start again.
+     * <p> The minDistance parameter can also be used to control the
+     * frequency of location updates. If it is greater than 0 then the
+     * location provider will only send your application an update when
+     * the location has changed by at least minDistance meters, AND
+     * at least minTime milliseconds have passed. However it is more
+     * difficult for location providers to save power using the minDistance
+     * parameter, so minTime should be the primary tool to conserving battery
+     * life.
      *
-     * <p> If the provider's status changes, an intent will be sent with an extra with key
-     * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
-     * with the status update will be sent as well.
+     * <p> If the provider is disabled by the user, updates will stop,
+     * and an intent will be sent with an extra with key
+     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+     * If the provider is re-enabled, an intent will be sent with an
+     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+     * true and location updates will start again.
      *
-     * @param minTime the minimum time interval for notifications, in
-     * milliseconds. This field is only used as a hint to conserve power, and actual
-     * time between location updates may be greater or lesser than this value.
-     * @param minDistance the minimum distance interval for notifications,
-     * in meters
+     * <p> If the provider's status changes, an intent will be sent with
+     * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
+     * indicating the new status.  Any extras associated with the status
+     * update will be sent as well.
+     *
+     * <p class="note"> Prior to Jellybean, the minTime parameter was
+     * only a hint, and some location provider implementations ignored it.
+     * From Jellybean and onwards it is mandatory for Android compatible
+     * devices to observe both the minTime and minDistance parameters.
+     *
+     * @param minTime minimum time interval between location updates, in milliseconds
+     * @param minDistance minimum distance between location updates, in meters
      * @param criteria contains parameters for the location manager to choose the
      * appropriate provider and parameters to compute the location
      * @param intent a {#link PendingIntent} to be sent for each location update
      *
-     * @throws IllegalArgumentException if provider is null or doesn't exist
+     * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if intent is null
      * @throws SecurityException if no suitable permission is present for the provider.
      */
-    public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) {
+    public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
+            PendingIntent intent) {
         if (criteria == null) {
             throw new IllegalArgumentException("criteria==null");
         }
@@ -741,12 +863,12 @@
      * {@link LocationListener#onLocationChanged} method will be called when
      * the location update is available
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism.
-     * If looper is null then the callbacks will be called on the main thread.
+     * implement the callback mechanism, or null to make callbacks on the
+     * main thread
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present for the provider.
+     * @throws SecurityException if no suitable permission is present for the provider
      */
     public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
         if (provider == null) {
@@ -779,13 +901,13 @@
      * {@link LocationListener#onLocationChanged} method will be called when
      * the location update is available
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism.
-     * If looper is null then the callbacks will be called on the current thread.
+     * implement the callback mechanism, or null to make callbacks on the
+     * main thread
      *
      * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if no suitable permission is present to access
-     * the location services.
+     * the location services
      */
     public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
         if (criteria == null) {
@@ -804,7 +926,8 @@
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
-     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+     * <p> Location updates are sent with a key of
+     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
      *
      * <p> In case the provider is disabled by the user, the update will not be received,
      * and the {@link LocationListener#onProviderDisabled(String)}
@@ -817,7 +940,7 @@
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider.
+     * @throws SecurityException if no suitable permission is present for the provider
      */
     public void requestSingleUpdate(String provider, PendingIntent intent) {
         if (provider == null) {
@@ -836,13 +959,15 @@
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
-     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+     * <p> Location updates are sent with a key of
+     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
      *
-     * <p> In case the provider is disabled by the user, the update will not be received,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
+     * <p> If the provider is disabled by the user, an update will not be
+     * received, and an intent will be sent with an extra with key
+     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+     * If the provider is re-enabled, an intent will be sent with an
+     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+     * true and the location update will occur.
      *
      * @param criteria contains parameters for the location manager to choose the
      * appropriate provider and parameters to compute the location
@@ -850,7 +975,7 @@
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider.
+     * @throws SecurityException if no suitable permission is present for the provider
      */
     public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
         if (criteria == null) {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index d165b5e..aa29444 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3699,38 +3699,15 @@
     }
 
     /**
-     * The minimum duration during which a user must press to trigger voice-based interactions
-     */
-    private final static int MEDIABUTTON_LONG_PRESS_DURATION_MS = 300;
-    /**
-     * The different states of the state machine to handle the launch of voice-based interactions,
-     * stored in mVoiceButtonState.
-     */
-    private final static int VOICEBUTTON_STATE_IDLE = 0;
-    private final static int VOICEBUTTON_STATE_DOWN = 1;
-    private final static int VOICEBUTTON_STATE_DOWN_IGNORE_NEW = 2;
-    /**
-     * The different actions after state transitions on mVoiceButtonState.
+     * The different actions performed in response to a voice button key event.
      */
     private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
     private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
     private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
 
     private final Object mVoiceEventLock = new Object();
-    private int mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
-    private long mVoiceButtonDownTime = 0;
-
-    /**
-     * Log an error when an unexpected action is encountered in the state machine to filter
-     * key events.
-     * @param keyAction the unexpected action of the key event being filtered
-     * @param stateName the string corresponding to the state in which the error occurred
-     */
-    private static void logErrorForKeyAction(int keyAction, String stateName) {
-        Log.e(TAG, "unexpected action "
-                + KeyEvent.actionToString(keyAction)
-                + " in " + stateName + " state");
-    }
+    private boolean mVoiceButtonDown;
+    private boolean mVoiceButtonHandled;
 
     /**
      * Filter key events that may be used for voice-based interactions
@@ -3740,67 +3717,32 @@
      *     is dispatched.
      */
     private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        if (DEBUG_RC) {
+            Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
+        }
+
         int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
         int keyAction = keyEvent.getAction();
         synchronized (mVoiceEventLock) {
-            // state machine on mVoiceButtonState
-            switch (mVoiceButtonState) {
-
-                case VOICEBUTTON_STATE_IDLE:
-                    if (keyAction == KeyEvent.ACTION_DOWN) {
-                        mVoiceButtonDownTime = keyEvent.getDownTime();
-                        // valid state transition
-                        mVoiceButtonState = VOICEBUTTON_STATE_DOWN;
-                    } else if (keyAction == KeyEvent.ACTION_UP) {
-                        // no state transition
-                        // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
-                    } else {
-                        logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_IDLE");
+            if (keyAction == KeyEvent.ACTION_DOWN) {
+                if (keyEvent.getRepeatCount() == 0) {
+                    // initial down
+                    mVoiceButtonDown = true;
+                    mVoiceButtonHandled = false;
+                } else if (mVoiceButtonDown && !mVoiceButtonHandled
+                        && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+                    // long-press, start voice-based interactions
+                    mVoiceButtonHandled = true;
+                    voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
+                }
+            } else if (keyAction == KeyEvent.ACTION_UP) {
+                if (mVoiceButtonDown) {
+                    // voice button up
+                    mVoiceButtonDown = false;
+                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+                        voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
                     }
-                    break;
-
-                case VOICEBUTTON_STATE_DOWN:
-                    if ((keyEvent.getEventTime() - mVoiceButtonDownTime)
-                            >= MEDIABUTTON_LONG_PRESS_DURATION_MS) {
-                        // press was long enough, start voice-based interactions, regardless of
-                        //   whether this was a DOWN or UP key event
-                        voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
-                        if (keyAction == KeyEvent.ACTION_UP) {
-                            // done tracking the key press, so transition back to idle state
-                            mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
-                        } else if (keyAction == KeyEvent.ACTION_DOWN) {
-                            // no need to observe the upcoming key events
-                            mVoiceButtonState = VOICEBUTTON_STATE_DOWN_IGNORE_NEW;
-                        } else {
-                            logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN");
-                        }
-                    } else {
-                        if (keyAction == KeyEvent.ACTION_UP) {
-                            // press wasn't long enough, simulate complete key press
-                            voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
-                            // not tracking the key press anymore, so transition back to idle state
-                            mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
-                        } else if (keyAction == KeyEvent.ACTION_DOWN) {
-                            // no state transition
-                            // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
-                        } else {
-                            logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN");
-                        }
-                    }
-                    break;
-
-                case VOICEBUTTON_STATE_DOWN_IGNORE_NEW:
-                    if (keyAction == KeyEvent.ACTION_UP) {
-                        // done tracking the key press, so transition back to idle state
-                        mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
-                        // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
-                    } else if (keyAction == KeyEvent.ACTION_DOWN) {
-                        // no state transition: we've already launched voice-based interactions
-                        // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
-                    } else  {
-                        logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN_IGNORE_NEW");
-                    }
-                    break;
+                }
             }
         }//synchronized (mVoiceEventLock)
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index aa4cdbe..9ed9de0 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -737,7 +737,7 @@
      * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
      */
     public void setVideoScalingMode(int mode) {
-        if (isVideoScalingModeSupported(mode)) {
+        if (!isVideoScalingModeSupported(mode)) {
             final String msg = "Scaling mode " + mode + " is not supported";
             throw new IllegalArgumentException(msg);
         }
diff --git a/packages/InputDevices/AndroidManifest.xml b/packages/InputDevices/AndroidManifest.xml
index 6831a74..f0e4abc 100644
--- a/packages/InputDevices/AndroidManifest.xml
+++ b/packages/InputDevices/AndroidManifest.xml
@@ -8,7 +8,8 @@
             android:label="@string/app_label"
             android:process="system">
 
-        <receiver android:name=".InputDeviceReceiver">
+        <receiver android:name=".InputDeviceReceiver"
+                android:label="@string/keyboard_layouts_label">
             <intent-filter>
                 <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
             </intent-filter>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 140c7d4..c13e606 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -3,6 +3,9 @@
     <!-- Name of the application. [CHAR LIMIT=35] -->
     <string name="app_label">Input Devices</string>
 
+    <!-- Keyboard layouts label, used to describe the set of all built-in layouts in the UI. [CHAR LIMIT=35] -->
+    <string name="keyboard_layouts_label">Android keyboard</string>
+
     <!-- US English keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_english_us_label">English (US)</string>
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0038d13..c9957f5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -30,6 +30,7 @@
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MASTER_CLEAR" />
+    <uses-permission android:name="android.permission.VIBRATE" />
 
     <!-- ActivityManager -->
     <uses-permission android:name="android.permission.GET_TASKS" />
diff --git a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
index 392a8b5..ae81167 100644
--- a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
@@ -55,7 +55,7 @@
                 prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
                 prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
                 prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="0"
+                prvandroid:vibrationDuration="@integer/config_vibration_duration"
                 prvandroid:alwaysTrackFinger="true"/>
 
         </RelativeLayout>
diff --git a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
index 371c575..785d5dd 100644
--- a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
@@ -55,7 +55,7 @@
                 prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
                 prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
                 prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="0"
+                prvandroid:vibrationDuration="@integer/config_vibration_duration"
                 prvandroid:alwaysTrackFinger="true"/>
 
         </RelativeLayout>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
index 0ccfe95..4da05d9 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
@@ -64,7 +64,7 @@
                 prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
                 prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
                 prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="0"
+                prvandroid:vibrationDuration="@integer/config_vibration_duration"
                 prvandroid:alwaysTrackFinger="true"/>
 
         </RelativeLayout>
diff --git a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
index 0a5390a..1ae8a694 100644
--- a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
@@ -64,7 +64,7 @@
                 prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
                 prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
                 prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="0"
+                prvandroid:vibrationDuration="@integer/config_vibration_duration"
                 prvandroid:alwaysTrackFinger="true"/>
 
         </RelativeLayout>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index be59075..1bf59b0 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -28,10 +28,10 @@
     <integer name="notification_panel_layout_gravity">0x31</integer>
 
     <!-- Diameter of outer shape drawable shown in navbar search-->
-    <dimen name="navbar_search_outerring_diameter">364dp</dimen>
+    <dimen name="navbar_search_outerring_diameter">430dip</dimen>
 
     <!-- Height of search panel including navigation bar height -->
-    <dimen name="navbar_search_panel_height">300dip</dimen>
+    <dimen name="navbar_search_panel_height">280dip</dimen>
 
     <!-- Size of application thumbnail -->
     <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5548445..2786013 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -3,16 +3,16 @@
 /*
 ** Copyright 2009, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
@@ -56,5 +56,8 @@
 
     <!-- Show rotation lock button in phone-style notification panel. -->
     <bool name="config_showRotationLock">true</bool>
+
+    <!-- Vibration duration for MultiWaveView used in SearchPanelView -->
+    <integer translatable="false" name="config_vibration_duration">20</integer>
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index c60c806..96f83c6 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -281,9 +281,16 @@
                 return;
             }
 
-            if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
-                // If we don't yet know the size of the wallpaper bitmap,
-                // we need to get it now.
+            // If we don't yet know the size of the wallpaper bitmap,
+            // we need to get it now.
+            boolean updateWallpaper = mBackgroundWidth < 0 || mBackgroundHeight < 0 ;
+
+            // If we somehow got to this point after we have last flushed
+            // the wallpaper, well we really need it to draw again.  So
+            // seems like we need to reload it.  Ouch.
+            updateWallpaper = updateWallpaper || mBackground == null;
+
+            if (updateWallpaper) {
                 updateWallpaperLocked();
             }
 
@@ -308,12 +315,6 @@
             mLastXTranslation = xPixels;
             mLastYTranslation = yPixels;
 
-            if (mBackground == null) {
-                // If we somehow got to this point after we have last flushed
-                // the wallpaper, well we really need it to draw again.  So
-                // seems like we need to reload it.  Ouch.
-                updateWallpaperLocked();
-            }
 
             if (mIsHwAccelerated) {
                 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 7edeaef..a90192e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -33,11 +33,7 @@
     private float mTriggerThreshhold;
 
     public DelegateViewHelper(View sourceView) {
-        mSourceView = sourceView;
-        if (mSourceView != null) {
-            mTriggerThreshhold = mSourceView.getContext().getResources()
-                    .getDimension(R.dimen.navbar_search_up_threshhold);
-        }
+        setSourceView(sourceView);
     }
 
     public void setDelegateView(View view) {
@@ -92,4 +88,12 @@
         }
         return false;
     }
+
+    public void setSourceView(View view) {
+        mSourceView = view;
+        if (mSourceView != null) {
+            mTriggerThreshhold = mSourceView.getContext().getResources()
+                    .getDimension(R.dimen.navbar_search_up_threshhold);
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
index a6fc396..25fdf07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.tablet;
 
+import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.DelegateViewHelper;
 
@@ -37,8 +38,7 @@
     private DelegateViewHelper mDelegateHelper;
 
     public TabletStatusBarView(Context context) {
-        super(context);
-        mDelegateHelper = new DelegateViewHelper(this);
+        this(context, null);
     }
 
     public TabletStatusBarView(Context context, AttributeSet attrs) {
@@ -55,6 +55,20 @@
     }
 
     @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        // Find the view we wish to grab events from in order to detect search gesture.
+        // Depending on the device, this will be one of the id's listed below.
+        // If we don't find one, we'll use the view provided in the constructor above (this view).
+        View view = null;
+        if ((view = findViewById(R.id.navigationArea)) != null) {
+            mDelegateHelper.setSourceView(view);
+        } else if ((view = findViewById(R.id.nav_buttons)) != null) {
+            mDelegateHelper.setSourceView(view);
+        }
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             if (TabletStatusBar.DEBUG) {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index 7f432bf..504bb63 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -117,7 +117,6 @@
             final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
             int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
                     | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
-                    | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
                     | WindowManager.LayoutParams.FLAG_SLIPPERY
                     /*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 9629f0a..47703c6 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1630,7 +1630,12 @@
         if (mActionBar != null) {
             SparseArray<Parcelable> actionBarStates =
                     savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
-            mActionBar.restoreHierarchyState(actionBarStates);
+            if (actionBarStates != null) {
+                mActionBar.restoreHierarchyState(actionBarStates);
+            } else {
+                Log.w(TAG, "Missing saved instance states for action bar views! " +
+                        "State will not be restored.");
+            }
         }
     }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a3768c6..2167c49 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -48,6 +48,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -252,6 +253,34 @@
     IBackupTransport mLocalTransport, mGoogleTransport;
     ActiveRestoreSession mActiveRestoreSession;
 
+    // Watch the device provisioning operation during setup
+    ContentObserver mProvisionedObserver;
+
+    class ProvisionedObserver extends ContentObserver {
+        public ProvisionedObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void onChange(boolean selfChange) {
+            final boolean wasProvisioned = mProvisioned;
+            final boolean isProvisioned = deviceIsProvisioned();
+            // latch: never unprovision
+            mProvisioned = wasProvisioned || isProvisioned;
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
+                        + " is=" + isProvisioned + " now=" + mProvisioned);
+            }
+
+            synchronized (mQueueLock) {
+                if (mProvisioned && !wasProvisioned && mEnabled) {
+                    // we're now good to go, so start the backup alarms
+                    if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
+                    startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+                }
+            }
+        }
+    }
+
     class RestoreGetSetsParams {
         public IBackupTransport transport;
         public ActiveRestoreSession session;
@@ -695,12 +724,19 @@
         mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
 
         // Set up our bookkeeping
-        boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
+        final ContentResolver resolver = context.getContentResolver();
+        boolean areEnabled = Settings.Secure.getInt(resolver,
                 Settings.Secure.BACKUP_ENABLED, 0) != 0;
-        mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
-                Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
+        mProvisioned = Settings.Secure.getInt(resolver,
+                Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+        mAutoRestore = Settings.Secure.getInt(resolver,
                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+        mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
+        resolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.DEVICE_PROVISIONED),
+                false, mProvisionedObserver);
+
         // If Encrypted file systems is enabled or disabled, this call will return the
         // correct directory.
         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
@@ -5172,24 +5208,9 @@
     public void setBackupProvisioned(boolean available) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "setBackupProvisioned");
-
-        boolean wasProvisioned = mProvisioned;
-        synchronized (this) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
-            mProvisioned = available;
-        }
-
-        synchronized (mQueueLock) {
-            if (available && !wasProvisioned && mEnabled) {
-                // we're now good to go, so start the backup alarms
-                startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
-            } else if (!available) {
-                // No longer enabled, so stop running backups
-                Slog.w(TAG, "Backup service no longer provisioned");
-                mAlarmManager.cancel(mRunBackupIntent);
-            }
-        }
+        /*
+         * This is now a no-op; provisioning is simply the device's own setup state.
+         */
     }
 
     private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 4582d67..1e707b2 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -105,6 +105,12 @@
     private static final String INSTALL_LOCATION_PROVIDER =
         android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
 
+    // Location Providers may sometimes deliver location updates
+    // slightly faster that requested - provide grace period so
+    // we don't unnecessarily filter events that are otherwise on
+    // time
+    private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100;
+
     // Set of providers that are explicitly enabled
     private final Set<String> mEnabledProviders = new HashSet<String>();
 
@@ -194,8 +200,9 @@
         final PendingIntent mPendingIntent;
         final Object mKey;
         final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
+
         int mPendingBroadcasts;
-        String requiredPermissions;
+        String mRequiredPermissions;
 
         Receiver(ILocationListener listener) {
             mListener = listener;
@@ -286,7 +293,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
-                                requiredPermissions);
+                                mRequiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -322,7 +329,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
-                                requiredPermissions);
+                                mRequiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -362,7 +369,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
-                                requiredPermissions);
+                                mRequiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -374,6 +381,7 @@
             return true;
         }
 
+        @Override
         public void binderDied() {
             if (LOCAL_LOGV) {
                 Slog.v(TAG, "Location listener died");
@@ -1026,7 +1034,7 @@
                     + Integer.toHexString(System.identityHashCode(this))
                     + " mProvider: " + mProvider + " mUid: " + mUid + "}";
         }
-        
+
         void dump(PrintWriter pw, String prefix) {
             pw.println(prefix + this);
             pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
@@ -1155,10 +1163,11 @@
 
         LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
-            throw new IllegalArgumentException("provider=" + provider);
+            throw new IllegalArgumentException("requested provider " + provider +
+                    " doesn't exisit");
         }
-        receiver.requiredPermissions = checkPermissionsSafe(provider,
-                receiver.requiredPermissions);
+        receiver.mRequiredPermissions = checkPermissionsSafe(provider,
+                receiver.mRequiredPermissions);
 
         // so wakelock calls will succeed
         final int callingPid = Binder.getCallingPid();
@@ -1752,9 +1761,9 @@
             return true;
         }
 
-        // Don't broadcast same location again regardless of condition
-        // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
-        if (loc.getTime() == lastLoc.getTime()) {
+        // Check whether sufficient time has passed
+        long minTime = record.mMinTime;
+        if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
             return false;
         }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d6606f6..13ab586 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -79,6 +79,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.Set;
 
 import javax.crypto.SecretKey;
@@ -178,7 +180,8 @@
     final private ArrayList<MountServiceBinderListener> mListeners =
             new ArrayList<MountServiceBinderListener>();
     private boolean                               mBooted = false;
-    private boolean                               mReady = false;
+    private CountDownLatch                        mConnectedSignal = new CountDownLatch(1);
+    private CountDownLatch                        mAsecsScanned = new CountDownLatch(1);
     private boolean                               mSendUmsConnectedOnBoot = false;
     // true if we should fake MEDIA_MOUNTED state for external storage
     private boolean                               mEmulateExternalStorage = false;
@@ -446,15 +449,30 @@
     final private HandlerThread mHandlerThread;
     final private Handler mHandler;
 
+    void waitForAsecScan() {
+        waitForLatch(mAsecsScanned);
+    }
+
     private void waitForReady() {
-        while (mReady == false) {
-            for (int retries = 5; retries > 0; retries--) {
-                if (mReady) {
+        waitForLatch(mConnectedSignal);
+    }
+
+    private void waitForLatch(CountDownLatch latch) {
+        if (latch == null) {
+            return;
+        }
+
+        for (;;) {
+            try {
+                if (latch.await(5000, TimeUnit.MILLISECONDS)) {
                     return;
+                } else {
+                    Slog.w(TAG, "Thread " + Thread.currentThread().getName()
+                            + " still waiting for MountService ready...");
                 }
-                SystemClock.sleep(1000);
+            } catch (InterruptedException e) {
+                Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
             }
-            Slog.w(TAG, "Waiting too long for mReady!");
         }
     }
 
@@ -627,7 +645,7 @@
          * Since we'll be calling back into the NativeDaemonConnector,
          * we need to do our work in a new thread.
          */
-        new Thread() {
+        new Thread("MountService#onDaemonConnected") {
             @Override
             public void run() {
                 /**
@@ -668,14 +686,19 @@
                     updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
                 }
 
-                // Let package manager load internal ASECs.
-                mPms.updateExternalMediaStatus(true, false);
-
                 /*
                  * Now that we've done our initialization, release
                  * the hounds!
                  */
-                mReady = true;
+                mConnectedSignal.countDown();
+                mConnectedSignal = null;
+
+                // Let package manager load internal ASECs.
+                mPms.scanAvailableAsecs();
+
+                // Notify people waiting for ASECs to be scanned that it's done.
+                mAsecsScanned.countDown();
+                mAsecsScanned = null;
             }
         }.start();
     }
@@ -1159,22 +1182,12 @@
         mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
 
         /*
-         * Vold does not run in the simulator, so pretend the connector thread
-         * ran and did its thing.
-         */
-        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-            mReady = true;
-            mUmsEnabling = true;
-            return;
-        }
-
-        /*
          * Create the connection to vold with a maximum queue of twice the
          * amount of containers we'd ever expect to have. This keeps an
          * "asec list" from blocking a thread repeatedly.
          */
         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
-        mReady = false;
+
         Thread thread = new Thread(mConnector, VOLD_TAG);
         thread.start();
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bb103580..eaecd4c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -320,6 +320,21 @@
         }
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+            MountService mountService = null;
+            if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+                try {
+                    /*
+                     * NotificationManagerService is dependant on MountService,
+                     * (for media / usb notifications) so we must start MountService first.
+                     */
+                    Slog.i(TAG, "Mount Service");
+                    mountService = new MountService(context);
+                    ServiceManager.addService("mount", mountService);
+                } catch (Throwable e) {
+                    reportWtf("starting Mount Service", e);
+                }
+            }
+
             try {
                 Slog.i(TAG,  "LockSettingsService");
                 lockSettings = new LockSettingsService(context);
@@ -441,17 +456,13 @@
                 reportWtf("starting UpdateLockService", e);
             }
 
-            if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
-                try {
-                    /*
-                     * NotificationManagerService is dependant on MountService,
-                     * (for media / usb notifications) so we must start MountService first.
-                     */
-                    Slog.i(TAG, "Mount Service");
-                    ServiceManager.addService("mount", new MountService(context));
-                } catch (Throwable e) {
-                    reportWtf("starting Mount Service", e);
-                }
+            /*
+             * MountService has a few dependencies: Notification Manager and
+             * AppWidget Provider. Make sure MountService is completely started
+             * first before continuing.
+             */
+            if (mountService != null) {
+                mountService.waitForAsecScan();
             }
 
             try {
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 84daead..c936ef9 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -93,6 +93,7 @@
     private final int mDefaultUiModeType;
     private final boolean mCarModeKeepsScreenOn;
     private final boolean mDeskModeKeepsScreenOn;
+    private final boolean mTelevision;
 
     private boolean mComputedNightMode;
     private int mCurUiMode = 0;
@@ -354,7 +355,9 @@
                 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
         mDeskModeKeepsScreenOn = (context.getResources().getInteger(
                 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
-        
+        mTelevision = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEVISION);
+
         mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
     }
@@ -455,7 +458,8 @@
     }
 
     final void updateConfigurationLocked(boolean sendIt) {
-        int uiMode = mDefaultUiModeType;
+        int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION
+                : mDefaultUiModeType;
         if (mCarModeEnabled) {
             uiMode = Configuration.UI_MODE_TYPE_CAR;
         } else if (isDeskDockState(mDockState)) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index b1558c7..1f03d17 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -376,11 +376,7 @@
                     @Override
                     public void onReceive(Context context, Intent intent) {
                         mAirplaneModeOn.set(isAirplaneModeOn());
-                        /* On airplane mode disable, restore wifi state if necessary */
-                        if (!mAirplaneModeOn.get() && (testAndClearWifiSavedState() ||
-                            mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) {
-                                persistWifiState(true);
-                        }
+                        handleAirplaneModeToggled(mAirplaneModeOn.get());
                         updateWifiState();
                     }
                 },
@@ -447,7 +443,10 @@
         boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                 (wifiEnabled ? "enabled" : "disabled"));
-        setWifiEnabled(wifiEnabled);
+
+        // If we are already disabled (could be due to airplane mode), avoid changing persist
+        // state here
+        if (wifiEnabled) setWifiEnabled(wifiEnabled);
 
         mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
                makeWifiWatchdogStateMachine(mContext);
@@ -485,26 +484,43 @@
         }
     }
 
-    private void persistWifiState(boolean enabled) {
-        final ContentResolver cr = mContext.getContentResolver();
-        boolean airplane = mAirplaneModeOn.get() && isAirplaneToggleable();
-        if (enabled) {
-            if (airplane) {
-                mPersistWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE);
+    private void handleWifiToggled(boolean wifiEnabled) {
+        boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable();
+        if (wifiEnabled) {
+            if (airplaneEnabled) {
+                persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
             } else {
-                mPersistWifiState.set(WIFI_ENABLED);
+                persistWifiState(WIFI_ENABLED);
             }
         } else {
-            if (airplane) {
-                mPersistWifiState.set(WIFI_DISABLED_AIRPLANE_ON);
-            } else {
-                mPersistWifiState.set(WIFI_DISABLED);
-            }
+            // When wifi state is disabled, we do not care
+            // if airplane mode is on or not. The scenario of
+            // wifi being disabled due to airplane mode being turned on
+            // is handled handleAirplaneModeToggled()
+            persistWifiState(WIFI_DISABLED);
         }
-
-        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mPersistWifiState.get());
     }
 
+    private void handleAirplaneModeToggled(boolean airplaneEnabled) {
+        if (airplaneEnabled) {
+            // Wifi disabled due to airplane on
+            if (mWifiEnabled) {
+                persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
+            }
+        } else {
+            /* On airplane mode disable, restore wifi state if necessary */
+            if (testAndClearWifiSavedState() ||
+                    mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
+                persistWifiState(WIFI_ENABLED);
+            }
+        }
+    }
+
+    private void persistWifiState(int state) {
+        final ContentResolver cr = mContext.getContentResolver();
+        mPersistWifiState.set(state);
+        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, state);
+    }
 
     /**
      * see {@link android.net.wifi.WifiManager#pingSupplicant()}
@@ -578,12 +594,9 @@
          * only CHANGE_WIFI_STATE is enforced
          */
 
-        /* Avoids overriding of airplane state when wifi is already in the expected state */
-        if (enable != mWifiEnabled) {
-            long ident = Binder.clearCallingIdentity();
-            persistWifiState(enable);
-            Binder.restoreCallingIdentity(ident);
-        }
+        long ident = Binder.clearCallingIdentity();
+        handleWifiToggled(enable);
+        Binder.restoreCallingIdentity(ident);
 
         if (enable) {
             if (!mIsReceiverRegistered) {
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 9e94b52..4f1f76f 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -37,7 +37,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
@@ -597,8 +596,8 @@
         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
             @Override
             public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, int keyboardLayoutResId) {
-                list.add(new KeyboardLayout(descriptor, label));
+                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+                list.add(new KeyboardLayout(descriptor, label, collection));
             }
         });
         return list.toArray(new KeyboardLayout[list.size()]);
@@ -614,8 +613,8 @@
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
             public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, int keyboardLayoutResId) {
-                result[0] = new KeyboardLayout(descriptor, label);
+                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+                result[0] = new KeyboardLayout(descriptor, label, collection);
             }
         });
         if (result[0] == null) {
@@ -663,6 +662,9 @@
             return;
         }
 
+        CharSequence receiverLabel = receiver.loadLabel(pm);
+        String collection = receiverLabel != null ? receiverLabel.toString() : "";
+
         try {
             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
             XmlResourceParser parser = resources.getXml(configResId);
@@ -696,7 +698,7 @@
                                         receiver.packageName, receiver.name, name);
                                 if (keyboardName == null || name.equals(keyboardName)) {
                                     visitor.visitKeyboardLayout(resources, descriptor,
-                                            label, keyboardLayoutResId);
+                                            label, collection, keyboardLayoutResId);
                                 }
                             }
                         } finally {
@@ -1139,7 +1141,7 @@
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
             public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, int keyboardLayoutResId) {
+                    String descriptor, String label, String collection, int keyboardLayoutResId) {
                 try {
                     result[0] = descriptor;
                     result[1] = Streams.readFully(new InputStreamReader(
@@ -1262,7 +1264,7 @@
 
     private interface KeyboardLayoutVisitor {
         void visitKeyboardLayout(Resources resources,
-                String descriptor, String label, int keyboardLayoutResId);
+                String descriptor, String label, String collection, int keyboardLayoutResId);
     }
 
     private final class InputDevicesChangedListenerRecord implements DeathRecipient {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3936c18..77c3e78 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -240,6 +240,9 @@
     // This is where all application persistent data goes for secondary users.
     final File mUserAppDataDir;
 
+    /** The location for ASEC container files on internal storage. */
+    final String mAsecInternalPath;
+
     // This is the object monitoring the framework dir.
     final FileObserver mFrameworkInstallObserver;
 
@@ -907,6 +910,7 @@
 
             File dataDir = Environment.getDataDirectory();
             mAppDataDir = new File(dataDir, "data");
+            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
             mUserAppDataDir = new File(dataDir, "user");
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
 
@@ -1043,7 +1047,7 @@
             scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR,
                     scanMode | SCAN_NO_DEX, 0);
-            
+
             // Collect all system packages.
             mSystemAppDir = new File(Environment.getRootDirectory(), "app");
             mSystemInstallObserver = new AppDirObserver(
@@ -6479,6 +6483,11 @@
         }
     }
 
+    private boolean isAsecExternal(String cid) {
+        final String asecPath = PackageHelper.getSdFilesystem(cid);
+        return !asecPath.startsWith(mAsecInternalPath);
+    }
+
     /**
      * Extract the MountService "container ID" from the full code path of an
      * .apk.
@@ -6517,7 +6526,7 @@
         }
 
         AsecInstallArgs(String cid) {
-            super(null, null, 0, null, null);
+            super(null, null, isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0, null, null);
             this.cid = cid;
             setCachePath(PackageHelper.getSdDir(cid));
         }
@@ -8659,6 +8668,14 @@
         });
     }
 
+    /**
+     * Called by MountService when the initial ASECs to scan are available.
+     * Should block until all the ASEC containers are finished being scanned.
+     */
+    public void scanAvailableAsecs() {
+        updateExternalMediaStatusInner(true, false);
+    }
+
     /*
      * Collect information of applications on external media, map them against
      * existing containers and update information based on current mount status.
@@ -8793,7 +8810,11 @@
                     continue;
                 }
                 // Parse package
-                int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+                int parseFlags = mDefParseFlags;
+                if (args.isExternal()) {
+                    parseFlags |= PackageParser.PARSE_ON_SDCARD;
+                }
+
                 doGc = true;
                 synchronized (mInstallLock) {
                     final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b3ac6f1..076ba9a 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -534,6 +534,8 @@
 
     Display mDisplay;
 
+    boolean mIsTouchDevice;
+
     final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
     final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
@@ -1173,8 +1175,7 @@
             if (DEBUG_INPUT_METHOD) {
                 Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
                 if (!w.isVisibleOrAdding()) {
-                    Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurface + " reportDestroy="
-                            + w.mWinAnimator.mReportDestroySurface
+                    Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurface
                             + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
                             + " policyVis=" + w.mPolicyVisibility + " attachHid=" + w.mAttachedHidden
                             + " exiting=" + w.mExiting + " destroying=" + w.mDestroying);
@@ -2651,7 +2652,7 @@
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outContentInsets,
             Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
-        boolean displayed = false;
+        boolean toBeDisplayed = false;
         boolean inTouchMode;
         boolean configChanged;
         boolean surfaceChanged = false;
@@ -2754,7 +2755,7 @@
             }
             if (viewVisibility == View.VISIBLE &&
                     (win.mAppToken == null || !win.mAppToken.clientHidden)) {
-                displayed = !win.isVisibleLw();
+                toBeDisplayed = !win.isVisibleLw();
                 if (win.mExiting) {
                     winAnimator.cancelExitAnimationForNextAnimationLocked();
                     win.mExiting = false;
@@ -2766,7 +2767,7 @@
                 if (oldVisibility == View.GONE) {
                     winAnimator.mEnterAnimationPending = true;
                 }
-                if (displayed) {
+                if (toBeDisplayed) {
                     if (win.isDrawnLw() && okToDisplay()) {
                         winAnimator.applyEnterAnimationLocked();
                     }
@@ -2792,7 +2793,7 @@
                 if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
                     // To change the format, we need to re-build the surface.
                     winAnimator.destroySurfaceLocked();
-                    displayed = true;
+                    toBeDisplayed = true;
                     surfaceChanged = true;
                 }
                 try {
@@ -2802,8 +2803,6 @@
                     Surface surface = winAnimator.createSurfaceLocked();
                     if (surface != null) {
                         outSurface.copyFrom(surface);
-                        winAnimator.mReportDestroySurface = false;
-                        winAnimator.mSurfacePendingDestroy = false;
                         if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                 "  OUT SURFACE " + outSurface + ": copied");
                     } else {
@@ -2820,7 +2819,7 @@
                     Binder.restoreCallingIdentity(origId);
                     return 0;
                 }
-                if (displayed) {
+                if (toBeDisplayed) {
                     focusMayChange = true;
                 }
                 if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2845,11 +2844,10 @@
                 winAnimator.mEnterAnimationPending = false;
                 if (winAnimator.mSurface != null) {
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
-                            + ": mExiting=" + win.mExiting
-                            + " mSurfacePendingDestroy=" + winAnimator.mSurfacePendingDestroy);
+                            + ": mExiting=" + win.mExiting);
                     // If we are not currently running the exit animation, we
                     // need to see about starting one.
-                    if (!win.mExiting || winAnimator.mSurfacePendingDestroy) {
+                    if (!win.mExiting) {
                         surfaceChanged = true;
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
@@ -2857,7 +2855,7 @@
                         if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                             transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                         }
-                        if (!winAnimator.mSurfacePendingDestroy && win.isWinVisibleLw() &&
+                        if (win.isWinVisibleLw() &&
                                 winAnimator.applyAnimationLocked(transit, false)) {
                             focusMayChange = true;
                             win.mExiting = true;
@@ -2880,22 +2878,8 @@
                     }
                 }
 
-                if (winAnimator.mSurface == null || (win.getAttrs().flags
-                        & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
-                        || winAnimator.mSurfacePendingDestroy) {
-                    // We could be called from a local process, which
-                    // means outSurface holds its current surface.  Ensure the
-                    // surface object is cleared, but we don't necessarily want
-                    // it actually destroyed at this point.
-                    winAnimator.mSurfacePendingDestroy = false;
-                    outSurface.release();
-                    if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
-                } else if (winAnimator.mSurface != null) {
-                    if (DEBUG_VISIBILITY) Slog.i(TAG,
-                            "Keeping surface, will report destroy: " + win);
-                    winAnimator.mReportDestroySurface = true;
-                    outSurface.copyFrom(winAnimator.mSurface);
-                }
+                outSurface.release();
+                if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
             }
 
             if (focusMayChange) {
@@ -2912,7 +2896,7 @@
             boolean assignLayers = false;
 
             if (imMayMove) {
-                if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {
+                if (moveInputMethodWindowsIfNeededLocked(false) || toBeDisplayed) {
                     // Little hack here -- we -should- be able to rely on the
                     // function to return true if the IME has moved and needs
                     // its layer recomputed.  However, if the IME was hidden
@@ -2934,7 +2918,7 @@
             }
             configChanged = updateOrientationFromAppTokensLocked(false);
             performLayoutAndPlaceSurfacesLocked();
-            if (displayed && win.mIsWallpaper) {
+            if (toBeDisplayed && win.mIsWallpaper) {
                 updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, false);
             }
             if (win.mAppToken != null) {
@@ -2970,7 +2954,7 @@
         Binder.restoreCallingIdentity(origId);
 
         return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0)
-                | (displayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
+                | (toBeDisplayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
                 | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0)
                 | (animating ? WindowManagerImpl.RELAYOUT_RES_ANIMATING : 0);
     }
@@ -6391,8 +6375,12 @@
                             WindowManagerPolicy.PRESENCE_EXTERNAL :
                                     WindowManagerPolicy.PRESENCE_INTERNAL;
 
-                    if ((sources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
-                        config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
+                    if (mIsTouchDevice) {
+                        if ((sources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+                            config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
+                        }
+                    } else {
+                        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
                     }
 
                     if ((sources & InputDevice.SOURCE_TRACKBALL) != 0) {
@@ -6626,6 +6614,8 @@
             }
             WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
             mDisplay = wm.getDefaultDisplay();
+            mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TOUCHSCREEN);
             synchronized(mDisplaySizeLock) {
                 mInitialDisplayWidth = mDisplay.getRawWidth();
                 mInitialDisplayHeight = mDisplay.getRawHeight();
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 1fd80c2..e2a904f 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -679,8 +679,7 @@
      */
     boolean isVisibleOrAdding() {
         final AppWindowToken atoken = mAppToken;
-        return ((mHasSurface && !mWinAnimator.mReportDestroySurface)
-                        || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
+        return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                 && mPolicyVisibility && !mAttachedHidden
                 && (atoken == null || !atoken.hiddenRequested)
                 && !mExiting && !mDestroying;
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 5516dea..355db6e 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -71,8 +71,6 @@
 
     Surface mSurface;
     Surface mPendingDestroySurface;
-    boolean mReportDestroySurface;
-    boolean mSurfacePendingDestroy;
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -561,8 +559,6 @@
 
     Surface createSurfaceLocked() {
         if (mSurface == null) {
-            mReportDestroySurface = false;
-            mSurfacePendingDestroy = false;
             if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
                     "createSurface " + this + ": mDrawState=DRAW_PENDING");
             mDrawState = DRAW_PENDING;
@@ -694,7 +690,6 @@
             mWin.mAppToken.startingDisplayed = false;
         }
 
-        mDrawState = NO_SURFACE;
         if (mSurface != null) {
 
             int i = mWin.mChildWindows.size();
@@ -704,17 +699,6 @@
                 c.mAttachedHidden = true;
             }
 
-            if (mReportDestroySurface) {
-                mReportDestroySurface = false;
-                mSurfacePendingDestroy = true;
-                try {
-                    mWin.mClient.dispatchGetNewSurface();
-                    // We'll really destroy on the next time around.
-                    return;
-                } catch (RemoteException e) {
-                }
-            }
-
             try {
                 if (DEBUG_VISIBILITY) {
                     RuntimeException e = null;
@@ -760,6 +744,7 @@
             mSurfaceShown = false;
             mSurface = null;
             mWin.mHasSurface =false;
+            mDrawState = NO_SURFACE;
         }
     }
 
@@ -1147,7 +1132,7 @@
             }
         } else {
             if (DEBUG_ANIM) {
-                Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin);
+                // Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin);
             }
             displayed = true;
         }
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
index 07a0a28..f40958d 100644
--- a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -19,15 +19,21 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Message;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
+import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
 
 import java.io.IOException;
+import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -45,6 +51,8 @@
  */
 public class SmsUsageMonitor {
     private static final String TAG = "SmsUsageMonitor";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /** Default checking period for SMS sent without user permission. */
     private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000;    // 30 minutes
@@ -69,6 +77,7 @@
 
     private final int mCheckPeriod;
     private final int mMaxAllowed;
+
     private final HashMap<String, ArrayList<Long>> mSmsStamp =
             new HashMap<String, ArrayList<Long>>();
 
@@ -87,6 +96,12 @@
     /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
     private ShortCodePatternMatcher mCurrentPatternMatcher;
 
+    /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */
+    private String mSettingsShortCodePatterns;
+
+    /** Handler for responding to content observer updates. */
+    private final SettingsObserverHandler mSettingsObserverHandler;
+
     /** XML tag for root element. */
     private static final String TAG_SHORTCODES = "shortcodes";
 
@@ -149,6 +164,74 @@
     }
 
     /**
+     * Observe the secure setting for updated regex patterns.
+     */
+    private static class SettingsObserver extends ContentObserver {
+        private final int mWhat;
+        private final Handler mHandler;
+
+        SettingsObserver(Handler handler, int what) {
+            super(handler);
+            mHandler = handler;
+            mWhat = what;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mHandler.obtainMessage(mWhat).sendToTarget();
+        }
+    }
+
+    /**
+     * Handler to update regex patterns when secure setting for the current country is updated.
+     */
+    private class SettingsObserverHandler extends Handler {
+        /** Current content observer, or null. */
+        SettingsObserver mSettingsObserver;
+
+        /** Current country code to watch for settings updates. */
+        private String mCountryIso;
+
+        /** Request to start observing a secure setting. */
+        static final int OBSERVE_SETTING = 1;
+
+        /** Handler event for updated secure settings. */
+        static final int SECURE_SETTINGS_CHANGED = 2;
+
+        /** Send a message to this handler requesting to observe the setting for a new country. */
+        void observeSettingForCountry(String countryIso) {
+            obtainMessage(OBSERVE_SETTING, countryIso).sendToTarget();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case OBSERVE_SETTING:
+                    if (msg.obj != null && msg.obj instanceof String) {
+                        mCountryIso = (String) msg.obj;
+                        String settingName = getSettingNameForCountry(mCountryIso);
+                        ContentResolver resolver = mContext.getContentResolver();
+
+                        if (mSettingsObserver != null) {
+                            if (VDBG) log("Unregistering old content observer");
+                            resolver.unregisterContentObserver(mSettingsObserver);
+                        }
+
+                        mSettingsObserver = new SettingsObserver(this, SECURE_SETTINGS_CHANGED);
+                        resolver.registerContentObserver(
+                                Settings.Secure.getUriFor(settingName), false, mSettingsObserver);
+                        if (VDBG) log("Registered content observer for " + settingName);
+                    }
+                    break;
+
+                case SECURE_SETTINGS_CHANGED:
+                    loadPatternsFromSettings(mCountryIso);
+                    break;
+            }
+        }
+    }
+
+    /**
      * Create SMS usage monitor.
      * @param context the context to use to load resources and get TelephonyManager service
      */
@@ -164,6 +247,8 @@
                 Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
                 DEFAULT_SMS_CHECK_PERIOD);
 
+        mSettingsObserverHandler = new SettingsObserverHandler();
+
         // system MMS app is always allowed to send to short codes
         mApprovedShortCodeSenders.add("com.android.mms");
     }
@@ -178,27 +263,7 @@
         XmlResourceParser parser = mContext.getResources().getXml(id);
 
         try {
-            XmlUtils.beginDocument(parser, TAG_SHORTCODES);
-
-            while (true) {
-                XmlUtils.nextElement(parser);
-
-                String element = parser.getName();
-                if (element == null) break;
-
-                if (element.equals(TAG_SHORTCODE)) {
-                    String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
-                    if (country.equals(currentCountry)) {
-                        String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
-                        String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
-                        String free = parser.getAttributeValue(null, ATTR_FREE);
-                        String standard = parser.getAttributeValue(null, ATTR_STANDARD);
-                        return new ShortCodePatternMatcher(pattern, premium, free, standard);
-                    }
-                } else {
-                    Log.e(TAG, "Error: skipping unknown XML tag " + element);
-                }
-            }
+            return getPatternMatcher(country, parser);
         } catch (XmlPullParserException e) {
             Log.e(TAG, "XML parser exception reading short code pattern resource", e);
         } catch (IOException e) {
@@ -209,6 +274,60 @@
         return null;    // country not found
     }
 
+    /**
+     * Return a pattern matcher object for the specified country from a secure settings string.
+     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+     */
+    private static ShortCodePatternMatcher getPatternMatcher(String country, String settingsPattern) {
+        // embed pattern tag into an XML document.
+        String document = "<shortcodes>" + settingsPattern + "</shortcodes>";
+        if (VDBG) log("loading updated patterns from: " + document);
+
+        try {
+            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+            XmlPullParser parser = factory.newPullParser();
+            parser.setInput(new StringReader(document));
+            return getPatternMatcher(country, parser);
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "XML parser exception reading short code pattern from settings", e);
+        } catch (IOException e) {
+            Log.e(TAG, "I/O exception reading short code pattern from settings", e);
+        }
+        return null;    // country not found
+    }
+
+    /**
+     * Return a pattern matcher object for the specified country and pattern XML parser.
+     * @param country the country to search for
+     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+     */
+    private static ShortCodePatternMatcher getPatternMatcher(String country, XmlPullParser parser)
+            throws XmlPullParserException, IOException
+    {
+        XmlUtils.beginDocument(parser, TAG_SHORTCODES);
+
+        while (true) {
+            XmlUtils.nextElement(parser);
+
+            String element = parser.getName();
+            if (element == null) break;
+
+            if (element.equals(TAG_SHORTCODE)) {
+                String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
+                if (country.equals(currentCountry)) {
+                    String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
+                    String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
+                    String free = parser.getAttributeValue(null, ATTR_FREE);
+                    String standard = parser.getAttributeValue(null, ATTR_STANDARD);
+                    return new ShortCodePatternMatcher(pattern, premium, free, standard);
+                }
+            } else {
+                Log.e(TAG, "Error: skipping unknown XML tag " + element);
+            }
+        }
+        return null;    // country not found
+    }
+
     /** Clear the SMS application list for disposal. */
     void dispose() {
         mSmsStamp.clear();
@@ -244,7 +363,9 @@
      * @return true if the app is approved; false if we need to confirm short code destinations
      */
     public boolean isApprovedShortCodeSender(String appName) {
-        return mApprovedShortCodeSenders.contains(appName);
+        synchronized (mApprovedShortCodeSenders) {
+            return mApprovedShortCodeSenders.contains(appName);
+        }
     }
 
     /**
@@ -252,8 +373,10 @@
      * @param appName the package name of the app to add
      */
     public void addApprovedShortCodeSender(String appName) {
-        Log.d(TAG, "Adding " + appName + " to list of approved short code senders.");
-        mApprovedShortCodeSenders.add(appName);
+        if (DBG) log("Adding " + appName + " to list of approved short code senders.");
+        synchronized (mApprovedShortCodeSenders) {
+            mApprovedShortCodeSenders.add(appName);
+        }
     }
 
     /**
@@ -271,32 +394,71 @@
      *  {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
      */
     public int checkDestination(String destAddress, String countryIso) {
-        // always allow emergency numbers
-        if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
-            return CATEGORY_NOT_SHORT_CODE;
-        }
+        synchronized (mSettingsObserverHandler) {
+            // always allow emergency numbers
+            if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
+                return CATEGORY_NOT_SHORT_CODE;
+            }
 
-        ShortCodePatternMatcher patternMatcher = null;
+            ShortCodePatternMatcher patternMatcher = null;
 
-        if (countryIso != null) {
-            if (countryIso.equals(mCurrentCountry)) {
-                patternMatcher = mCurrentPatternMatcher;
+            if (countryIso != null) {
+                // query secure settings and initialize content observer for updated regex patterns
+                if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry)) {
+                    loadPatternsFromSettings(countryIso);
+                    mSettingsObserverHandler.observeSettingForCountry(countryIso);
+                }
+
+                if (countryIso.equals(mCurrentCountry)) {
+                    patternMatcher = mCurrentPatternMatcher;
+                } else {
+                    patternMatcher = getPatternMatcher(countryIso);
+                    mCurrentCountry = countryIso;
+                    mCurrentPatternMatcher = patternMatcher;    // may be null if not found
+                }
+            }
+
+            if (patternMatcher != null) {
+                return patternMatcher.getNumberCategory(destAddress);
             } else {
-                patternMatcher = getPatternMatcher(countryIso);
-                mCurrentCountry = countryIso;
-                mCurrentPatternMatcher = patternMatcher;    // may be null if not found
+                // Generic rule: numbers of 5 digits or less are considered potential short codes
+                Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
+                if (destAddress.length() <= 5) {
+                    return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+                } else {
+                    return CATEGORY_NOT_SHORT_CODE;
+                }
             }
         }
+    }
 
-        if (patternMatcher != null) {
-            return patternMatcher.getNumberCategory(destAddress);
-        } else {
-            // Generic rule: numbers of 5 digits or less are considered potential short codes
-            Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
-            if (destAddress.length() <= 5) {
-                return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
-            } else {
-                return CATEGORY_NOT_SHORT_CODE;
+    private static String getSettingNameForCountry(String countryIso) {
+        return Settings.Secure.SMS_SHORT_CODES_PREFIX + countryIso;
+    }
+
+    /**
+     * Load regex patterns from secure settings if present.
+     * @param countryIso the country to search for
+     */
+    void loadPatternsFromSettings(String countryIso) {
+        synchronized (mSettingsObserverHandler) {
+            if (VDBG) log("loadPatternsFromSettings(" + countryIso + ") called");
+            String settingsPatterns = Settings.Secure.getString(
+                    mContext.getContentResolver(), getSettingNameForCountry(countryIso));
+            if (settingsPatterns != null && !settingsPatterns.equals(
+                    mSettingsShortCodePatterns)) {
+                // settings pattern string has changed: update the pattern matcher
+                mSettingsShortCodePatterns = settingsPatterns;
+                ShortCodePatternMatcher matcher = getPatternMatcher(countryIso, settingsPatterns);
+                if (matcher != null) {
+                    mCurrentCountry = countryIso;
+                    mCurrentPatternMatcher = matcher;
+                }
+            } else if (settingsPatterns == null && mSettingsShortCodePatterns != null) {
+                // pattern string was removed: caller will load default patterns from XML resource
+                mCurrentCountry = null;
+                mCurrentPatternMatcher = null;
+                mSettingsShortCodePatterns = null;
             }
         }
     }
@@ -324,7 +486,7 @@
         Long ct = System.currentTimeMillis();
         long beginCheckPeriod = ct - mCheckPeriod;
 
-        Log.d(TAG, "SMS send size=" + sent.size() + " time=" + ct);
+        if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct);
 
         while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
             sent.remove(0);
@@ -338,4 +500,8 @@
         }
         return false;
     }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
 }