Merge "Modify auto brightness to return to dim level." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index 18a4b7b..7e9794f 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -485,6 +485,7 @@
     field public static final int focusable = 16842970; // 0x10100da
     field public static final int focusableInTouchMode = 16842971; // 0x10100db
     field public static final int focusedMonthDateColor = 16843587; // 0x1010343
+    field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
     field public static final int foreground = 16843017; // 0x1010109
     field public static final int foregroundGravity = 16843264; // 0x1010200
@@ -5822,6 +5823,7 @@
     field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
     field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000
     field public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
+    field public static final int FLAG_ACTIVITY_CLOSE_SYSTEM_DIALOGS = 8192; // 0x2000
     field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
@@ -6517,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";
@@ -6792,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
@@ -6809,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
@@ -9240,7 +9243,6 @@
     method public int getMinimumWidth();
     method public abstract int getOpacity();
     method public boolean getPadding(android.graphics.Rect);
-    method public int getResolvedLayoutDirectionSelf();
     method public int[] getState();
     method public android.graphics.Region getTransparentRegion();
     method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -19688,6 +19690,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";
@@ -19703,6 +19706,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";
@@ -22733,6 +22737,17 @@
     method public void onPrepareSubMenu(android.view.SubMenu);
   }
 
+  public final class Choreographer {
+    method public static android.view.Choreographer getInstance();
+    method public void postFrameCallback(android.view.Choreographer.FrameCallback);
+    method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long);
+    method public void removeFrameCallback(android.view.Choreographer.FrameCallback);
+  }
+
+  public static abstract interface Choreographer.FrameCallback {
+    method public abstract void doFrame(long);
+  }
+
   public abstract interface CollapsibleActionView {
     method public abstract void onActionViewCollapsed();
     method public abstract void onActionViewExpanded();
@@ -24508,14 +24523,14 @@
   public class ViewDebug {
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
-    method public static void startHierarchyTracing(java.lang.String, android.view.View);
-    method public static void startRecyclerTracing(java.lang.String, android.view.View);
-    method public static void stopHierarchyTracing();
-    method public static void stopRecyclerTracing();
-    method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
-    method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
-    field public static final boolean TRACE_HIERARCHY = false;
-    field public static final boolean TRACE_RECYCLER = false;
+    method public static deprecated void startHierarchyTracing(java.lang.String, android.view.View);
+    method public static deprecated void startRecyclerTracing(java.lang.String, android.view.View);
+    method public static deprecated void stopHierarchyTracing();
+    method public static deprecated void stopRecyclerTracing();
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
+    field public static final deprecated boolean TRACE_HIERARCHY = false;
+    field public static final deprecated boolean TRACE_RECYCLER = false;
   }
 
   public static abstract class ViewDebug.CapturedViewProperty implements java.lang.annotation.Annotation {
@@ -24527,7 +24542,7 @@
   public static abstract class ViewDebug.FlagToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.HierarchyTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.HierarchyTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.HierarchyTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.HierarchyTraceType[] values();
     enum_constant public static final android.view.ViewDebug.HierarchyTraceType BUILD_CACHE;
@@ -24543,7 +24558,7 @@
   public static abstract class ViewDebug.IntToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.RecyclerTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.RecyclerTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.RecyclerTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.RecyclerTraceType[] values();
     enum_constant public static final android.view.ViewDebug.RecyclerTraceType BIND_VIEW;
@@ -27324,7 +27339,7 @@
     field public int gravity;
   }
 
-  public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+  public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
     ctor public Gallery(android.content.Context);
     ctor public Gallery(android.content.Context, android.util.AttributeSet);
     ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28615,8 +28630,6 @@
     method public void onEndBatchEdit();
     method public boolean onPreDraw();
     method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
-    method public void onResolvedLayoutDirectionReset();
-    method public void onResolvedTextDirectionChanged();
     method public void onRestoreInstanceState(android.os.Parcelable);
     method public android.os.Parcelable onSaveInstanceState();
     method protected void onSelectionChanged(int, int);
diff --git a/api/current.txt b/api/current.txt
index 8402f31..7e9794f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5823,6 +5823,7 @@
     field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
     field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000
     field public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
+    field public static final int FLAG_ACTIVITY_CLOSE_SYSTEM_DIALOGS = 8192; // 0x2000
     field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
@@ -6518,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";
@@ -6793,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
@@ -6810,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
@@ -9241,7 +9243,6 @@
     method public int getMinimumWidth();
     method public abstract int getOpacity();
     method public boolean getPadding(android.graphics.Rect);
-    method public int getResolvedLayoutDirectionSelf();
     method public int[] getState();
     method public android.graphics.Region getTransparentRegion();
     method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -19689,6 +19690,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";
@@ -19704,6 +19706,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";
@@ -22734,6 +22737,17 @@
     method public void onPrepareSubMenu(android.view.SubMenu);
   }
 
+  public final class Choreographer {
+    method public static android.view.Choreographer getInstance();
+    method public void postFrameCallback(android.view.Choreographer.FrameCallback);
+    method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long);
+    method public void removeFrameCallback(android.view.Choreographer.FrameCallback);
+  }
+
+  public static abstract interface Choreographer.FrameCallback {
+    method public abstract void doFrame(long);
+  }
+
   public abstract interface CollapsibleActionView {
     method public abstract void onActionViewCollapsed();
     method public abstract void onActionViewExpanded();
@@ -24509,14 +24523,14 @@
   public class ViewDebug {
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
-    method public static void startHierarchyTracing(java.lang.String, android.view.View);
-    method public static void startRecyclerTracing(java.lang.String, android.view.View);
-    method public static void stopHierarchyTracing();
-    method public static void stopRecyclerTracing();
-    method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
-    method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
-    field public static final boolean TRACE_HIERARCHY = false;
-    field public static final boolean TRACE_RECYCLER = false;
+    method public static deprecated void startHierarchyTracing(java.lang.String, android.view.View);
+    method public static deprecated void startRecyclerTracing(java.lang.String, android.view.View);
+    method public static deprecated void stopHierarchyTracing();
+    method public static deprecated void stopRecyclerTracing();
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
+    method public static deprecated void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
+    field public static final deprecated boolean TRACE_HIERARCHY = false;
+    field public static final deprecated boolean TRACE_RECYCLER = false;
   }
 
   public static abstract class ViewDebug.CapturedViewProperty implements java.lang.annotation.Annotation {
@@ -24528,7 +24542,7 @@
   public static abstract class ViewDebug.FlagToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.HierarchyTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.HierarchyTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.HierarchyTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.HierarchyTraceType[] values();
     enum_constant public static final android.view.ViewDebug.HierarchyTraceType BUILD_CACHE;
@@ -24544,7 +24558,7 @@
   public static abstract class ViewDebug.IntToString implements java.lang.annotation.Annotation {
   }
 
-  public static final class ViewDebug.RecyclerTraceType extends java.lang.Enum {
+  public static final deprecated class ViewDebug.RecyclerTraceType extends java.lang.Enum {
     method public static android.view.ViewDebug.RecyclerTraceType valueOf(java.lang.String);
     method public static final android.view.ViewDebug.RecyclerTraceType[] values();
     enum_constant public static final android.view.ViewDebug.RecyclerTraceType BIND_VIEW;
@@ -27325,7 +27339,7 @@
     field public int gravity;
   }
 
-  public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+  public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
     ctor public Gallery(android.content.Context);
     ctor public Gallery(android.content.Context, android.util.AttributeSet);
     ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28616,8 +28630,6 @@
     method public void onEndBatchEdit();
     method public boolean onPreDraw();
     method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
-    method public void onResolvedLayoutDirectionReset();
-    method public void onResolvedTextDirectionChanged();
     method public void onRestoreInstanceState(android.os.Parcelable);
     method public android.os.Parcelable onSaveInstanceState();
     method protected void onSelectionChanged(int, int);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8cd8900..cb53422 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -645,10 +645,6 @@
         String process = null;
         
         String cmd = nextArgRequired();
-        if ("looper".equals(cmd)) {
-            cmd = nextArgRequired();
-            profileType = 1;
-        }
 
         if ("start".equals(cmd)) {
             start = true;
@@ -1295,8 +1291,8 @@
                 "       am broadcast <INTENT>\n" +
                 "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
                 "               [--no-window-animation] <COMPONENT>\n" +
-                "       am profile [looper] start <PROCESS> <FILE>\n" +
-                "       am profile [looper] stop [<PROCESS>]\n" +
+                "       am profile start <PROCESS> <FILE>\n" +
+                "       am profile stop [<PROCESS>]\n" +
                 "       am dumpheap [flags] <PROCESS> <FILE>\n" +
                 "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
                 "       am clear-debug-app\n" +
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 044c0c2..ebe2b98 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -59,9 +59,13 @@
  * An accessibility is declared as any other service in an AndroidManifest.xml but it
  * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
  * {@link android.content.Intent}. Failure to declare this intent will cause the system to
- * ignore the accessibility service. Following is an example declaration:
+ * ignore the accessibility service. Additionally an accessibility service must request
+ * "android.permission.BIND_ACCESSIBILITY_SERVICE" permission to ensure that only the system
+ * can bind to it. Failure to declare this intent will cause the system to ignore the
+ * accessibility service. Following is an example declaration:
  * </p>
- * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
+ * <pre> &lt;service android:name=".MyAccessibilityService"
+ *         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE&gt;
  *     &lt;intent-filter&gt;
  *         &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
  *     &lt;/intent-filter&gt;
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 136c68c..291e75e 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -119,8 +119,6 @@
                     + savedInstanceState + ")");
         }
 
-        setContentView(R.layout.choose_type_and_account);
-
         if (savedInstanceState != null) {
             mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);
             mExistingAccounts =
@@ -164,14 +162,29 @@
             }
         }
 
-        // Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes
-        Set<String> setOfAllowableAccountTypes = null;
-        final String[] validAccountTypes =
+        // An account type is relevant iff it is allowed by the caller and supported by the account
+        // manager.
+        Set<String> setOfRelevantAccountTypes = null;
+        final String[] allowedAccountTypes =
                 intent.getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);
-        if (validAccountTypes != null) {
-            setOfAllowableAccountTypes = new HashSet<String>(validAccountTypes.length);
-            for (String type : validAccountTypes) {
-                setOfAllowableAccountTypes.add(type);
+        if (allowedAccountTypes != null) {
+
+            setOfRelevantAccountTypes = new HashSet<String>(allowedAccountTypes.length);
+            Set<String> setOfAllowedAccountTypes = new HashSet<String>(allowedAccountTypes.length);
+            for (String type : allowedAccountTypes) {
+                setOfAllowedAccountTypes.add(type);
+            }
+
+            AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+            Set<String> supportedAccountTypes = new HashSet<String>(descs.length);
+            for (AuthenticatorDescription desc : descs) {
+                supportedAccountTypes.add(desc.type);
+            }
+
+            for (String acctType : setOfAllowedAccountTypes) {
+                if (supportedAccountTypes.contains(acctType)) {
+                    setOfRelevantAccountTypes.add(acctType);
+                }
             }
         }
 
@@ -185,8 +198,8 @@
                     && !setOfAllowableAccounts.contains(account)) {
                 continue;
             }
-            if (setOfAllowableAccountTypes != null
-                    && !setOfAllowableAccountTypes.contains(account.type)) {
+            if (setOfRelevantAccountTypes != null
+                    && !setOfRelevantAccountTypes.contains(account.type)) {
                 continue;
             }
             mAccountInfos.add(new AccountInfo(account,
@@ -194,30 +207,15 @@
                     account.equals(selectedAccount)));
         }
 
-        // there is more than one allowable account. initialize the list adapter to allow
-        // the user to select an account.
-        ListView list = (ListView) findViewById(android.R.id.list);
-        list.setAdapter(new AccountArrayAdapter(this,
-                android.R.layout.simple_list_item_1, mAccountInfos));
-        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-            public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-                onListItemClick((ListView)parent, v, position, id);
-            }
-        });
-
-        // set the listener for the addAccount button
-        Button addAccountButton = (Button) findViewById(R.id.addAccount);
-        addAccountButton.setOnClickListener(new View.OnClickListener() {
-            public void onClick(final View v) {
-                startChooseAccountTypeActivity();
-            }
-        });
-
         if (mPendingRequest == REQUEST_NULL) {
-            // If there are no allowable accounts go directly to add account
-            if (shouldSkipToChooseAccountTypeFlow()) {
-                startChooseAccountTypeActivity();
+            // If there are no relevant accounts and only one relevant account typoe go directly to
+            // add account. Otherwise let the user choose.
+            if (mAccountInfos.isEmpty()) {
+                if (setOfRelevantAccountTypes.size() == 1) {
+                    runAddAccountForAuthenticator(setOfRelevantAccountTypes.iterator().next());
+                } else {
+                    startChooseAccountTypeActivity();
+                }
                 return;
             }
 
@@ -229,6 +227,30 @@
                 return;
             }
         }
+
+        setContentView(R.layout.choose_type_and_account);
+
+        // there is more than one allowable account. initialize the list adapter to allow
+        // the user to select an account.
+        ListView list = (ListView) findViewById(android.R.id.list);
+        list.setAdapter(new AccountArrayAdapter(this,
+                android.R.layout.simple_list_item_1, mAccountInfos));
+        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+                onListItemClick((ListView)parent, v, position, id);
+            }
+        });
+
+        // set the listener for the addAccount button
+        Button addAccountButton = (Button) findViewById(R.id.addAccount);
+        addAccountButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(final View v) {
+                startChooseAccountTypeActivity();
+            }
+        });
     }
 
     @Override
@@ -267,7 +289,7 @@
         if (resultCode == RESULT_CANCELED) {
             // if cancelling out of addAccount and the original state caused us to skip this,
             // finish this activity
-            if (shouldSkipToChooseAccountTypeFlow()) {
+            if (mAccountInfos.isEmpty()) {
                 setResult(Activity.RESULT_CANCELED);
                 finish();
             }
@@ -324,14 +346,6 @@
         finish();
     }
 
-    /**
-     * convenience method to check if we should skip the accounts list display and immediately
-     * jump to the flow that asks the user to select from the account type list
-     */
-    private boolean shouldSkipToChooseAccountTypeFlow() {
-        return mAccountInfos.isEmpty();
-    }
-
     protected void runAddAccountForAuthenticator(String type) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "runAddAccountForAuthenticator: " + type);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ac55abe..f20fd33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2713,7 +2713,16 @@
                 onCreateNavigateUpTaskStack(b);
                 onPrepareNavigateUpTaskStack(b);
                 b.startActivities();
-                finishAffinity();
+
+                // We can't finishAffinity if we have a result.
+                // Fall back and simply finish the current activity instead.
+                if (mResultCode != RESULT_CANCELED || mResultData != null) {
+                    // Tell the developer what's going on to avoid hair-pulling.
+                    Log.i(TAG, "onNavigateUp only finishing topmost activity to return a result");
+                    finish();
+                } else {
+                    finishAffinity();
+                }
             } else {
                 navigateUpTo(upIntent);
             }
@@ -3264,7 +3273,7 @@
         if (mMenuInflater == null) {
             initActionBar();
             if (mActionBar != null) {
-                mMenuInflater = new MenuInflater(mActionBar.getThemedContext());
+                mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this);
             } else {
                 mMenuInflater = new MenuInflater(this);
             }
@@ -4990,7 +4999,8 @@
         mCurrentConfig = config;
     }
 
-    final IBinder getActivityToken() {
+    /** @hide */
+    public final IBinder getActivityToken() {
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4e61c3c..17b1962 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -26,7 +26,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
-import android.content.res.Configuration;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
@@ -36,16 +36,17 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -1798,6 +1799,40 @@
         }
     }
 
+    /** @hide */
+    public static int checkComponentPermission(String permission, int uid,
+            int owningUid, boolean exported) {
+        // Root, system server get to do everything.
+        if (uid == 0 || uid == Process.SYSTEM_UID) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        // Isolated processes don't get any permissions.
+        if (UserId.isIsolated(uid)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        // If there is a uid that owns whatever is being accessed, it has
+        // blanket access to it regardless of the permissions it requires.
+        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        // If the target is not exported, then nobody else can get to it.
+        if (!exported) {
+            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
+            return PackageManager.PERMISSION_DENIED;
+        }
+        if (permission == null) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        try {
+            return AppGlobals.getPackageManager()
+                    .checkUidPermission(permission, uid);
+        } catch (RemoteException e) {
+            // Should never happen, but if it does... deny!
+            Slog.e(TAG, "PackageManager is dead?!?", e);
+        }
+        return PackageManager.PERMISSION_DENIED;
+    }
+
     /**
      * Returns the usage statistics of each installed package.
      *
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2f2918d..4506546 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1656,6 +1656,15 @@
             return true;
         }
 
+        case GET_LAUNCHED_FROM_UID_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            int res = getLaunchedFromUid(token);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3785,5 +3794,18 @@
         return result;
     }
 
+    public int getLaunchedFromUid(IBinder activityToken) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(activityToken);
+        mRemote.transact(GET_LAUNCHED_FROM_UID_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return result;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5085b1e..33e639e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2090,8 +2090,15 @@
                     r.activity.mCalled = false;
                     mInstrumentation.callActivityOnPause(r.activity);
                     // We need to keep around the original state, in case
-                    // we need to be created again.
-                    r.state = oldState;
+                    // we need to be created again.  But we only do this
+                    // for pre-Honeycomb apps, which always save their state
+                    // when pausing, so we can not have them save their state
+                    // when restarting from a paused state.  For HC and later,
+                    // we want to (and can) let the state be saved as the normal
+                    // part of stopping the activity.
+                    if (r.isPreHoneycomb()) {
+                        r.state = oldState;
+                    }
                     if (!r.activity.mCalled) {
                         throw new SuperNotCalledException(
                             "Activity " + r.intent.getComponent().toShortString() +
@@ -3744,9 +3751,6 @@
         if (start) {
             try {
                 switch (profileType) {
-                    case 1:
-                        ViewDebug.startLooperProfiling(pcd.path, pcd.fd.getFileDescriptor());
-                        break;
                     default:                        
                         mProfiler.setProfiler(pcd.path, pcd.fd);
                         mProfiler.autoStopProfiler = false;
@@ -3765,9 +3769,6 @@
             }
         } else {
             switch (profileType) {
-                case 1:
-                    ViewDebug.stopLooperProfiling();
-                    break;
                 default:
                     mProfiler.stopProfiling();
                     break;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index d3ba497..5e6b090 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -28,6 +28,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.AttributeSet;
 import android.util.DebugUtils;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -108,7 +109,9 @@
         mInstance.mRetainInstance = mRetainInstance;
         mInstance.mDetached = mDetached;
         mInstance.mFragmentManager = activity.mFragments;
-        
+        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+                "Instantiated fragment " + mInstance);
+
         return mInstance;
     }
     
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 7e1daa4..6058bdc 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1074,6 +1074,7 @@
             f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
             mActive.set(f.mIndex, f);
         }
+        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
     }
     
     void makeInactive(Fragment f) {
@@ -1081,7 +1082,7 @@
             return;
         }
         
-        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
+        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
         mActive.set(f.mIndex, null);
         if (mAvailIndices == null) {
             mAvailIndices = new ArrayList<Integer>();
@@ -1493,6 +1494,7 @@
                     fragments.add(f);
                     f.mRetaining = true;
                     f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
+                    if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                 }
             }
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a2c7fa4..cf304df 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -350,6 +350,10 @@
     public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent resultData)
             throws RemoteException;
 
+    // This is not public because you need to be very careful in how you
+    // manage your activity to make sure it is always the uid you expect.
+    public int getLaunchedFromUid(IBinder activityToken) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -592,4 +596,5 @@
     int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146;
     int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
     int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
+    int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0c47069..618f1f8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -31,6 +32,8 @@
 import android.text.TextUtils;
 import android.util.IntProperty;
 import android.util.Log;
+import android.util.Slog;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
@@ -907,6 +910,8 @@
      * </pre>
      */
     public static class Builder {
+        private static final int MAX_ACTION_BUTTONS = 2;
+
         private Context mContext;
 
         private long mWhen;
@@ -938,7 +943,7 @@
         private ArrayList<String> mKindList = new ArrayList<String>(1);
         private Bundle mExtras;
         private int mPriority;
-        private ArrayList<Action> mActions = new ArrayList<Action>(3);
+        private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
         private boolean mUseChronometer;
         private Style mStyle;
 
@@ -1376,8 +1381,8 @@
 
         private RemoteViews applyStandardTemplate(int resId) {
             RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
-            boolean hasLine3 = false;
-            boolean hasLine2 = false;
+            boolean showLine3 = false;
+            boolean showLine2 = false;
             int smallIconImageViewId = R.id.icon;
             if (mLargeIcon != null) {
                 contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
@@ -1399,15 +1404,13 @@
                 contentView.setTextViewText(R.id.title, mContentTitle);
             }
             if (mContentText != null) {
-                contentView.setTextViewText(
-                        (mSubText != null) ? R.id.text2 : R.id.text, 
-                        mContentText);
-                hasLine3 = true;
+                contentView.setTextViewText(R.id.text, mContentText);
+                showLine3 = true;
             }
             if (mContentInfo != null) {
                 contentView.setTextViewText(R.id.info, mContentInfo);
                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
-                hasLine3 = true;
+                showLine3 = true;
             } else if (mNumber > 0) {
                 final int tooBig = mContext.getResources().getInteger(
                         R.integer.status_bar_notification_info_maxnum);
@@ -1419,25 +1422,42 @@
                     contentView.setTextViewText(R.id.info, f.format(mNumber));
                 }
                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
-                hasLine3 = true;
+                showLine3 = true;
             } else {
                 contentView.setViewVisibility(R.id.info, View.GONE);
             }
 
+            // Need to show three lines?
             if (mSubText != null) {
                 contentView.setTextViewText(R.id.text, mSubText);
-                contentView.setViewVisibility(R.id.text2,
-                        mContentText != null ? View.VISIBLE : View.GONE);
+                if (mContentText != null) {
+                    contentView.setTextViewText(R.id.text2, mContentText);
+                    // need to shrink all the type to make sure everything fits
+                    contentView.setViewVisibility(R.id.text2, View.VISIBLE);
+                    showLine2 = true;
+                } else {
+                    contentView.setViewVisibility(R.id.text2, View.GONE);
+                }
             } else {
                 contentView.setViewVisibility(R.id.text2, View.GONE);
                 if (mProgressMax != 0 || mProgressIndeterminate) {
                     contentView.setProgressBar(
                             R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
                     contentView.setViewVisibility(R.id.progress, View.VISIBLE);
+                    showLine2 = true;
                 } else {
                     contentView.setViewVisibility(R.id.progress, View.GONE);
                 }
             }
+            if (showLine2) {
+                final Resources res = mContext.getResources();
+                final float subTextSize = res.getDimensionPixelSize(
+                        R.dimen.notification_subtext_size);
+                contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+                // vertical centering
+                contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
+            }
+
             if (mWhen != 0) {
                 if (mUseChronometer) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
@@ -1449,7 +1469,7 @@
                     contentView.setLong(R.id.time, "setTime", mWhen);
                 }
             }
-            contentView.setViewVisibility(R.id.line3, hasLine3 ? View.VISIBLE : View.GONE);
+            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
             return contentView;
         }
 
@@ -1460,7 +1480,7 @@
             if (N > 0) {
                 // Log.d("Notification", "has actions: " + mContentText);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
-                if (N>3) N=3;
+                if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 big.removeAllViews(R.id.actions);
                 for (int i=0; i<N; i++) {
                     final RemoteViews button = generateActionButton(mActions.get(i));
@@ -1500,18 +1520,14 @@
         }
 
         private RemoteViews generateActionButton(Action action) {
-            RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action);
+            final boolean tombstone = (action.actionIntent == null);
+            RemoteViews button = new RemoteViews(mContext.getPackageName(), 
+                    tombstone ? R.layout.notification_action_tombstone
+                              : R.layout.notification_action);
             button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0);
             button.setTextViewText(R.id.action0, action.title);
-            if (action.actionIntent != null) {
+            if (!tombstone) {
                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
-                //button.setBoolean(R.id.action0, "setEnabled", true);
-                button.setFloat(R.id.button0, "setAlpha", 1.0f);
-                button.setBoolean(R.id.button0, "setClickable", true);
-            } else {
-                //button.setBoolean(R.id.action0, "setEnabled", false);
-                button.setFloat(R.id.button0, "setAlpha", 0.5f);
-                button.setBoolean(R.id.button0, "setClickable", false);
             }
             button.setContentDescription(R.id.action0, action.title);
             return button;
@@ -1631,23 +1647,21 @@
                 mBuilder.setContentTitle(mBigContentTitle);
             }
 
-            if (mBuilder.mSubText == null) {
-                mBuilder.setContentText(null);
-            }
-
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
 
-            if (mBuilder.mSubText == null) {
-                contentView.setViewVisibility(R.id.line3, View.GONE);
-            }
-
             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
                 contentView.setViewVisibility(R.id.line1, View.GONE);
+            } else {
+                contentView.setViewVisibility(R.id.line1, View.VISIBLE);
             }
 
             if (mSummaryText != null && !mSummaryText.equals("")) {
                 contentView.setViewVisibility(R.id.overflow_title, View.VISIBLE);
                 contentView.setTextViewText(R.id.overflow_title, mSummaryText);
+                contentView.setViewVisibility(R.id.line3, View.GONE);
+            } else {
+                contentView.setViewVisibility(R.id.overflow_title, View.GONE);
+                contentView.setViewVisibility(R.id.line3, View.VISIBLE);
             }
 
             return contentView;
@@ -1846,7 +1860,12 @@
             contentView.setViewVisibility(R.id.text2, View.GONE);
 
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
-                    R.id.inbox_text4};
+                    R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
+
+            // Make sure all rows are gone in case we reuse a view.
+            for (int rowId : rowIds) {
+                contentView.setViewVisibility(rowId, View.GONE);
+            }
 
             int i=0;
             while (i < mTexts.size() && i < rowIds.length) {
@@ -1858,6 +1877,12 @@
                 i++;
             }
 
+            if  (mTexts.size() > rowIds.length) {
+                contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE);
+            } else {
+                contentView.setViewVisibility(R.id.inbox_more, View.GONE);
+            }
+
             return contentView;
         }
 
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java
index 14c5736..f21b3fd 100644
--- a/core/java/android/app/TaskStackBuilder.java
+++ b/core/java/android/app/TaskStackBuilder.java
@@ -161,18 +161,12 @@
             ActivityInfo info = pm.getActivityInfo(
                     new ComponentName(mSourceContext, sourceActivityClass), 0);
             String parentActivity = info.parentActivityName;
-            Intent parent = new Intent().setComponent(
-                    new ComponentName(mSourceContext, parentActivity));
-            while (parent != null) {
+            while (parentActivity != null) {
+                Intent parent = new Intent().setComponent(
+                        new ComponentName(mSourceContext, parentActivity));
                 mIntents.add(insertAt, parent);
                 info = pm.getActivityInfo(parent.getComponent(), 0);
                 parentActivity = info.parentActivityName;
-                if (parentActivity != null) {
-                    parent = new Intent().setComponent(
-                            new ComponentName(mSourceContext, parentActivity));
-                } else {
-                    parent = null;
-                }
             }
         } catch (NameNotFoundException e) {
             Log.e(TAG, "Bad ComponentName while traversing activity parent metadata");
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 01b68d4..ed95ae5 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -38,6 +38,7 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Adapter;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
@@ -523,6 +524,12 @@
         return tv;
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(AppWidgetHostView.class.getName());
+    }
+
     private static class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
         public int describeContents() {
             return 0;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 7a8c1fb..3aa5181 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -320,6 +320,10 @@
      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
      * and outside of the handler.
      * This method will only work when called from the uid that owns the AppWidget provider.
+     * 
+     * <p>
+     * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
+     * fill the screen once, ie. (screen width x screen height x 4) bytes.
      *
      * @param appWidgetIds     The AppWidget instances for which to set the RemoteViews.
      * @param views         The RemoteViews object to show.
@@ -385,6 +389,10 @@
      * and outside of the handler.
      * This method will only work when called from the uid that owns the AppWidget provider.
      *
+     * <p>
+     * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
+     * fill the screen once, ie. (screen width x screen height x 4) bytes.
+     *
      * @param appWidgetId      The AppWidget instance for which to set the RemoteViews.
      * @param views         The RemoteViews object to show.
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c791e47..edd509b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -808,13 +808,22 @@
      * always present to the user a list of the things they can do, with a
      * nice title given by the caller such as "Send this photo with:".
      * <p>
+     * If you need to grant URI permissions through a chooser, you must specify
+     * the permissions to be granted on the ACTION_CHOOSER Intent
+     * <em>in addition</em> to the EXTRA_INTENT inside.  This means using
+     * {@link #setClipData} to specify the URIs to be granted as well as
+     * {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION} as appropriate.
+     * <p>
      * As a convenience, an Intent of this form can be created with the
      * {@link #createChooser} function.
-     * <p>Input: No data should be specified.  get*Extra must have
+     * <p>
+     * Input: No data should be specified.  get*Extra must have
      * a {@link #EXTRA_INTENT} field containing the Intent being executed,
      * and can optionally have a {@link #EXTRA_TITLE} field containing the
      * title text to display in the chooser.
-     * <p>Output: Depends on the protocol of {@link #EXTRA_INTENT}.
+     * <p>
+     * Output: Depends on the protocol of {@link #EXTRA_INTENT}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CHOOSER = "android.intent.action.CHOOSER";
@@ -822,6 +831,14 @@
     /**
      * Convenience function for creating a {@link #ACTION_CHOOSER} Intent.
      *
+     * <p>Builds a new {@link #ACTION_CHOOSER} Intent that wraps the given
+     * target intent, also optionally supplying a title.  If the target
+     * intent has specified {@link #FLAG_GRANT_READ_URI_PERMISSION} or
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be
+     * set in the returned chooser intent, with its ClipData set appropriately:
+     * either a direct reflection of {@link #getClipData()} if that is non-null,
+     * or a new ClipData build from {@link #getData()}.
+     *
      * @param target The Intent that the user will be selecting an activity
      * to perform.
      * @param title Optional title that will be displayed in the chooser.
@@ -835,8 +852,31 @@
         if (title != null) {
             intent.putExtra(EXTRA_TITLE, title);
         }
+
+        // Migrate any clip data and flags from target.
+        int permFlags = target.getFlags()
+                & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (permFlags != 0) {
+            ClipData targetClipData = target.getClipData();
+            if (targetClipData == null && target.getData() != null) {
+                ClipData.Item item = new ClipData.Item(target.getData());
+                String[] mimeTypes;
+                if (target.getType() != null) {
+                    mimeTypes = new String[] { target.getType() };
+                } else {
+                    mimeTypes = new String[] { };
+                }
+                targetClipData = new ClipData(null, mimeTypes, item);
+            }
+            if (targetClipData != null) {
+                intent.setClipData(targetClipData);
+                intent.addFlags(permFlags);
+            }
+        }
+
         return intent;
     }
+
     /**
      * Activity Action: Allow the user to select a particular kind of data and
      * return it.  This is different than {@link #ACTION_PICK} in that here we
@@ -3063,6 +3103,17 @@
      */
     public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
     /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * upon starting the activity the system will also clear any system dialogs that
+     * are currently shown.  This is intended primarily for any actions that are
+     * associated with buttons in a notification: tapping on the button to launch
+     * the activity needs to also dismiss the notification window (which is one
+     * of the system dialogs); setting this flag on the Intent associated with that
+     * action will ensure that and other system dialogs are dismissed so that the
+     * user arrives in the new activity.
+     */
+    public static final int FLAG_ACTIVITY_CLOSE_SYSTEM_DIALOGS = 0X00002000;
+    /**
      * If set, when sending a broadcast only registered receivers will be
      * called -- no BroadcastReceiver components will be launched.
      */
@@ -6587,19 +6638,38 @@
 
     /**
      * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
-     * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}.
+     * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
+     * intents in {@link #ACTION_CHOOSER}.
      *
+     * @return Whether any contents were migrated.
      * @hide
      */
-    public void migrateExtraStreamToClipData() {
+    public boolean migrateExtraStreamToClipData() {
         // Refuse to touch if extras already parcelled
-        if (mExtras != null && mExtras.isParcelled()) return;
+        if (mExtras != null && mExtras.isParcelled()) return false;
 
         // Bail when someone already gave us ClipData
-        if (getClipData() != null) return;
+        if (getClipData() != null) return false;
 
         final String action = getAction();
-        if (ACTION_SEND.equals(action)) {
+        if (ACTION_CHOOSER.equals(action)) {
+            try {
+                // Inspect target intent to see if we need to migrate
+                final Intent target = getParcelableExtra(EXTRA_INTENT);
+                if (target != null && target.migrateExtraStreamToClipData()) {
+                    // Since we migrated in child, we need to promote ClipData
+                    // and flags to ourselves to grant.
+                    setClipData(target.getClipData());
+                    addFlags(target.getFlags()
+                            & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION));
+                    return true;
+                } else {
+                    return false;
+                }
+            } catch (ClassCastException e) {
+            }
+
+        } else if (ACTION_SEND.equals(action)) {
             try {
                 final Uri stream = getParcelableExtra(EXTRA_STREAM);
                 final CharSequence text = getCharSequenceExtra(EXTRA_TEXT);
@@ -6610,6 +6680,7 @@
                             new ClipData.Item(text, htmlText, null, stream));
                     setClipData(clipData);
                     addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    return true;
                 }
             } catch (ClassCastException e) {
             }
@@ -6626,14 +6697,14 @@
                 if (texts != null) {
                     if (num >= 0 && num != texts.size()) {
                         // Wha...!  F- you.
-                        return;
+                        return false;
                     }
                     num = texts.size();
                 }
                 if (htmlTexts != null) {
                     if (num >= 0 && num != htmlTexts.size()) {
                         // Wha...!  F- you.
-                        return;
+                        return false;
                     }
                     num = htmlTexts.size();
                 }
@@ -6648,10 +6719,13 @@
 
                     setClipData(clipData);
                     addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    return true;
                 }
             } catch (ClassCastException e) {
             }
         }
+
+        return false;
     }
 
     private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts,
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/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c682852..c630bb5 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -630,7 +630,20 @@
      * Various types of objects will be returned depending on the underlying
      * resource -- for example, a solid color, PNG image, scalable image, etc.
      * The Drawable API hides these implementation details.
-     * 
+     *
+     * <p class="note"><strong>Note:</strong> Prior to
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, this function
+     * would not correctly retrieve the final configuration density when
+     * the resource ID passed here is an alias to another Drawable resource.
+     * This means that if the density configuration of the alias resource
+     * is different than the actual resource, the density of the returned
+     * Drawable would be incorrect, resulting in bad scaling.  To work
+     * around this, you can instead retrieve the Drawable through
+     * {@link TypedArray#getDrawable TypedArray.getDrawable}.  Use
+     * {@link android.content.Context#obtainStyledAttributes(int[])
+     * Context.obtainStyledAttributes} with
+     * an array containing the resource ID of interest to create the TypedArray.</p>
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index aeb46cf..b8ad818 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1122,9 +1122,9 @@
 
     /** Helper function to compute the angle change between two rotation matrices.
      *  Given a current rotation matrix (R) and a previous rotation matrix
-     *  (prevR) computes the rotation around the x,y, and z axes which
+     *  (prevR) computes the rotation around the z,x, and y axes which
      *  transforms prevR to R.
-     *  outputs a 3 element vector containing the x,y, and z angle
+     *  outputs a 3 element vector containing the z,x, and y angle
      *  change at indexes 0, 1, and 2 respectively.
      * <p> Each input matrix is either as a 3x3 or 4x4 row-major matrix
      * depending on the length of the passed array:
@@ -1143,14 +1143,13 @@
      *</pre>
      * @param R current rotation matrix
      * @param prevR previous rotation matrix
-     * @param angleChange an array of floats in which the angle change is stored
+     * @param angleChange an an array of floats (z, x, and y) in which the angle change is stored
      */
 
     public static void getAngleChange( float[] angleChange, float[] R, float[] prevR) {
         float rd1=0,rd4=0, rd6=0,rd7=0, rd8=0;
         float ri0=0,ri1=0,ri2=0,ri3=0,ri4=0,ri5=0,ri6=0,ri7=0,ri8=0;
         float pri0=0, pri1=0, pri2=0, pri3=0, pri4=0, pri5=0, pri6=0, pri7=0, pri8=0;
-        int i, j, k;
 
         if(R.length == 9) {
             ri0 = R[0];
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 3137947..9b6f82a 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,8 +41,13 @@
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
-    String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
-    void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor);
+    void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor);
+    String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor);
+    void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor);
+    void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor);
 
     // Registers an input devices changed listener.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6448b55..262d87d 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.input;
 
+import com.android.internal.util.ArrayUtils;
+
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
@@ -77,7 +79,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 +93,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">
@@ -214,6 +219,41 @@
     }
 
     /**
+     * Gets information about the input device with the specified descriptor.
+     * @param descriptor The input device descriptor.
+     * @return The input device or null if not found.
+     * @hide
+     */
+    public InputDevice getInputDeviceByDescriptor(String descriptor) {
+        if (descriptor == null) {
+            throw new IllegalArgumentException("descriptor must not be null.");
+        }
+
+        synchronized (mInputDevicesLock) {
+            populateInputDevicesLocked();
+
+            int numDevices = mInputDevices.size();
+            for (int i = 0; i < numDevices; i++) {
+                InputDevice inputDevice = mInputDevices.valueAt(i);
+                if (inputDevice == null) {
+                    int id = mInputDevices.keyAt(i);
+                    try {
+                        inputDevice = mIm.getInputDevice(id);
+                    } catch (RemoteException ex) {
+                        // Ignore the problem for the purposes of this method.
+                        continue;
+                    }
+                    mInputDevices.setValueAt(i, inputDevice);
+                }
+                if (descriptor.equals(inputDevice.getDescriptor())) {
+                    return inputDevice;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
      * Gets the ids of all input devices in the system.
      * @return The input device ids.
      */
@@ -329,50 +369,129 @@
     }
 
     /**
-     * Gets the keyboard layout descriptor for the specified input device.
+     * Gets the current keyboard layout descriptor for the specified input device.
      *
      * @param inputDeviceDescriptor The input device descriptor.
-     * @return The keyboard layout descriptor, or null if unknown or if the default
-     * keyboard layout will be used.
+     * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
      *
      * @hide
      */
-    public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
+    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         try {
-            return mIm.getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+            return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
         } catch (RemoteException ex) {
-            Log.w(TAG, "Could not get keyboard layout for input device.", ex);
+            Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
             return null;
         }
     }
 
     /**
-     * Sets the keyboard layout descriptor for the specified input device.
+     * Sets the current keyboard layout descriptor for the specified input device.
      * <p>
      * This method may have the side-effect of causing the input device in question
      * to be reconfigured.
      * </p>
      *
      * @param inputDeviceDescriptor The input device descriptor.
-     * @param keyboardLayoutDescriptor The keyboard layout descriptor, or null to remove
-     * the mapping so that the default keyboard layout will be used for the input device.
+     * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
      *
      * @hide
      */
-    public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor,
+                    keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
+        }
+    }
+
+    /**
+     * Gets all keyboard layout descriptors that are enabled for the specified input device.
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @return The keyboard layout descriptors.
+     *
+     * @hide
+     */
+    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
 
         try {
-            mIm.setKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+            return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor);
         } catch (RemoteException ex) {
-            Log.w(TAG, "Could not set keyboard layout for input device.", ex);
+            Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
+            return ArrayUtils.emptyArray(String.class);
+        }
+    }
+
+    /**
+     * Adds the keyboard layout descriptor for the specified input device.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
+     *
+     * @hide
+     */
+    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not add keyboard layout for input device.", ex);
+        }
+    }
+
+    /**
+     * Removes the keyboard layout descriptor for the specified input device.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
+     *
+     * @hide
+     */
+    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        try {
+            mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
         }
     }
 
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/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 7257521..4916244 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -855,15 +855,23 @@
                 Key oldKey = keys[oldKeyIndex];
                 oldKey.onReleased(mCurrentKeyIndex == NOT_A_KEY);
                 invalidateKey(oldKeyIndex);
+                final int keyCode = oldKey.codes[0];
                 sendAccessibilityEventForUnicodeCharacter(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT,
-                        oldKey.codes[0]);
+                        keyCode);
+                // TODO: We need to implement AccessibilityNodeProvider for this view.
+                sendAccessibilityEventForUnicodeCharacter(
+                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, keyCode);
             }
             if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) {
                 Key newKey = keys[mCurrentKeyIndex];
                 newKey.onPressed();
                 invalidateKey(mCurrentKeyIndex);
+                final int keyCode = newKey.codes[0];
                 sendAccessibilityEventForUnicodeCharacter(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
-                        newKey.codes[0]);
+                        keyCode);
+                // TODO: We need to implement AccessibilityNodeProvider for this view.
+                sendAccessibilityEventForUnicodeCharacter(
+                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, keyCode);
             }
         }
         // If key changed and preview is on ...
@@ -1154,20 +1162,17 @@
         if (mAccessibilityManager.isTouchExplorationEnabled() && event.getPointerCount() == 1) {
             final int action = event.getAction();
             switch (action) {
-                case MotionEvent.ACTION_HOVER_ENTER:
-                case MotionEvent.ACTION_HOVER_MOVE:
-                    final int touchX = (int) event.getX() - mPaddingLeft;
-                    int touchY = (int) event.getY() - mPaddingTop;
-                    if (touchY >= -mVerticalCorrection) {
-                        touchY += mVerticalCorrection;
-                    }
-                    final int keyIndex = getKeyIndices(touchX, touchY, null);
-                    showPreview(keyIndex);
-                    break;
-                case MotionEvent.ACTION_HOVER_EXIT:
-                    showPreview(NOT_A_KEY);
-                    break;
+                case MotionEvent.ACTION_HOVER_ENTER: {
+                    event.setAction(MotionEvent.ACTION_DOWN);
+                } break;
+                case MotionEvent.ACTION_HOVER_MOVE: {
+                    event.setAction(MotionEvent.ACTION_MOVE);
+                } break;
+                case MotionEvent.ACTION_HOVER_EXIT: {
+                    event.setAction(MotionEvent.ACTION_UP);
+                } break;
             }
+            return onTouchEvent(event);
         }
         return true;
     }
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 844d055..fb7a4f8 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -111,6 +111,14 @@
                     && operations == 0;
         }
 
+        public void add(Entry another) {
+            this.rxBytes += another.rxBytes;
+            this.rxPackets += another.rxPackets;
+            this.txBytes += another.txBytes;
+            this.txPackets += another.txPackets;
+            this.operations += another.operations;
+        }
+
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 0003c6e..a37c26f9 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -342,11 +342,23 @@
      * for combining together stats for external reporting.
      */
     public void recordEntireHistory(NetworkStatsHistory input) {
+        recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE);
+    }
+
+    /**
+     * Record given {@link NetworkStatsHistory} into this history, copying only
+     * buckets that atomically occur in the inclusive time range. Doesn't
+     * interpolate across partial buckets.
+     */
+    public void recordHistory(NetworkStatsHistory input, long start, long end) {
         final NetworkStats.Entry entry = new NetworkStats.Entry(
                 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
         for (int i = 0; i < input.bucketCount; i++) {
-            final long start = input.bucketStart[i];
-            final long end = start + input.bucketDuration;
+            final long bucketStart = input.bucketStart[i];
+            final long bucketEnd = bucketStart + input.bucketDuration;
+
+            // skip when bucket is outside requested range
+            if (bucketStart < start || bucketEnd > end) continue;
 
             entry.rxBytes = getLong(input.rxBytes, i, 0L);
             entry.rxPackets = getLong(input.rxPackets, i, 0L);
@@ -354,7 +366,7 @@
             entry.txPackets = getLong(input.txPackets, i, 0L);
             entry.operations = getLong(input.operations, i, 0L);
 
-            recordData(start, end, entry);
+            recordData(bucketStart, bucketEnd, entry);
         }
     }
 
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 39a4d7b..d8e53d5 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -61,6 +61,13 @@
                 com.android.internal.R.array.config_data_usage_network_types);
     }
 
+    private static boolean sForceAllNetworkTypes = false;
+
+    // @VisibleForTesting
+    public static void forceAllNetworkTypes() {
+        sForceAllNetworkTypes = true;
+    }
+
     /**
      * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
      * the given IMSI.
@@ -225,7 +232,7 @@
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
-            return (contains(DATA_USAGE_NETWORK_TYPES, ident.mType)
+            return ((sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType))
                     && Objects.equal(mSubscriberId, ident.mSubscriberId));
         }
     }
@@ -291,7 +298,7 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
+            return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
         }
     }
 
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 6a4f1f2..2703f1d 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -261,8 +261,8 @@
      * server then the first protocol in the client's list will be selected.
      * The order of the client's protocols is otherwise insignificant.
      *
-     * @param npnProtocols a possibly-empty list of protocol byte arrays. All
-     *     arrays must be non-empty and of length less than 256.
+     * @param npnProtocols a non-empty list of protocol byte arrays. All arrays
+     *     must be non-empty and of length less than 256.
      */
     public void setNpnProtocols(byte[][] npnProtocols) {
         this.mNpnProtocols = toNpnProtocolsList(npnProtocols);
@@ -273,6 +273,9 @@
      * strings.
      */
     static byte[] toNpnProtocolsList(byte[]... npnProtocols) {
+        if (npnProtocols.length == 0) {
+            throw new IllegalArgumentException("npnProtocols.length == 0");
+        }
         int totalLength = 0;
         for (byte[] s : npnProtocols) {
             if (s.length == 0 || s.length > 255) {
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/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 97ed235..69e1de9 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -36,6 +36,13 @@
  * perform background operations and publish results on the UI thread without
  * having to manipulate threads and/or handlers.</p>
  *
+ * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
+ * and does not constitute a generic threading framework. AsyncTasks should ideally be
+ * used for short operations (a few seconds at the most.) If you need to keep threads
+ * running for long periods of time, it is highly recommended you use the various APIs
+ * provided by the <code>java.util.concurrent</code> pacakge such as {@link Executor},
+ * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
+ *
  * <p>An asynchronous task is defined by a computation that runs on a background thread and
  * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
  * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
@@ -63,6 +70,8 @@
  *         for (int i = 0; i < count; i++) {
  *             totalSize += Downloader.downloadFile(urls[i]);
  *             publishProgress((int) ((i / (float) count) * 100));
+ *             // Escape early if cancel() is called
+ *             if (isCancelled()) break;
  *         }
  *         return totalSize;
  *     }
@@ -154,6 +163,16 @@
  *     <li>Set member fields in {@link #doInBackground}, and refer to them in
  *     {@link #onProgressUpdate} and {@link #onPostExecute}.
  * </ul>
+ *
+ * <h2>Order of execution</h2>
+ * <p>When first introduced, AsyncTasks were executed serially on a single background
+ * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+ * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
+ * thread to avoid common application errors caused by parallel execution.</p>
+ * <p>If you truly want parallel execution, you can invoke
+ * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
+ * {@link #THREAD_POOL_EXECUTOR}.</p>
  */
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
@@ -491,13 +510,13 @@
      * thread or pool of threads depending on the platform version.  When first
      * introduced, AsyncTasks were executed serially on a single background thread.
      * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
-     * to a pool of threads allowing multiple tasks to operate in parallel.  After
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, it is planned to change this
-     * back to a single thread to avoid common application errors caused
+     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
+     * executed on a single thread to avoid common application errors caused
      * by parallel execution.  If you truly want parallel execution, you can use
      * the {@link #executeOnExecutor} version of this method
-     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
-     * its use.
+     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
+     * on its use.
      *
      * <p>This method must be invoked on the UI thread.
      *
@@ -507,6 +526,9 @@
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
      *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     * @see #execute(Runnable)
      */
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
         return executeOnExecutor(sDefaultExecutor, params);
@@ -542,6 +564,8 @@
      *
      * @throws IllegalStateException If {@link #getStatus()} returns either
      *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #execute(Object[])
      */
     public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
             Params... params) {
@@ -569,7 +593,11 @@
 
     /**
      * Convenience version of {@link #execute(Object...)} for use with
-     * a simple Runnable object.
+     * a simple Runnable object. See {@link #execute(Object[])} for more
+     * information on the order of execution.
+     *
+     * @see #execute(Object[])
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
      */
     public static void execute(Runnable runnable) {
         sDefaultExecutor.execute(runnable);
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f7f0263..679cf1a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -94,6 +94,16 @@
     }
 
     /**
+     * Return directory used for internal media storage, which is protected by
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
+     *
+     * @hide
+     */
+    public static File getMediaStorageDirectory() {
+        return MEDIA_STORAGE_DIRECTORY;
+    }
+
+    /**
      * Returns whether the Encrypted File System feature is enabled on the device or not.
      * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
      * if disabled.
@@ -112,6 +122,10 @@
     private static final File SECURE_DATA_DIRECTORY
             = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
 
+    /** @hide */
+    private static final File MEDIA_STORAGE_DIRECTORY
+            = getDirectory("MEDIA_STORAGE", "/data/media");
+
     private static final File EXTERNAL_STORAGE_DIRECTORY
             = getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0");
 
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index a06aadb6..02135bc 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -127,29 +127,17 @@
                 return;
             }
 
-            long wallStart = 0;
-            long threadStart = 0;
-
             // This must be in a local variable, in case a UI event sets the logger
             Printer logging = me.mLogging;
             if (logging != null) {
                 logging.println(">>>>> Dispatching to " + msg.target + " " +
                         msg.callback + ": " + msg.what);
-                wallStart = SystemClock.currentTimeMicro();
-                threadStart = SystemClock.currentThreadTimeMicro();
             }
 
             msg.target.dispatchMessage(msg);
 
             if (logging != null) {
-                long wallTime = SystemClock.currentTimeMicro() - wallStart;
-                long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
-
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
-                if (logging instanceof Profiler) {
-                    ((Profiler) logging).profile(msg, wallStart, wallTime,
-                            threadStart, threadTime);
-                }
             }
 
             // Make sure that during the course of dispatching the
@@ -290,12 +278,4 @@
     public String toString() {
         return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
     }
-
-    /**
-     * @hide
-     */
-    public static interface Profiler {
-        void profile(Message message, long wallStart, long wallTime,
-                long threadStart, long threadTime);
-    }
 }
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index c0240fe..d2050b7 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -43,7 +43,7 @@
     public static final int TRACE_FLAGS_START_BIT = 1;
     public static final String[] TRACE_TAGS = {
         "Graphics", "Input", "View", "WebView", "Window Manager",
-        "Activity Manager", "Sync Manager", "Audio"
+        "Activity Manager", "Sync Manager", "Audio", "Video",
     };
 
     public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
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/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index c59ed18..a643c8a 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -261,6 +261,8 @@
     
     @Override
     protected void onClick() {
+        if (mDialog != null && mDialog.isShowing()) return;
+
         showDialog(null);
     }
 
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 2e8d551..553ce80 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -125,8 +125,9 @@
      * @param values The values to set for the key.
      */
     public void setValues(Set<String> values) {
-        mValues = values;
-        
+        mValues.clear();
+        mValues.addAll(values);
+
         persistStringSet(values);
     }
     
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 09bf42b..1ef0916 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -105,6 +105,17 @@
      * and it should call {@link Activity#setResult(int)} with
      * {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} to
      * acknowledge whether the action was handled or not.
+     *
+     * The custom app should have an intent-filter like the following
+     * <pre>
+     * {@code
+     * <intent-filter>
+     *    <action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" />
+     *    <category android:name="android.intent.category.DEFAULT" />
+     *    <data android:mimeType="vnd.android.cursor.item/event" />
+     * </intent-filter>
+     * }
+     * </pre>
      * <p>
      * Input: {@link Intent#getData} has the event URI. The extra
      * {@link #EXTRA_EVENT_BEGIN_TIME} has the start time of the instance. The
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ea3cab4..8b7ee0e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4210,6 +4210,8 @@
         public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
         /** {@hide} */
         public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
+        /** {@hide} */
+        public static final String NETSTATS_REPORT_XT_OVER_DEV = "netstats_report_xt_over_dev";
 
         /** {@hide} */
         public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
@@ -4266,6 +4268,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/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 3cf207f..97c0209 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -530,7 +530,7 @@
         // Bluetooth stack needs a small delay here before adding
         // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
         try {
-            Thread.sleep(20);
+            Thread.sleep(50);
         } catch (InterruptedException e) {}
         updateSdpRecords();
         return true;
@@ -602,7 +602,7 @@
         // Bluetooth stack need some a small delay here before adding more
         // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
         try {
-            Thread.sleep(20);
+            Thread.sleep(50);
         } catch (InterruptedException e) {}
 
         if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
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/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index e1f1db2..6dc31dd 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -29,7 +29,6 @@
 import android.util.PoolableManager;
 import android.util.Pools;
 import android.util.SparseLongArray;
-import android.view.ViewGroup.ChildListForAccessibility;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
@@ -491,20 +490,28 @@
                 if ((direction & View.FOCUS_ACCESSIBILITY) ==  View.FOCUS_ACCESSIBILITY) {
                     AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
                     if (provider != null) {
-                        next = provider.accessibilityFocusSearch(direction,
-                                virtualDescendantId);
-                    } else if (virtualDescendantId == View.NO_ID) {
-                        View nextView = root.focusSearch(direction);
-                        if (nextView != null) {
-                            // If the focus search reached a node with a provider
-                            // we delegate to the provider to find the next one.
-                            provider = nextView.getAccessibilityNodeProvider();
-                            if (provider != null) {
-                                next = provider.accessibilityFocusSearch(direction,
-                                        virtualDescendantId);
-                            } else {
-                                next = nextView.createAccessibilityNodeInfo();
-                             }
+                        next = provider.accessibilityFocusSearch(direction, virtualDescendantId);
+                        if (next != null) {
+                            return;
+                        }
+                    }
+                    View nextView = root.focusSearch(direction);
+                    while (nextView != null) {
+                        // If the focus search reached a node with a provider
+                        // we delegate to the provider to find the next one.
+                        // If the provider does not return a virtual view to
+                        // take accessibility focus we try the next view found
+                        // by the focus search algorithm.
+                        provider = nextView.getAccessibilityNodeProvider();
+                        if (provider != null) {
+                            next = provider.accessibilityFocusSearch(direction, View.NO_ID);
+                            if (next != null) {
+                                break;
+                            }
+                            nextView = nextView.focusSearch(direction);
+                        } else {
+                            next = nextView.createAccessibilityNodeInfo();
+                            break;
                         }
                     }
                 } else {
@@ -615,6 +622,8 @@
 
         private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
 
+        private final ArrayList<View> mTempViewList = new ArrayList<View>();
+
         public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
                 List<AccessibilityNodeInfo> outInfos) {
             AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
@@ -655,8 +664,6 @@
             while (parent instanceof View
                     && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
                 View parentView = (View) parent;
-                final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
-                        parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
                 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
                 if (info != null) {
                     outInfos.add(info);
@@ -670,19 +677,21 @@
             ViewParent parent = current.getParentForAccessibility();
             if (parent instanceof ViewGroup) {
                 ViewGroup parentGroup = (ViewGroup) parent;
-                ChildListForAccessibility children = ChildListForAccessibility.obtain(parentGroup,
-                        false);
+                ArrayList<View> children = mTempViewList;
+                children.clear();
                 try {
-                    final int childCount = children.getChildCount();
+                    parentGroup.addChildrenForAccessibility(children);
+                    final int childCount = children.size();
                     for (int i = 0; i < childCount; i++) {
                         if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
                             return;
                         }
-                        View child = children.getChildAt(i);
+                        View child = children.get(i);
                         if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
                                 &&  isShown(child)) {
                             AccessibilityNodeInfo info = null;
-                            AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+                            AccessibilityNodeProvider provider =
+                                child.getAccessibilityNodeProvider();
                             if (provider == null) {
                                 info = child.createAccessibilityNodeInfo();
                             } else {
@@ -695,7 +704,7 @@
                         }
                     }
                 } finally {
-                    children.recycle();
+                    children.clear();
                 }
             }
         }
@@ -708,14 +717,16 @@
             ViewGroup rootGroup = (ViewGroup) root;
             HashMap<View, AccessibilityNodeInfo> addedChildren =
                 new HashMap<View, AccessibilityNodeInfo>();
-            ChildListForAccessibility children = ChildListForAccessibility.obtain(rootGroup, false);
+            ArrayList<View> children = mTempViewList;
+            children.clear();
             try {
-                final int childCount = children.getChildCount();
+                root.addChildrenForAccessibility(children);
+                final int childCount = children.size();
                 for (int i = 0; i < childCount; i++) {
                     if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
                         return;
                     }
-                    View child = children.getChildAt(i);
+                    View child = children.get(i);
                     if (isShown(child)) {
                         AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
                         if (provider == null) {
@@ -735,7 +746,7 @@
                     }
                 }
             } finally {
-                children.recycle();
+                children.clear();
             }
             if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
                 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
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/Choreographer.java b/core/java/android/view/Choreographer.java
index 825f351..aaa081c 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -24,16 +24,46 @@
 import android.util.Log;
 
 /**
- * Coordinates animations and drawing for UI on a particular thread.
- *
- * This object is thread-safe.  Other threads can post callbacks to run at a later time
- * on the UI thread.
- *
- * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver}
- * can only be accessed from the UI thread so operations that touch the event receiver
- * are posted to the UI thread if needed.
- *
- * @hide
+ * Coordinates the timing of animations, input and drawing.
+ * <p>
+ * The choreographer receives timing pulses (such as vertical synchronization)
+ * from the display subsystem then schedules work to occur as part of rendering
+ * the next display frame.
+ * </p><p>
+ * Applications typically interact with the choreographer indirectly using
+ * higher level abstractions in the animation framework or the view hierarchy.
+ * Here are some examples of things you can do using the higher-level APIs.
+ * </p>
+ * <ul>
+ * <li>To post an animation to be processed on a regular time basis synchronized with
+ * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
+ * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
+ * frame, use {@link View#postOnAnimation}.</li>
+ * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
+ * frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
+ * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
+ * next display frame, use {@link View#postInvalidateOnAnimation()} or
+ * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
+ * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
+ * sync with display frame rendering, do nothing.  This already happens automatically.
+ * {@link View#onDraw} will be called at the appropriate time.</li>
+ * </ul>
+ * <p>
+ * However, there are a few cases where you might want to use the functions of the
+ * choreographer directly in your application.  Here are some examples.
+ * </p>
+ * <ul>
+ * <li>If your application does its rendering in a different thread, possibly using GL,
+ * or does not use the animation framework or view hierarchy at all
+ * and you want to ensure that it is appropriately synchronized with the display, then use
+ * {@link Choreographer#postFrameCallback}.</li>
+ * <li>... and that's about it.</li>
+ * </ul>
+ * <p>
+ * Each {@link Looper} thread has its own choreographer.  Other threads can
+ * post callbacks to run on the choreographer but they will run on the {@link Looper}
+ * to which the choreographer belongs.
+ * </p>
  */
 public final class Choreographer {
     private static final String TAG = "Choreographer";
@@ -79,13 +109,22 @@
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
 
+    // All frame callbacks posted by applications have this token.
+    private static final Object FRAME_CALLBACK_TOKEN = new Object() {
+        public String toString() { return "FRAME_CALLBACK_TOKEN"; }
+    };
+
     private final Object mLock = new Object();
 
     private final Looper mLooper;
     private final FrameHandler mHandler;
+
+    // The display event receiver can only be accessed by the looper thread to which
+    // it is attached.  We take care to ensure that we post message to the looper
+    // if appropriate when interacting with the display event receiver.
     private final FrameDisplayEventReceiver mDisplayEventReceiver;
 
-    private Callback mCallbackPool;
+    private CallbackRecord mCallbackPool;
 
     private final CallbackQueue[] mCallbackQueues;
 
@@ -96,17 +135,20 @@
 
     /**
      * Callback type: Input callback.  Runs first.
+     * @hide
      */
     public static final int CALLBACK_INPUT = 0;
 
     /**
      * Callback type: Animation callback.  Runs before traversals.
+     * @hide
      */
     public static final int CALLBACK_ANIMATION = 1;
 
     /**
      * Callback type: Traversal callback.  Handles layout and draw.  Runs last
      * after all other asynchronous messages have been handled.
+     * @hide
      */
     public static final int CALLBACK_TRAVERSAL = 2;
 
@@ -138,32 +180,38 @@
     }
 
     /**
-     * The amount of time, in milliseconds, between each frame of the animation. This is a
-     * requested time that the animation will attempt to honor, but the actual delay between
-     * frames may be different, depending on system load and capabilities. This is a static
+     * The amount of time, in milliseconds, between each frame of the animation.
+     * <p>
+     * This is a requested time that the animation will attempt to honor, but the actual delay
+     * between frames may be different, depending on system load and capabilities. This is a static
      * function because the same delay will be applied to all animations, since they are all
      * run off of a single timing loop.
-     *
+     * </p><p>
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
+     * </p>
      *
      * @return the requested time between frames, in milliseconds
+     * @hide
      */
     public static long getFrameDelay() {
         return sFrameDelay;
     }
 
     /**
-     * The amount of time, in milliseconds, between each frame of the animation. This is a
-     * requested time that the animation will attempt to honor, but the actual delay between
-     * frames may be different, depending on system load and capabilities. This is a static
+     * The amount of time, in milliseconds, between each frame of the animation.
+     * <p>
+     * This is a requested time that the animation will attempt to honor, but the actual delay
+     * between frames may be different, depending on system load and capabilities. This is a static
      * function because the same delay will be applied to all animations, since they are all
      * run off of a single timing loop.
-     *
+     * </p><p>
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
+     * </p>
      *
      * @param frameDelay the requested time between frames, in milliseconds
+     * @hide
      */
     public static void setFrameDelay(long frameDelay) {
         sFrameDelay = frameDelay;
@@ -171,23 +219,25 @@
 
     /**
      * Subtracts typical frame delay time from a delay interval in milliseconds.
-     *
+     * <p>
      * This method can be used to compensate for animation delay times that have baked
      * in assumptions about the frame delay.  For example, it's quite common for code to
      * assume a 60Hz frame time and bake in a 16ms delay.  When we call
      * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
      * posting the animation callback but let the animation timer take care of the remaining
      * frame delay time.
-     *
+     * </p><p>
      * This method is somewhat conservative about how much of the frame delay it
      * subtracts.  It uses the same value returned by {@link #getFrameDelay} which by
      * default is 10ms even though many parts of the system assume 16ms.  Consequently,
      * we might still wait 6ms before posting an animation callback that we want to run
      * on the next frame, but this is much better than waiting a whole 16ms and likely
      * missing the deadline.
+     * </p>
      *
      * @param delayMillis The original delay time including an assumed frame delay.
      * @return The adjusted delay time with the assumed frame delay subtracted out.
+     * @hide
      */
     public static long subtractFrameDelay(long delayMillis) {
         final long frameDelay = sFrameDelay;
@@ -196,21 +246,26 @@
 
     /**
      * Posts a callback to run on the next frame.
-     * The callback only runs once and then is automatically removed.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
      *
      * @param callbackType The callback type.
      * @param action The callback action to run during the next frame.
      * @param token The callback token, or null if none.
      *
      * @see #removeCallbacks
+     * @hide
      */
     public void postCallback(int callbackType, Runnable action, Object token) {
         postCallbackDelayed(callbackType, action, token, 0);
     }
 
     /**
-     * Posts a callback to run on the next frame following the specified delay.
-     * The callback only runs once and then is automatically removed.
+     * Posts a callback to run on the next frame after the specified delay.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
      *
      * @param callbackType The callback type.
      * @param action The callback action to run during the next frame after the specified delay.
@@ -218,6 +273,7 @@
      * @param delayMillis The delay time in milliseconds.
      *
      * @see #removeCallback
+     * @hide
      */
     public void postCallbackDelayed(int callbackType,
             Runnable action, Object token, long delayMillis) {
@@ -228,6 +284,11 @@
             throw new IllegalArgumentException("callbackType is invalid");
         }
 
+        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
+    }
+
+    private void postCallbackDelayedInternal(int callbackType,
+            Object action, Object token, long delayMillis) {
         if (DEBUG) {
             Log.d(TAG, "PostCallback: type=" + callbackType
                     + ", action=" + action + ", token=" + token
@@ -261,12 +322,17 @@
      *
      * @see #postCallback
      * @see #postCallbackDelayed
+     * @hide
      */
     public void removeCallbacks(int callbackType, Runnable action, Object token) {
         if (callbackType < 0 || callbackType > CALLBACK_LAST) {
             throw new IllegalArgumentException("callbackType is invalid");
         }
 
+        removeCallbacksInternal(callbackType, action, token);
+    }
+
+    private void removeCallbacksInternal(int callbackType, Object action, Object token) {
         if (DEBUG) {
             Log.d(TAG, "RemoveCallbacks: type=" + callbackType
                     + ", action=" + action + ", token=" + token);
@@ -281,17 +347,81 @@
     }
 
     /**
-     * Gets the time when the current frame started.  The frame time should be used
-     * instead of {@link SystemClock#uptimeMillis()} to synchronize animations.
-     * This helps to reduce inter-frame jitter because the frame time is fixed at the
-     * time the frame was scheduled to start, regardless of when the animations or
-     * drawing code actually ran.
+     * Posts a frame callback to run on the next frame.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
      *
+     * @param callback The frame callback to run during the next frame.
+     *
+     * @see #postFrameCallbackDelayed
+     * @see #removeFrameCallback
+     */
+    public void postFrameCallback(FrameCallback callback) {
+        postFrameCallbackDelayed(callback, 0);
+    }
+
+    /**
+     * Posts a frame callback to run on the next frame after the specified delay.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
+     *
+     * @param callback The frame callback to run during the next frame.
+     * @param delayMillis The delay time in milliseconds.
+     *
+     * @see #postFrameCallback
+     * @see #removeFrameCallback
+     */
+    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        postCallbackDelayedInternal(CALLBACK_ANIMATION,
+                callback, FRAME_CALLBACK_TOKEN, delayMillis);
+    }
+
+    /**
+     * Removes a previously posted frame callback.
+     *
+     * @param callback The frame callback to remove.
+     *
+     * @see #postFrameCallback
+     * @see #postFrameCallbackDelayed
+     */
+    public void removeFrameCallback(FrameCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN);
+    }
+
+    /**
+     * Gets the time when the current frame started.
+     * <p>
+     * This method provides the time in nanoseconds when the frame started being rendered.
+     * The frame time provides a stable time base for synchronizing animations
+     * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
+     * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
+     * time helps to reduce inter-frame jitter because the frame time is fixed at the time
+     * the frame was scheduled to start, regardless of when the animations or drawing
+     * callback actually runs.  All callbacks that run as part of rendering a frame will
+     * observe the same frame time so using the frame time also helps to synchronize effects
+     * that are performed by different callbacks.
+     * </p><p>
+     * Please note that the framework already takes care to process animations and
+     * drawing using the frame time as a stable time base.  Most applications should
+     * not need to use the frame time information directly.
+     * </p><p>
      * This method should only be called from within a callback.
+     * </p>
      *
      * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base.
      *
      * @throws IllegalStateException if no frame is in progress.
+     * @hide
      */
     public long getFrameTime() {
         return getFrameTimeNanos() / NANOS_PER_MS;
@@ -303,6 +433,7 @@
      * @return The frame start time, in the {@link System#nanoTime()} time base.
      *
      * @throws IllegalStateException if no frame is in progress.
+     * @hide
      */
     public long getFrameTimeNanos() {
         synchronized (mLock) {
@@ -345,7 +476,7 @@
         }
     }
 
-    void doFrame(long timestampNanos, int frame) {
+    void doFrame(long frameTimeNanos, int frame) {
         final long startNanos;
         synchronized (mLock) {
             if (!mFrameScheduled) {
@@ -353,7 +484,7 @@
             }
 
             startNanos = System.nanoTime();
-            final long jitterNanos = startNanos - timestampNanos;
+            final long jitterNanos = startNanos - frameTimeNanos;
             if (jitterNanos >= mFrameIntervalNanos) {
                 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                 if (DEBUG) {
@@ -363,10 +494,10 @@
                             + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                             + " ms in the past.");
                 }
-                timestampNanos = startNanos - lastFrameOffset;
+                frameTimeNanos = startNanos - lastFrameOffset;
             }
 
-            if (timestampNanos < mLastFrameTimeNanos) {
+            if (frameTimeNanos < mLastFrameTimeNanos) {
                 if (DEBUG) {
                     Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                             + "previously skipped frame.  Waiting for next vsync");
@@ -376,24 +507,27 @@
             }
 
             mFrameScheduled = false;
-            mLastFrameTimeNanos = timestampNanos;
+            mLastFrameTimeNanos = frameTimeNanos;
         }
 
-        doCallbacks(Choreographer.CALLBACK_INPUT);
-        doCallbacks(Choreographer.CALLBACK_ANIMATION);
-        doCallbacks(Choreographer.CALLBACK_TRAVERSAL);
+        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
+        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
 
         if (DEBUG) {
             final long endNanos = System.nanoTime();
             Log.d(TAG, "Frame " + frame + ": Finished, took "
                     + (endNanos - startNanos) * 0.000001f + " ms, latency "
-                    + (startNanos - timestampNanos) * 0.000001f + " ms.");
+                    + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
         }
     }
 
-    void doCallbacks(int callbackType) {
-        Callback callbacks;
+    void doCallbacks(int callbackType, long frameTimeNanos) {
+        CallbackRecord callbacks;
         synchronized (mLock) {
+            // We use "now" to determine when callbacks become due because it's possible
+            // for earlier processing phases in a frame to post callbacks that should run
+            // in a following phase, such as an input event that causes an animation to start.
             final long now = SystemClock.uptimeMillis();
             callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
             if (callbacks == null) {
@@ -402,19 +536,19 @@
             mCallbacksRunning = true;
         }
         try {
-            for (Callback c = callbacks; c != null; c = c.next) {
+            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                 if (DEBUG) {
                     Log.d(TAG, "RunCallback: type=" + callbackType
                             + ", action=" + c.action + ", token=" + c.token
                             + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                 }
-                c.action.run();
+                c.run(frameTimeNanos);
             }
         } finally {
             synchronized (mLock) {
                 mCallbacksRunning = false;
                 do {
-                    final Callback next = callbacks.next;
+                    final CallbackRecord next = callbacks.next;
                     recycleCallbackLocked(callbacks);
                     callbacks = next;
                 } while (callbacks != null);
@@ -449,10 +583,10 @@
         return Looper.myLooper() == mLooper;
     }
 
-    private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) {
-        Callback callback = mCallbackPool;
+    private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
+        CallbackRecord callback = mCallbackPool;
         if (callback == null) {
-            callback = new Callback();
+            callback = new CallbackRecord();
         } else {
             mCallbackPool = callback.next;
             callback.next = null;
@@ -463,13 +597,44 @@
         return callback;
     }
 
-    private void recycleCallbackLocked(Callback callback) {
+    private void recycleCallbackLocked(CallbackRecord callback) {
         callback.action = null;
         callback.token = null;
         callback.next = mCallbackPool;
         mCallbackPool = callback;
     }
 
+    /**
+     * Implement this interface to receive a callback when a new display frame is
+     * being rendered.  The callback is invoked on the {@link Looper} thread to
+     * which the {@link Choreographer} is attached.
+     */
+    public interface FrameCallback {
+        /**
+         * Called when a new display frame is being rendered.
+         * <p>
+         * This method provides the time in nanoseconds when the frame started being rendered.
+         * The frame time provides a stable time base for synchronizing animations
+         * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
+         * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
+         * time helps to reduce inter-frame jitter because the frame time is fixed at the time
+         * the frame was scheduled to start, regardless of when the animations or drawing
+         * callback actually runs.  All callbacks that run as part of rendering a frame will
+         * observe the same frame time so using the frame time also helps to synchronize effects
+         * that are performed by different callbacks.
+         * </p><p>
+         * Please note that the framework already takes care to process animations and
+         * drawing using the frame time as a stable time base.  Most applications should
+         * not need to use the frame time information directly.
+         * </p>
+         *
+         * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
+         * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
+         * to convert it to the {@link SystemClock#uptimeMillis()} time base.
+         */
+        public void doFrame(long frameTimeNanos);
+    }
+
     private final class FrameHandler extends Handler {
         public FrameHandler(Looper looper) {
             super(looper);
@@ -491,39 +656,65 @@
         }
     }
 
-    private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
+    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
+            implements Runnable {
+        private long mTimestampNanos;
+        private int mFrame;
+
         public FrameDisplayEventReceiver(Looper looper) {
             super(looper);
         }
 
         @Override
         public void onVsync(long timestampNanos, int frame) {
-            doFrame(timestampNanos, frame);
+            // Post the vsync event to the Handler.
+            // The idea is to prevent incoming vsync events from completely starving
+            // the message queue.  If there are no messages in the queue with timestamps
+            // earlier than the frame time, then the vsync event will be processed immediately.
+            // Otherwise, messages that predate the vsync event will be handled first.
+            mTimestampNanos = timestampNanos;
+            mFrame = frame;
+            Message msg = Message.obtain(mHandler, this);
+            msg.setAsynchronous(true);
+            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
+        }
+
+        @Override
+        public void run() {
+            doFrame(mTimestampNanos, mFrame);
         }
     }
 
-    private static final class Callback {
-        public Callback next;
+    private static final class CallbackRecord {
+        public CallbackRecord next;
         public long dueTime;
-        public Runnable action;
+        public Object action; // Runnable or FrameCallback
         public Object token;
+
+        public void run(long frameTimeNanos) {
+            if (token == FRAME_CALLBACK_TOKEN) {
+                ((FrameCallback)action).doFrame(frameTimeNanos);
+            } else {
+                ((Runnable)action).run();
+            }
+        }
     }
 
     private final class CallbackQueue {
-        private Callback mHead;
+        private CallbackRecord mHead;
 
         public boolean hasDueCallbacksLocked(long now) {
             return mHead != null && mHead.dueTime <= now;
         }
 
-        public Callback extractDueCallbacksLocked(long now) {
-            Callback callbacks = mHead;
+        public CallbackRecord extractDueCallbacksLocked(long now) {
+            CallbackRecord callbacks = mHead;
             if (callbacks == null || callbacks.dueTime > now) {
                 return null;
             }
 
-            Callback last = callbacks;
-            Callback next = last.next;
+            CallbackRecord last = callbacks;
+            CallbackRecord next = last.next;
             while (next != null) {
                 if (next.dueTime > now) {
                     last.next = null;
@@ -536,9 +727,9 @@
             return callbacks;
         }
 
-        public void addCallbackLocked(long dueTime, Runnable action, Object token) {
-            Callback callback = obtainCallbackLocked(dueTime, action, token);
-            Callback entry = mHead;
+        public void addCallbackLocked(long dueTime, Object action, Object token) {
+            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
+            CallbackRecord entry = mHead;
             if (entry == null) {
                 mHead = callback;
                 return;
@@ -558,10 +749,10 @@
             entry.next = callback;
         }
 
-        public void removeCallbacksLocked(Runnable action, Object token) {
-            Callback predecessor = null;
-            for (Callback callback = mHead; callback != null;) {
-                final Callback next = callback.next;
+        public void removeCallbacksLocked(Object action, Object token) {
+            CallbackRecord predecessor = null;
+            for (CallbackRecord callback = mHead; callback != null;) {
+                final CallbackRecord next = callback.next;
                 if ((action == null || callback.action == action)
                         && (token == null || callback.token == token)) {
                     if (predecessor != null) {
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 25d08ac..0114a41 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -585,8 +585,12 @@
             }
             // Hold the event we obtained above - listeners may have changed the original.
             mPreviousUpEvent = currentUpEvent;
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
+            if (mVelocityTracker != null) {
+                // This may have been cleared when we called out to the
+                // application above.
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
+            }
             mIsDoubleTapping = false;
             mHandler.removeMessages(SHOW_PRESS);
             mHandler.removeMessages(LONG_PRESS);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e25e2ef..f986d15 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -647,15 +647,10 @@
                 Log.d(LOG_TAG, "Disabling v-sync");
             }
 
-            //noinspection PointlessBooleanExpression,ConstantConditions
-            if (!ViewDebug.DEBUG_LATENCY) {
-                property = SystemProperties.get(PROFILE_PROPERTY, "false");
-                mProfileEnabled = "true".equalsIgnoreCase(property);
-                if (mProfileEnabled) {
-                    Log.d(LOG_TAG, "Profiling hardware renderer");
-                }
-            } else {
-                mProfileEnabled = true;
+            property = SystemProperties.get(PROFILE_PROPERTY, "false");
+            mProfileEnabled = "true".equalsIgnoreCase(property);
+            if (mProfileEnabled) {
+                Log.d(LOG_TAG, "Profiling hardware renderer");
             }
 
             if (mProfileEnabled) {
@@ -1132,11 +1127,6 @@
                             float total = (now - getDisplayListStartTime) * 0.000001f;
                             //noinspection PointlessArithmeticExpression
                             mProfileData[mProfileCurrentFrame] = total;
-
-                            if (ViewDebug.DEBUG_LATENCY) {
-                                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " +
-                                        total + "ms");
-                            }
                         }
 
                         if (displayList != null) {
@@ -1152,11 +1142,6 @@
                                 long now = System.nanoTime();
                                 float total = (now - drawDisplayListStartTime) * 0.000001f;
                                 mProfileData[mProfileCurrentFrame + 1] = total;
-
-                                if (ViewDebug.DEBUG_LATENCY) {
-                                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
-                                            total + "ms, status=" + status);
-                                }
                             }
 
                             handleFunctorStatus(attachInfo, status);
@@ -1198,11 +1183,6 @@
                         long now = System.nanoTime();
                         float total = (now - eglSwapBuffersStartTime) * 0.000001f;
                         mProfileData[mProfileCurrentFrame + 2] = total;
-
-                        if (ViewDebug.DEBUG_LATENCY) {
-                            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " +
-                                    total + "ms");
-                        }
                     }
 
                     checkEglErrors();
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index c46e43a..a7ee12b 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -65,6 +65,7 @@
     private final Object[] mActionProviderConstructorArguments;
 
     private Context mContext;
+    private Object mRealOwner;
 
     /**
      * Constructs a menu inflater.
@@ -73,6 +74,20 @@
      */
     public MenuInflater(Context context) {
         mContext = context;
+        mRealOwner = context;
+        mActionViewConstructorArguments = new Object[] {context};
+        mActionProviderConstructorArguments = mActionViewConstructorArguments;
+    }
+
+    /**
+     * Constructs a menu inflater.
+     *
+     * @see Activity#getMenuInflater()
+     * @hide
+     */
+    public MenuInflater(Context context, Object realOwner) {
+        mContext = context;
+        mRealOwner = realOwner;
         mActionViewConstructorArguments = new Object[] {context};
         mActionProviderConstructorArguments = mActionViewConstructorArguments;
     }
@@ -190,12 +205,12 @@
             implements MenuItem.OnMenuItemClickListener {
         private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
         
-        private Context mContext;
+        private Object mRealOwner;
         private Method mMethod;
         
-        public InflatedOnMenuItemClickListener(Context context, String methodName) {
-            mContext = context;
-            Class<?> c = context.getClass();
+        public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
+            mRealOwner = realOwner;
+            Class<?> c = realOwner.getClass();
             try {
                 mMethod = c.getMethod(methodName, PARAM_TYPES);
             } catch (Exception e) {
@@ -210,9 +225,9 @@
         public boolean onMenuItemClick(MenuItem item) {
             try {
                 if (mMethod.getReturnType() == Boolean.TYPE) {
-                    return (Boolean) mMethod.invoke(mContext, item);
+                    return (Boolean) mMethod.invoke(mRealOwner, item);
                 } else {
-                    mMethod.invoke(mContext, item);
+                    mMethod.invoke(mRealOwner, item);
                     return true;
                 }
             } catch (Exception e) {
@@ -400,7 +415,7 @@
                             + "be used within a restricted context");
                 }
                 item.setOnMenuItemClickListener(
-                        new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
+                        new InflatedOnMenuItemClickListener(mRealOwner, itemListenerMethodName));
             }
 
             if (item instanceof MenuItemImpl) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 2048de2..a719a01 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);
@@ -368,6 +374,14 @@
             // tell mLayer about it and set the SurfaceTexture to use the
             // current view size.
             mUpdateSurface = false;
+
+            // Since we are updating the layer, force an update to ensure its
+            // parameters are correct (width, height, transform, etc.)
+            synchronized (mLock) {
+                mUpdateLayer = true;
+            }
+            mMatrixChanged = true;
+
             mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
             nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
         }
@@ -465,7 +479,7 @@
     }
 
     private void applyTransformMatrix() {
-        if (mMatrixChanged) {
+        if (mMatrixChanged && mLayer != null) {
             mLayer.setTransform(mMatrix);
             mMatrixChanged = false;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 14523d3..a74a0ef 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2255,9 +2255,27 @@
      * flags, we would like a stable view of the content insets given to
      * {@link #fitSystemWindows(Rect)}.  This means that the insets seen there
      * will always represent the worst case that the application can expect
-     * as a continue state.  In practice this means with any of system bar,
-     * nav bar, and status bar shown, but not the space that would be needed
-     * for an input method.
+     * as a continuous state.  In the stock Android UI this is the space for
+     * the system bar, nav bar, and status bar, but not more transient elements
+     * such as an input method.
+     *
+     * The stable layout your UI sees is based on the system UI modes you can
+     * switch to.  That is, if you specify {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}
+     * then you will get a stable layout for changes of the
+     * {@link #SYSTEM_UI_FLAG_FULLSCREEN} mode; if you specify
+     * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} and
+     * {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}, then you can transition
+     * to {@link #SYSTEM_UI_FLAG_FULLSCREEN} and {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}
+     * with a stable layout.  (Note that you should avoid using
+     * {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} by itself.)
+     *
+     * If you have set the window flag {@ WindowManager.LayoutParams#FLAG_FULLSCREEN}
+     * to hide the status bar (instead of using {@link #SYSTEM_UI_FLAG_FULLSCREEN}),
+     * then a hidden status bar will be considered a "stable" state for purposes
+     * here.  This allows your UI to continually hide the status bar, while still
+     * using the system UI flags to hide the action bar while still retaining
+     * a stable layout.  Note that changing the window fullscreen flag will never
+     * provide a stable layout for a clean transition.
      *
      * <p>If you are using ActionBar in
      * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
@@ -4307,7 +4325,6 @@
         if (gainFocus) {
             if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                requestAccessibilityFocus();
             }
         }
 
@@ -4776,11 +4793,11 @@
             info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
         }
 
-        if (isClickable()) {
+        if (isClickable() && isEnabled()) {
             info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
         }
 
-        if (isLongClickable()) {
+        if (isLongClickable() && isEnabled()) {
             info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
         }
 
@@ -5237,12 +5254,25 @@
      * which you would like to ensure are not being covered.
      *
      * <p>The default implementation of this method simply applies the content
-     * inset's to the view's padding.  This can be enabled through
-     * {@link #setFitsSystemWindows(boolean)}.  Alternatively, you can override
-     * the method and handle the insets however you would like.  Note that the
-     * insets provided by the framework are always relative to the far edges
-     * of the window, not accounting for the location of the called view within
-     * that window.  (In fact when this method is called you do not yet know
+     * inset's to the view's padding, consuming that content (modifying the
+     * insets to be 0), and returning true.  This behavior is off by default, but can
+     * be enabled through {@link #setFitsSystemWindows(boolean)}.
+     *
+     * <p>This function's traversal down the hierarchy is depth-first.  The same content
+     * insets object is propagated down the hierarchy, so any changes made to it will
+     * be seen by all following views (including potentially ones above in
+     * the hierarchy since this is a depth-first traversal).  The first view
+     * that returns true will abort the entire traversal.
+     *
+     * <p>The default implementation works well for a situation where it is
+     * used with a container that covers the entire window, allowing it to
+     * apply the appropriate insets to its content on all edges.  If you need
+     * a more complicated layout (such as two different views fitting system
+     * windows, one on the top of the window, and one on the bottom),
+     * you can override the method and handle the insets however you would like.
+     * Note that the insets provided by the framework are always relative to the
+     * far edges of the window, not accounting for the location of the called view
+     * within that window.  (In fact when this method is called you do not yet know
      * where the layout will place the view, as it is done before layout happens.)
      *
      * <p>Note: unlike many View methods, there is no dispatch phase to this
@@ -5263,6 +5293,9 @@
      *
      * @return Return true if this view applied the insets and it should not
      * continue propagating further down the hierarchy, false otherwise.
+     * @see #getFitsSystemWindows()
+     * @see #setFitsSystemWindows()
+     * @see #setSystemUiVisibility(int)
      */
     protected boolean fitSystemWindows(Rect insets) {
         if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
@@ -5283,16 +5316,23 @@
     }
 
     /**
-     * Set whether or not this view should account for system screen decorations
-     * such as the status bar and inset its content. This allows this view to be
-     * positioned in absolute screen coordinates and remain visible to the user.
+     * Sets whether or not this view should account for system screen decorations
+     * such as the status bar and inset its content; that is, controlling whether
+     * the default implementation of {@link #fitSystemWindows(Rect)} will be
+     * executed.  See that method for more details.
      *
-     * <p>This should only be used by top-level window decor views.
+     * <p>Note that if you are providing your own implementation of
+     * {@link #fitSystemWindows(Rect)}, then there is no need to set this
+     * flag to true -- your implementation will be overriding the default
+     * implementation that checks this flag.
      *
-     * @param fitSystemWindows true to inset content for system screen decorations, false for
-     *                         default behavior.
+     * @param fitSystemWindows If true, then the default implementation of
+     * {@link #fitSystemWindows(Rect)} will be executed.
      *
      * @attr ref android.R.styleable#View_fitsSystemWindows
+     * @see #getFitsSystemWindows()
+     * @see #fitSystemWindows(Rect)
+     * @see #setSystemUiVisibility(int)
      */
     public void setFitsSystemWindows(boolean fitSystemWindows) {
         setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS : 0, FITS_SYSTEM_WINDOWS);
@@ -5300,14 +5340,16 @@
 
     /**
      * Check for state of {@link #setFitsSystemWindows(boolean). If this method
-     * returns true, this view
-     * will account for system screen decorations such as the status bar and inset its
-     * content. This allows the view to be positioned in absolute screen coordinates
-     * and remain visible to the user.
+     * returns true, the default implementation of {@link #fitSystemWindows(Rect)}
+     * will be executed.
      *
-     * @return true if this view will adjust its content bounds for system screen decorations.
+     * @return Returns true if the default implementation of
+     * {@link #fitSystemWindows(Rect)} will be executed.
      *
      * @attr ref android.R.styleable#View_fitsSystemWindows
+     * @see #setFitsSystemWindows()
+     * @see #fitSystemWindows(Rect)
+     * @see #setSystemUiVisibility(int)
      */
     public boolean getFitsSystemWindows() {
         return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
@@ -6027,7 +6069,7 @@
             return;
         }
         if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
-            if (canTakeAccessibilityFocusFromHover()) {
+            if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
                 views.add(this);
                 return;
             }
@@ -6140,8 +6182,6 @@
             invalidate();
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
             notifyAccessibilityStateChanged();
-            // Try to give input focus to this view - not a descendant.
-            requestFocusNoSearch(View.FOCUS_DOWN, null);
             return true;
         }
         return false;
@@ -6158,24 +6198,19 @@
     public void clearAccessibilityFocus() {
         if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
-            ViewRootImpl viewRootImpl = getViewRootImpl();
-            if (viewRootImpl != null) {
-                viewRootImpl.setAccessibilityFocusedHost(null);
-            }
             invalidate();
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
             notifyAccessibilityStateChanged();
-
             // Clear the text navigation state.
             setAccessibilityCursorPosition(-1);
-
-            // Try to move accessibility focus to the input focus.
-            View rootView = getRootView();
-            if (rootView != null) {
-                View inputFocus = rootView.findFocus();
-                if (inputFocus != null) {
-                    inputFocus.requestAccessibilityFocus();
-                }
+        }
+        // Clear the global reference of accessibility focus if this
+        // view or any of its descendants had accessibility focus.
+        ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl != null) {
+            View focusHost = viewRootImpl.getAccessibilityFocusedHost();
+            if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
+                viewRootImpl.setAccessibilityFocusedHost(null);
             }
         }
     }
@@ -6183,11 +6218,13 @@
     private void requestAccessibilityFocusFromHover() {
         if (includeForAccessibility() && isActionableForAccessibility()) {
             requestAccessibilityFocus();
+            requestFocusNoSearch(View.FOCUS_DOWN, null);
         } else {
             if (mParent != null) {
                 View nextFocus = mParent.findViewToTakeAccessibilityFocusFromHover(this, this);
                 if (nextFocus != null) {
                     nextFocus.requestAccessibilityFocus();
+                    nextFocus.requestFocusNoSearch(View.FOCUS_DOWN, null);
                 }
             }
         }
@@ -6634,7 +6671,7 @@
 
     private boolean nextAtGranularity(int granularity) {
         CharSequence text = getIterableTextForAccessibility();
-        if (text != null && text.length() > 0) {
+        if (text == null || text.length() == 0) {
             return false;
         }
         TextSegmentIterator iterator = getIteratorForGranularity(granularity);
@@ -6658,7 +6695,7 @@
 
     private boolean previousAtGranularity(int granularity) {
         CharSequence text = getIterableTextForAccessibility();
-        if (text != null && text.length() > 0) {
+        if (text == null || text.length() == 0) {
             return false;
         }
         TextSegmentIterator iterator = getIteratorForGranularity(granularity);
@@ -9865,10 +9902,6 @@
      * @param dirty the rectangle representing the bounds of the dirty region
      */
     public void invalidate(Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -9912,10 +9945,6 @@
      * @param b the bottom position of the dirty region
      */
     public void invalidate(int l, int t, int r, int b) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -9968,10 +9997,6 @@
      * View's contents or dimensions have not changed.
      */
     void invalidate(boolean invalidateCache) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
-        }
-
         if (skipInvalidate()) {
             return;
         }
@@ -12344,10 +12369,6 @@
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
             mCachingFailed = false;
 
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
-            }
-
             int width = mRight - mLeft;
             int height = mBottom - mTop;
 
@@ -12467,9 +12488,6 @@
 
             // Fast path for layouts with no backgrounds
             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                if (ViewDebug.TRACE_HIERARCHY) {
-                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
-                }
                 mPrivateFlags &= ~DIRTY_MASK;
                 dispatchDraw(canvas);
             } else {
@@ -13083,9 +13101,6 @@
                 if (!hasDisplayList) {
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                        if (ViewDebug.TRACE_HIERARCHY) {
-                            ViewDebug.trace(parent, ViewDebug.HierarchyTraceType.DRAW);
-                        }
                         mPrivateFlags &= ~DIRTY_MASK;
                         dispatchDraw(canvas);
                     } else {
@@ -13158,10 +13173,6 @@
      * @param canvas The Canvas to which the View is rendered.
      */
     public void draw(Canvas canvas) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
-        }
-
         final int privateFlags = mPrivateFlags;
         final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
                 (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
@@ -13505,10 +13516,6 @@
         int oldR = mRight;
         boolean changed = setFrame(l, t, r, b);
         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
-            }
-
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~LAYOUT_REQUIRED;
 
@@ -14764,60 +14771,6 @@
     }
 
     /**
-     * @param consistency The type of consistency. See ViewDebug for more information.
-     *
-     * @hide
-     */
-    protected boolean dispatchConsistencyCheck(int consistency) {
-        return onConsistencyCheck(consistency);
-    }
-
-    /**
-     * Method that subclasses should implement to check their consistency. The type of
-     * consistency check is indicated by the bit field passed as a parameter.
-     *
-     * @param consistency The type of consistency. See ViewDebug for more information.
-     *
-     * @throws IllegalStateException if the view is in an inconsistent state.
-     *
-     * @hide
-     */
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = true;
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
-
-        if (checkLayout) {
-            if (getParent() == null) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " does not have a parent.");
-            }
-
-            if (mAttachInfo == null) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " is not attached to a window.");
-            }
-        }
-
-        if (checkDrawing) {
-            // Do not check the DIRTY/DRAWN flags because views can call invalidate()
-            // from their draw() method
-
-            if ((mPrivateFlags & DRAWN) != DRAWN &&
-                    (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
-                result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                        "View " + this + " was invalidated but its drawing cache is valid.");
-            }
-        }
-
-        return result;
-    }
-
-    /**
      * Prints information about this view in the log output, with the tag
      * {@link #VIEW_LOG_TAG}.
      *
@@ -14930,10 +14883,6 @@
      * tree.
      */
     public void requestLayout() {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
-        }
-
         mPrivateFlags |= FORCE_LAYOUT;
         mPrivateFlags |= INVALIDATED;
 
@@ -14984,10 +14933,6 @@
             // first clears the measured dimension flag
             mPrivateFlags &= ~MEASURED_DIMENSION_SET;
 
-            if (ViewDebug.TRACE_HIERARCHY) {
-                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
-            }
-
             // measure ourselves, this should set the measured dimension flag back
             onMeasure(widthMeasureSpec, heightMeasureSpec);
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9134966..823befb 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -193,19 +193,12 @@
     private static final int MAXIMUM_FLING_VELOCITY = 8000;
 
     /**
-     * Distance in dips between a touch up event denoting the end of a touch exploration
-     * gesture and the touch up event of a subsequent tap for the latter tap to be
-     * considered as a tap i.e. to perform a click.
-     */
-    private static final int TOUCH_EXPLORE_TAP_SLOP = 80;
-
-    /**
      * Delay before dispatching a recurring accessibility event in milliseconds.
      * This delay guarantees that a recurring event will be send at most once
      * during the {@link #SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS} time
      * frame.
      */
-    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 400;
+    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
 
     /**
      * The maximum size of View's drawing cache, expressed in bytes. This size
@@ -238,7 +231,6 @@
     private final int mDoubleTapTouchSlop;
     private final int mPagingTouchSlop;
     private final int mDoubleTapSlop;
-    private final int mScaledTouchExploreTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
@@ -265,7 +257,6 @@
         mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
         mDoubleTapSlop = DOUBLE_TAP_SLOP;
-        mScaledTouchExploreTapSlop = TOUCH_EXPLORE_TAP_SLOP;
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -302,7 +293,6 @@
         mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
         mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
-        mScaledTouchExploreTapSlop = (int) (density * TOUCH_EXPLORE_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
         final Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -553,17 +543,6 @@
     }
 
     /**
-     * @return Distance in pixels between a touch up event denoting the end of a touch exploration
-     * gesture and the touch up event of a subsequent tap for the latter tap to be
-     * considered as a tap i.e. to perform a click.
-     *
-     * @hide
-     */
-    public int getScaledTouchExploreTapSlop() {
-        return mScaledTouchExploreTapSlop;
-    }
-
-    /**
      * Interval for dispatching a recurring accessibility event in milliseconds.
      * This interval guarantees that a recurring event will be send at most once
      * during the {@link #getSendRecurringAccessibilityEventsInterval()} time frame.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index cb37a1c..dd671dc 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -22,24 +22,14 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.Environment;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.Printer;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -51,14 +41,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -67,107 +51,24 @@
  */
 public class ViewDebug {
     /**
-     * Log tag used to log errors related to the consistency of the view hierarchy.
-     *
-     * @hide
+     * @deprecated This flag is now unused
      */
-    public static final String CONSISTENCY_LOG_TAG = "ViewConsistency";
-
-    /**
-     * Flag indicating the consistency check should check layout-related properties.
-     *
-     * @hide
-     */
-    public static final int CONSISTENCY_LAYOUT = 0x1;
-
-    /**
-     * Flag indicating the consistency check should check drawing-related properties.
-     *
-     * @hide
-     */
-    public static final int CONSISTENCY_DRAWING = 0x2;
-
-    /**
-     * Enables or disables view hierarchy tracing. Any invoker of
-     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
-     * check that this value is set to true as not to affect performance.
-     */
+    @Deprecated
     public static final boolean TRACE_HIERARCHY = false;
 
     /**
-     * Enables or disables view recycler tracing. Any invoker of
-     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
-     * check that this value is set to true as not to affect performance.
+     * @deprecated This flag is now unused
      */
+    @Deprecated
     public static final boolean TRACE_RECYCLER = false;
 
     /**
-     * Profiles drawing times in the events log.
-     *
-     * @hide
-     */
-    public static final boolean DEBUG_PROFILE_DRAWING = false;
-
-    /**
-     * Profiles layout times in the events log.
-     *
-     * @hide
-     */
-    public static final boolean DEBUG_PROFILE_LAYOUT = false;
-
-    /**
      * Enables detailed logging of drag/drop operations.
      * @hide
      */
     public static final boolean DEBUG_DRAG = false;
 
     /**
-     * Enables logging of factors that affect the latency and responsiveness of an application.
-     *
-     * Logs the relative difference between the time an event was created and the time it
-     * was delivered.
-     *
-     * Logs the time spent waiting for Surface.lockCanvas(), Surface.unlockCanvasAndPost()
-     * or eglSwapBuffers().  This is time that the event loop spends blocked and unresponsive.
-     * Ideally, drawing and animations should be perfectly synchronized with VSYNC so that
-     * dequeuing and queueing buffers is instantaneous.
-     *
-     * Logs the time spent in ViewRoot.performTraversals() and ViewRoot.performDraw().
-     * @hide
-     */
-    public static final boolean DEBUG_LATENCY = false;
-
-    /** @hide */
-    public static final String DEBUG_LATENCY_TAG = "ViewLatency";
-
-    /**
-     * Enables detailed logging of accessibility focus operations.
-     * @hide
-     */
-    public static final boolean DEBUG_ACCESSIBILITY_FOCUS = false;
-
-    /**
-     * Tag for logging of accessibility focus operations
-     * @hide
-     */
-    public static final String DEBUG_ACCESSIBILITY_FOCUS_TAG = "AccessibilityFocus";
-
-    /**
-     * <p>Enables or disables views consistency check. Even when this property is enabled,
-     * view consistency checks happen only if {@link false} is set
-     * to true. The value of this property can be configured externally in one of the
-     * following files:</p>
-     * <ul>
-     *  <li>/system/debug.prop</li>
-     *  <li>/debug.prop</li>
-     *  <li>/data/debug.prop</li>
-     * </ul>
-     * @hide
-     */
-    @Debug.DebugProperty
-    public static boolean consistencyCheckEnabled = false;
-
-    /**
      * This annotation can be used to mark fields and methods to be dumped by
      * the view server. Only non-void methods with no arguments can be annotated
      * by this annotation.
@@ -373,8 +274,9 @@
     private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
 
     /**
-     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
+     * @deprecated This enum is now unused
      */
+    @Deprecated
     public enum HierarchyTraceType {
         INVALIDATE,
         INVALIDATE_CHILD,
@@ -386,13 +288,10 @@
         BUILD_CACHE
     }
 
-    private static BufferedWriter sHierarchyTraces;
-    private static ViewRootImpl sHierarchyRoot;
-    private static String sHierarchyTracePrefix;
-
     /**
-     * Defines the type of recycler trace to output to the recycler traces file.
+     * @deprecated This enum is now unused
      */
+    @Deprecated
     public enum RecyclerTraceType {
         NEW_VIEW,
         BIND_VIEW,
@@ -402,21 +301,6 @@
         MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
     }
 
-    private static class RecyclerTrace {
-        public int view;
-        public RecyclerTraceType type;
-        public int position;
-        public int indexOnScreen;
-    }
-
-    private static View sRecyclerOwnerView;
-    private static List<View> sRecyclerViews;
-    private static List<RecyclerTrace> sRecyclerTraces;
-    private static String sRecyclerTracePrefix;
-
-    private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage =
-            new ThreadLocal<LooperProfiler>();
-
     /**
      * Returns the number of instanciated Views.
      *
@@ -440,511 +324,50 @@
     }
 
     /**
-     * Starts profiling the looper associated with the current thread.
-     * You must call {@link #stopLooperProfiling} to end profiling
-     * and obtain the traces. Both methods must be invoked on the
-     * same thread.
-     * 
-     * @hide
+     * @deprecated This method is now unused and invoking it is a no-op
      */
-    public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) {
-        if (sLooperProfilerStorage.get() == null) {
-            LooperProfiler profiler = new LooperProfiler(path, fileDescriptor);
-            sLooperProfilerStorage.set(profiler);
-            Looper.myLooper().setMessageLogging(profiler);
-        }
-    }    
-
-    /**
-     * Stops profiling the looper associated with the current thread.
-     * 
-     * @see #startLooperProfiling(String, java.io.FileDescriptor) 
-     * 
-     * @hide
-     */
-    public static void stopLooperProfiling() {
-        LooperProfiler profiler = sLooperProfilerStorage.get();
-        if (profiler != null) {
-            sLooperProfilerStorage.remove();
-            Looper.myLooper().setMessageLogging(null);
-            profiler.save();
-        }
-    }
-
-    private static class LooperProfiler implements Looper.Profiler, Printer {
-        private static final String LOG_TAG = "LooperProfiler";
-
-        private static final int TRACE_VERSION_NUMBER = 3;
-        private static final int ACTION_EXIT_METHOD = 0x1;
-        private static final int HEADER_SIZE = 32;
-        private static final String HEADER_MAGIC = "SLOW";
-        private static final short HEADER_RECORD_SIZE = (short) 14;
-
-        private final long mTraceWallStart;
-        private final long mTraceThreadStart;
-        
-        private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
-
-        private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32);
-        private int mTraceId = 0;
-
-        private final String mPath;
-        private ParcelFileDescriptor mFileDescriptor;
-
-        LooperProfiler(String path, FileDescriptor fileDescriptor) {
-            mPath = path;
-            try {
-                mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor);
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                throw new RuntimeException(e);
-            }
-            mTraceWallStart = SystemClock.currentTimeMicro();
-            mTraceThreadStart = SystemClock.currentThreadTimeMicro();            
-        }
-
-        @Override
-        public void println(String x) {
-            // Ignore messages
-        }
-
-        @Override
-        public void profile(Message message, long wallStart, long wallTime,
-                long threadStart, long threadTime) {
-            Entry entry = new Entry();
-            entry.traceId = getTraceId(message);
-            entry.wallStart = wallStart;
-            entry.wallTime = wallTime;
-            entry.threadStart = threadStart;
-            entry.threadTime = threadTime;
-
-            mTraces.add(entry);
-        }
-
-        private int getTraceId(Message message) {
-            String name = message.getTarget().getMessageName(message);
-            Integer traceId = mTraceNames.get(name);
-            if (traceId == null) {
-                traceId = mTraceId++ << 4;
-                mTraceNames.put(name, traceId);
-            }
-            return traceId;
-        }
-
-        void save() {
-            // Don't block the UI thread
-            new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    saveTraces();
-                }
-            }, "LooperProfiler[" + mPath + "]").start();
-        }
-
-        private void saveTraces() {
-            FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor());
-            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
-            try {
-                writeHeader(out, mTraceWallStart, mTraceNames, mTraces);
-                out.flush();
-
-                writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces);
-
-                Log.d(LOG_TAG, "Looper traces ready: " + mPath);
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-            } finally {
-                try {
-                    out.close();
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                }
-                try {
-                    mFileDescriptor.close();
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
-                }
-            }
-        }
-        
-        private static void writeTraces(FileOutputStream out, long offset, long wallStart,
-                long threadStart, ArrayList<Entry> entries) throws IOException {
-    
-            FileChannel channel = out.getChannel();
-    
-            // Header
-            ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE);
-            buffer.put(HEADER_MAGIC.getBytes());
-            buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
-            buffer.putShort((short) TRACE_VERSION_NUMBER);    // version
-            buffer.putShort((short) HEADER_SIZE);             // offset to data
-            buffer.putLong(wallStart);                        // start time in usec
-            buffer.putShort(HEADER_RECORD_SIZE);              // size of a record in bytes
-            // padding to 32 bytes
-            for (int i = 0; i < HEADER_SIZE - 18; i++) {
-                buffer.put((byte) 0);
-            }
-    
-            buffer.flip();
-            channel.position(offset).write(buffer);
-            
-            buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN);
-            for (Entry entry : entries) {
-                buffer.putShort((short) 1);   // we simulate only one thread
-                buffer.putInt(entry.traceId); // entering method
-                buffer.putInt((int) (entry.threadStart - threadStart));
-                buffer.putInt((int) (entry.wallStart - wallStart));
-    
-                buffer.flip();
-                channel.write(buffer);
-                buffer.clear();
-    
-                buffer.putShort((short) 1);
-                buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method
-                buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart));
-                buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart));
-    
-                buffer.flip();
-                channel.write(buffer);
-                buffer.clear();
-            }
-    
-            channel.close();
-        }
-    
-        private static void writeHeader(DataOutputStream out, long start,
-                HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException {
-    
-            Entry last = entries.get(entries.size() - 1);
-            long wallTotal = (last.wallStart + last.wallTime) - start;
-    
-            startSection("version", out);
-            addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out);
-            addValue("data-file-overflow", "false", out);
-            addValue("clock", "dual", out);
-            addValue("elapsed-time-usec", Long.toString(wallTotal), out);
-            addValue("num-method-calls", Integer.toString(entries.size()), out);
-            addValue("clock-call-overhead-nsec", "1", out);
-            addValue("vm", "dalvik", out);
-    
-            startSection("threads", out);
-            addThreadId(1, "main", out);
-    
-            startSection("methods", out);
-            addMethods(names, out);
-    
-            startSection("end", out);
-        }
-    
-        private static void addMethods(HashMap<String, Integer> names, DataOutputStream out)
-                throws IOException {
-    
-            for (Map.Entry<String, Integer> name : names.entrySet()) {
-                out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n",
-                        name.getValue(), name.getKey()));
-            }
-        }
-    
-        private static void addThreadId(int id, String name, DataOutputStream out)
-                throws IOException {
-
-            out.writeBytes(Integer.toString(id) + '\t' + name + '\n');
-        }
-    
-        private static void addValue(String name, String value, DataOutputStream out)
-                throws IOException {
-    
-            if (name != null) {
-                out.writeBytes(name + "=");
-            }
-            out.writeBytes(value + '\n');
-        }
-    
-        private static void startSection(String name, DataOutputStream out) throws IOException {
-            out.writeBytes("*" + name + '\n');
-        }
-
-        static class Entry {
-            int traceId;
-            long wallStart;
-            long wallTime;
-            long threadStart;
-            long threadTime;
-        }
-    }
-
-    /**
-     * Outputs a trace to the currently opened recycler traces. The trace records the type of
-     * recycler action performed on the supplied view as well as a number of parameters.
-     *
-     * @param view the view to trace
-     * @param type the type of the trace
-     * @param parameters parameters depending on the type of the trace
-     */
+    @Deprecated
+    @SuppressWarnings({ "UnusedParameters", "deprecation" })
     public static void trace(View view, RecyclerTraceType type, int... parameters) {
-        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
-            return;
-        }
-
-        if (!sRecyclerViews.contains(view)) {
-            sRecyclerViews.add(view);
-        }
-
-        final int index = sRecyclerViews.indexOf(view);
-
-        RecyclerTrace trace = new RecyclerTrace();
-        trace.view = index;
-        trace.type = type;
-        trace.position = parameters[0];
-        trace.indexOnScreen = parameters[1];
-
-        sRecyclerTraces.add(trace);
     }
 
     /**
-     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
-     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
-     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
-     *
-     * Only one view recycler can be traced at the same time. After calling this method, any
-     * other invocation will result in a <code>IllegalStateException</code> unless
-     * {@link #stopRecyclerTracing()} is invoked before.
-     *
-     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
-     *
-     * This method will return immediately if TRACE_RECYCLER is false.
-     *
-     * @param prefix the traces files name prefix
-     * @param view the view whose recycler must be traced
-     *
-     * @see #stopRecyclerTracing()
-     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void startRecyclerTracing(String prefix, View view) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_RECYCLER) {
-            return;
-        }
-
-        if (sRecyclerOwnerView != null) {
-            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
-                " a new trace!");
-        }
-
-        sRecyclerTracePrefix = prefix;
-        sRecyclerOwnerView = view;
-        sRecyclerViews = new ArrayList<View>();
-        sRecyclerTraces = new LinkedList<RecyclerTrace>();
     }
 
     /**
-     * Stops the current view recycer tracing.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
-     * containing all the traces (or method calls) relative to the specified view's recycler.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
-     * containing all of the views used by the recycler of the view supplied to
-     * {@link #startRecyclerTracing(String, View)}.
-     *
-     * This method will return immediately if TRACE_RECYCLER is false.
-     *
-     * @see #startRecyclerTracing(String, View)
-     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void stopRecyclerTracing() {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_RECYCLER) {
-            return;
-        }
-
-        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
-            throw new IllegalStateException("You must call startRecyclerTracing() before" +
-                " stopRecyclerTracing()!");
-        }
-
-        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
-        //noinspection ResultOfMethodCallIgnored
-        recyclerDump.mkdirs();
-
-        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
-        try {
-            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
-
-            for (View view : sRecyclerViews) {
-                final String name = view.getClass().getName();
-                out.write(name);
-                out.newLine();
-            }
-
-            out.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not dump recycler content");
-            return;
-        }
-
-        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
-        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
-        try {
-            if (recyclerDump.exists()) {
-                //noinspection ResultOfMethodCallIgnored
-                recyclerDump.delete();
-            }
-            final FileOutputStream file = new FileOutputStream(recyclerDump);
-            final DataOutputStream out = new DataOutputStream(file);
-
-            for (RecyclerTrace trace : sRecyclerTraces) {
-                out.writeInt(trace.view);
-                out.writeInt(trace.type.ordinal());
-                out.writeInt(trace.position);
-                out.writeInt(trace.indexOnScreen);
-                out.flush();
-            }
-
-            out.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not dump recycler traces");
-            return;
-        }
-
-        sRecyclerViews.clear();
-        sRecyclerViews = null;
-
-        sRecyclerTraces.clear();
-        sRecyclerTraces = null;
-
-        sRecyclerOwnerView = null;
     }
 
     /**
-     * Outputs a trace to the currently opened traces file. The trace contains the class name
-     * and instance's hashcode of the specified view as well as the supplied trace type.
-     *
-     * @param view the view to trace
-     * @param type the type of the trace
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings({ "UnusedParameters", "deprecation" })
     public static void trace(View view, HierarchyTraceType type) {
-        if (sHierarchyTraces == null) {
-            return;
-        }
-
-        try {
-            sHierarchyTraces.write(type.name());
-            sHierarchyTraces.write(' ');
-            sHierarchyTraces.write(view.getClass().getName());
-            sHierarchyTraces.write('@');
-            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
-            sHierarchyTraces.newLine();
-        } catch (IOException e) {
-            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
-        }
     }
 
     /**
-     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
-     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
-     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
-     *
-     * Only one view hierarchy can be traced at the same time. After calling this method, any
-     * other invocation will result in a <code>IllegalStateException</code> unless
-     * {@link #stopHierarchyTracing()} is invoked before.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
-     * containing all the traces (or method calls) relative to the specified view's hierarchy.
-     *
-     * This method will return immediately if TRACE_HIERARCHY is false.
-     *
-     * @param prefix the traces files name prefix
-     * @param view the view whose hierarchy must be traced
-     *
-     * @see #stopHierarchyTracing()
-     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
+    @SuppressWarnings("UnusedParameters")
     public static void startHierarchyTracing(String prefix, View view) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_HIERARCHY) {
-            return;
-        }
-
-        if (sHierarchyRoot != null) {
-            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
-                " a new trace!");
-        }
-
-        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
-        //noinspection ResultOfMethodCallIgnored
-        hierarchyDump.mkdirs();
-
-        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
-        sHierarchyTracePrefix = prefix;
-
-        try {
-            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
-        } catch (IOException e) {
-            Log.e("View", "Could not dump view hierarchy");
-            return;
-        }
-
-        sHierarchyRoot = view.getViewRootImpl();
     }
 
     /**
-     * Stops the current view hierarchy tracing. This method closes the file
-     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
-     *
-     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
-     * containing the view hierarchy of the view supplied to
-     * {@link #startHierarchyTracing(String, View)}.
-     *
-     * This method will return immediately if TRACE_HIERARCHY is false.
-     *
-     * @see #startHierarchyTracing(String, View)
-     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     * @deprecated This method is now unused and invoking it is a no-op
      */
+    @Deprecated
     public static void stopHierarchyTracing() {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (!TRACE_HIERARCHY) {
-            return;
-        }
-
-        if (sHierarchyRoot == null || sHierarchyTraces == null) {
-            throw new IllegalStateException("You must call startHierarchyTracing() before" +
-                " stopHierarchyTracing()!");
-        }
-
-        try {
-            sHierarchyTraces.close();
-        } catch (IOException e) {
-            Log.e("View", "Could not write view traces");
-        }
-        sHierarchyTraces = null;
-
-        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
-        //noinspection ResultOfMethodCallIgnored
-        hierarchyDump.mkdirs();
-        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
-
-        BufferedWriter out;
-        try {
-            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
-        } catch (IOException e) {
-            Log.e("View", "Could not dump view hierarchy");
-            return;
-        }
-
-        View view = sHierarchyRoot.getView();
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-            dumpViewHierarchy(group, out, 0);
-            try {
-                out.close();
-            } catch (IOException e) {
-                Log.e("View", "Could not dump view hierarchy");
-            }
-        }
-
-        sHierarchyRoot = null;
     }
 
     static void dispatchCommand(View view, String command, String parameters,
@@ -1725,38 +1148,6 @@
         }
     }
 
-    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
-        if (!dumpView(group, out, level)) {
-            return;
-        }
-
-        final int count = group.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View view = group.getChildAt(i);
-            if (view instanceof ViewGroup) {
-                dumpViewHierarchy((ViewGroup) view, out, level + 1);
-            } else {
-                dumpView(view, out, level + 1);
-            }
-        }
-    }
-
-    private static boolean dumpView(Object view, BufferedWriter out, int level) {
-        try {
-            for (int i = 0; i < level; i++) {
-                out.write(' ');
-            }
-            out.write(view.getClass().getName());
-            out.write('@');
-            out.write(Integer.toHexString(view.hashCode()));
-            out.newLine();
-        } catch (IOException e) {
-            Log.w("View", "Error while dumping hierarchy tree");
-            return false;
-        }
-        return true;
-    }
-
     private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
         if (mCapturedViewFieldsForClasses == null) {
             mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
@@ -1863,7 +1254,6 @@
     }
 
     private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
-
         if (obj == null) {
             return "null";
         }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b95ca5e..421109f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3949,10 +3949,6 @@
      * the view hierarchy.
      */
     public final void invalidateChild(View child, final Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
-        }
-
         ViewParent parent = this;
 
         final AttachInfo attachInfo = mAttachInfo;
@@ -4045,10 +4041,6 @@
      * does not intersect with this ViewGroup's bounds.
      */
     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
-        if (ViewDebug.TRACE_HIERARCHY) {
-            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
-        }
-
         if ((mPrivateFlags & DRAWN) == DRAWN ||
                 (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
             if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
@@ -4631,61 +4623,6 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    protected boolean dispatchConsistencyCheck(int consistency) {
-        boolean result = super.dispatchConsistencyCheck(consistency);
-
-        final int count = mChildrenCount;
-        final View[] children = mChildren;
-        for (int i = 0; i < count; i++) {
-            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
-        }
-
-        return result;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = super.onConsistencyCheck(consistency);
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
-
-        if (checkLayout) {
-            final int count = mChildrenCount;
-            final View[] children = mChildren;
-            for (int i = 0; i < count; i++) {
-                if (children[i].getParent() != this) {
-                    result = false;
-                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                            "View " + children[i] + " has no parent/a parent that is not " + this);
-                }
-            }
-        }
-
-        if (checkDrawing) {
-            // If this group is dirty, check that the parent is dirty as well
-            if ((mPrivateFlags & DIRTY_MASK) != 0) {
-                final ViewParent parent = getParent();
-                if (parent != null && !(parent instanceof ViewRootImpl)) {
-                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
-                        result = false;
-                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
-                    }
-                }
-            }
-        }
-
-        return result;
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f86e036..c9a41ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -55,7 +55,6 @@
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TypedValue;
@@ -218,8 +217,6 @@
 
     boolean mTraversalScheduled;
     int mTraversalBarrier;
-    long mLastTraversalFinishedTimeNanos;
-    long mLastDrawFinishedTimeNanos;
     boolean mWillDrawSoon;
     boolean mFitSystemWindowsRequested;
     boolean mLayoutRequested;
@@ -488,7 +485,9 @@
                 mFallbackEventHandler.setView(view);
                 mWindowAttributes.copyFrom(attrs);
                 attrs = mWindowAttributes;
-                
+
+                setAccessibilityFocusedHost(null);
+
                 if (view instanceof RootViewSurfaceTaker) {
                     mSurfaceHolderCallback =
                             ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
@@ -556,6 +555,7 @@
                     mInputChannel = null;
                     mFallbackEventHandler.setView(null);
                     unscheduleTraversals();
+                    setAccessibilityFocusedHost(null);
                     throw new RuntimeException("Adding window failed", e);
                 } finally {
                     if (restore) {
@@ -575,6 +575,7 @@
                     mAdded = false;
                     mFallbackEventHandler.setView(null);
                     unscheduleTraversals();
+                    setAccessibilityFocusedHost(null);
                     switch (res) {
                         case WindowManagerImpl.ADD_BAD_APP_TOKEN:
                         case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
@@ -635,8 +636,6 @@
                 if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                     view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                 }
-
-                setAccessibilityFocusedHost(null);
             }
         }
     }
@@ -985,18 +984,6 @@
                 Debug.startMethodTracing("ViewAncestor");
             }
 
-            final long traversalStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                traversalStartTime = System.nanoTime();
-                if (mLastTraversalFinishedTimeNanos != 0) {
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been "
-                            + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f)
-                            + "ms since the last traversals finished.");
-                } else {
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals().");
-                }
-            }
-
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
             try {
                 performTraversals();
@@ -1004,14 +991,6 @@
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
             }
 
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took "
-                        + ((now - traversalStartTime) * 0.000001f)
-                        + "ms.");
-                mLastTraversalFinishedTimeNanos = now;
-            }
-
             if (mProfile) {
                 Debug.stopMethodTracing();
                 mProfile = false;
@@ -1074,7 +1053,7 @@
             if (baseSize != 0 && desiredWindowWidth > baseSize) {
                 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
-                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
                         + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
@@ -1085,7 +1064,7 @@
                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
                             + baseSize);
                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
-                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
@@ -1099,7 +1078,7 @@
         if (!goodMeasure) {
             childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
             childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
-            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
             if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                 windowSizeMayChange = true;
             }
@@ -1648,7 +1627,7 @@
                             + " coveredInsetsChanged=" + contentInsetsChanged);
     
                      // Ask host how big it wants to be
-                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     
                     // Implementation of weights from WindowManager.LayoutParams
                     // We just grow the dimensions as needed and re-measure if
@@ -1674,7 +1653,7 @@
                         if (DEBUG_LAYOUT) Log.v(TAG,
                                 "And hey let's measure once more: width=" + width
                                 + " height=" + height);
-                        host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                     }
     
                     layoutRequested = true;
@@ -1686,28 +1665,7 @@
         boolean triggerGlobalLayoutListener = didLayout
                 || attachInfo.mRecomputeGlobalAttributes;
         if (didLayout) {
-            mLayoutRequested = false;
-            mScrollMayChange = true;
-            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
-                TAG, "Laying out " + host + " to (" +
-                host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
-            long startTime = 0L;
-            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
-                startTime = SystemClock.elapsedRealtime();
-            }
-            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
-
-            if (false && ViewDebug.consistencyCheckEnabled) {
-                if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
-                    throw new IllegalStateException("The view hierarchy is an inconsistent state,"
-                            + "please refer to the logs with the tag "
-                            + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
-                }
-            }
-
-            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
-                EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
-            }
+            performLayout();
 
             // By this point all views have been sized and positionned
             // We can compute the transparent area
@@ -1853,18 +1811,42 @@
                 performDraw();
             }
         } else {
-            // End any pending transitions on this non-visible window
-            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+            if (viewVisibility == View.VISIBLE) {
+                // Try again
+                scheduleTraversals();
+            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                 for (int i = 0; i < mPendingTransitions.size(); ++i) {
                     mPendingTransitions.get(i).endChangingAnimations();
                 }
                 mPendingTransitions.clear();
             }
+        }
+    }
 
-            if (viewVisibility == View.VISIBLE) {
-                // Try again
-                scheduleTraversals();
-            }
+    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
+        try {
+            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private void performLayout() {
+        mLayoutRequested = false;
+        mScrollMayChange = true;
+
+        final View host = mView;
+        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
+            Log.v(TAG, "Laying out " + host + " to (" +
+                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
+        try {
+            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
     }
 
@@ -2009,18 +1991,6 @@
             return;
         }
 
-        final long drawStartTime;
-        if (ViewDebug.DEBUG_LATENCY) {
-            drawStartTime = System.nanoTime();
-            if (mLastDrawFinishedTimeNanos != 0) {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting draw(); it has been "
-                        + ((drawStartTime - mLastDrawFinishedTimeNanos) * 0.000001f)
-                        + "ms since the last draw finished.");
-            } else {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting draw().");
-            }
-        }
-
         final boolean fullRedrawNeeded = mFullRedrawNeeded;
         mFullRedrawNeeded = false;
 
@@ -2033,14 +2003,6 @@
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            long now = System.nanoTime();
-            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performDraw() took "
-                    + ((now - drawStartTime) * 0.000001f)
-                    + "ms.");
-            mLastDrawFinishedTimeNanos = now;
-        }
-
         if (mReportNextDraw) {
             mReportNextDraw = false;
 
@@ -2184,6 +2146,18 @@
     private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
             boolean scalingRequired, Rect dirty) {
 
+        // If we get here with a disabled & requested hardware renderer, something went
+        // wrong (an invalidate posted right before we destroyed the hardware surface
+        // for instance) so we should just bail out. Locking the surface with software
+        // rendering at this point would lock it forever and prevent hardware renderer
+        // from doing its job when it comes back.
+        if (attachInfo.mHardwareRenderer != null && !attachInfo.mHardwareRenderer.isEnabled() &&
+                attachInfo.mHardwareRenderer.isRequested()) {
+            mFullRedrawNeeded = true;
+            scheduleTraversals();
+            return false;
+        }
+
         // Draw with software renderer.
         Canvas canvas;
         try {
@@ -2192,19 +2166,8 @@
             int right = dirty.right;
             int bottom = dirty.bottom;
 
-            final long lockCanvasStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                lockCanvasStartTime = System.nanoTime();
-            }
-
             canvas = mSurface.lockCanvas(dirty);
 
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
-                        + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
-            }
-
             if (left != dirty.left || top != dirty.top || right != dirty.right ||
                     bottom != dirty.bottom) {
                 attachInfo.mIgnoreDirtyState = true;
@@ -2224,7 +2187,7 @@
             mLayoutRequested = true;    // ask wm for a new surface next time.
             return false;
         } catch (IllegalArgumentException e) {
-            Log.e(TAG, "IllegalArgumentException locking surface", e);
+            Log.e(TAG, "Could not lock surface", e);
             // Don't assume this is due to out of memory, it could be
             // something else, and if it is something else then we could
             // kill stuff (or ourself) for no reason.
@@ -2239,11 +2202,6 @@
                 //canvas.drawARGB(255, 255, 0, 0);
             }
 
-            long startTime = 0L;
-            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                startTime = SystemClock.elapsedRealtime();
-            }
-
             // If this bitmap's format includes an alpha channel, we
             // need to clear it before drawing so that the child will
             // properly re-composite its drawing on a transparent
@@ -2276,46 +2234,23 @@
                         ? DisplayMetrics.DENSITY_DEVICE : 0);
                 attachInfo.mSetIgnoreDirtyState = false;
 
-                final long drawStartTime;
-                if (ViewDebug.DEBUG_LATENCY) {
-                    drawStartTime = System.nanoTime();
-                }
-
                 mView.draw(canvas);
 
                 drawAccessibilityFocusedDrawableIfNeeded(canvas);
-
-                if (ViewDebug.DEBUG_LATENCY) {
-                    long now = System.nanoTime();
-                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
-                            + ((now - drawStartTime) * 0.000001f) + "ms");
-                }
             } finally {
                 if (!attachInfo.mSetIgnoreDirtyState) {
                     // Only clear the flag if it was not set during the mView.draw() call
                     attachInfo.mIgnoreDirtyState = false;
                 }
             }
-
-            if (false && ViewDebug.consistencyCheckEnabled) {
-                mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
-            }
-
-            if (ViewDebug.DEBUG_PROFILE_DRAWING) {
-                EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
-            }
         } finally {
-            final long unlockCanvasAndPostStartTime;
-            if (ViewDebug.DEBUG_LATENCY) {
-                unlockCanvasAndPostStartTime = System.nanoTime();
-            }
-
-            surface.unlockCanvasAndPost(canvas);
-
-            if (ViewDebug.DEBUG_LATENCY) {
-                long now = System.nanoTime();
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
-                        + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
+            try {
+                surface.unlockCanvasAndPost(canvas);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Could not unlock surface", e);
+                mLayoutRequested = true;    // ask wm for a new surface next time.
+                //noinspection ReturnInsideFinallyBlock
+                return false;
             }
 
             if (LOCAL_LOGV) {
@@ -2543,11 +2478,51 @@
     }
 
     void setAccessibilityFocusedHost(View host) {
-        if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) {
+        // If we have a virtual view with accessibility focus we need
+        // to clear the focus and invalidate the virtual view bounds.
+        if (mAccessibilityFocusedVirtualView != null) {
+
+            AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView;
+            View focusHost = mAccessibilityFocusedHost;
+            focusHost.clearAccessibilityFocusNoCallbacks();
+
+            // Wipe the state of the current accessibility focus since
+            // the call into the provider to clear accessibility focus
+            // will fire an accessibility event which will end up calling
+            // this method and we want to have clean state when this
+            // invocation happens.
+            mAccessibilityFocusedHost = null;
+            mAccessibilityFocusedVirtualView = null;
+
+            AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
+            if (provider != null) {
+                // Invalidate the area of the cleared accessibility focus.
+                focusNode.getBoundsInParent(mTempRect);
+                focusHost.invalidate(mTempRect);
+                // Clear accessibility focus in the virtual node.
+                final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        focusNode.getSourceNodeId());
+                provider.performAction(virtualNodeId,
+                        AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
+            }
+        }
+        if (mAccessibilityFocusedHost != null) {
+            // Clear accessibility focus in the view.
             mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
         }
+
+        // Set the new focus host.
         mAccessibilityFocusedHost = host;
-        mAccessibilityFocusedVirtualView = null;
+
+        // If the host has a provide find the virtual descendant that has focus.
+        if (mAccessibilityFocusedHost != null) {
+            AccessibilityNodeProvider provider =
+                mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+            if (provider != null) {
+                mAccessibilityFocusedVirtualView = provider.findAccessibilityFocus(View.NO_ID);
+                return;
+            }
+        }
     }
 
     public void requestChildFocus(View child, View focused) {
@@ -2633,6 +2608,8 @@
 
         destroyHardwareRenderer();
 
+        setAccessibilityFocusedHost(null);
+
         mView = null;
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
@@ -2918,20 +2895,6 @@
                         if (hasWindowFocus) {
                             mView.sendAccessibilityEvent(
                                     AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                            // Give accessibility focus to the view that has input
-                            // focus if such, otherwise to the first one.
-                            if (mView instanceof ViewGroup) {
-                                ViewGroup viewGroup = (ViewGroup) mView;
-                                View focused = viewGroup.findFocus();
-                                if (focused != null) {
-                                    focused.requestAccessibilityFocus();
-                                }
-                            }
-                            // There is no accessibility focus, despite our effort
-                            // above, now just give it to the first view.
-                            if (mAccessibilityFocusedHost == null) {
-                                mView.requestAccessibilityFocus();
-                            }
                         }
                     }
                 }
@@ -3134,10 +3097,6 @@
     }
 
     private void deliverInputEvent(QueuedInputEvent q) {
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mDeliverTimeNanos = System.nanoTime();
-        }
-
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
         try {
             if (q.mEvent instanceof KeyEvent) {
@@ -3585,9 +3544,6 @@
 
     private void deliverKeyEventPostIme(QueuedInputEvent q) {
         final KeyEvent event = (KeyEvent)q.mEvent;
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mDeliverPostImeTimeNanos = System.nanoTime();
-        }
 
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
@@ -4110,11 +4066,6 @@
         public InputEvent mEvent;
         public InputEventReceiver mReceiver;
         public int mFlags;
-
-        // Used for latency calculations.
-        public long mReceiveTimeNanos;
-        public long mDeliverTimeNanos;
-        public long mDeliverPostImeTimeNanos;
     }
 
     private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -4153,12 +4104,6 @@
             InputEventReceiver receiver, int flags, boolean processImmediately) {
         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            q.mReceiveTimeNanos = System.nanoTime();
-            q.mDeliverTimeNanos = 0;
-            q.mDeliverPostImeTimeNanos = 0;
-        }
-
         // Always enqueue the input event in order, regardless of its time stamp.
         // We do this because the application or the IME may inject key events
         // in response to touch events and we want to ensure that the injected keys
@@ -4212,42 +4157,6 @@
             throw new IllegalStateException("finished input event out of order");
         }
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            final long now = System.nanoTime();
-            final long eventTime = q.mEvent.getEventTimeNano();
-            final StringBuilder msg = new StringBuilder();
-            msg.append("Spent ");
-            msg.append((now - q.mReceiveTimeNanos) * 0.000001f);
-            msg.append("ms processing ");
-            if (q.mEvent instanceof KeyEvent) {
-                final KeyEvent  keyEvent = (KeyEvent)q.mEvent;
-                msg.append("key event, action=");
-                msg.append(KeyEvent.actionToString(keyEvent.getAction()));
-            } else {
-                final MotionEvent motionEvent = (MotionEvent)q.mEvent;
-                msg.append("motion event, action=");
-                msg.append(MotionEvent.actionToString(motionEvent.getAction()));
-                msg.append(", historySize=");
-                msg.append(motionEvent.getHistorySize());
-            }
-            msg.append(", handled=");
-            msg.append(handled);
-            msg.append(", received at +");
-            msg.append((q.mReceiveTimeNanos - eventTime) * 0.000001f);
-            if (q.mDeliverTimeNanos != 0) {
-                msg.append("ms, delivered at +");
-                msg.append((q.mDeliverTimeNanos - eventTime) * 0.000001f);
-            }
-            if (q.mDeliverPostImeTimeNanos != 0) {
-                msg.append("ms, delivered post IME at +");
-                msg.append((q.mDeliverPostImeTimeNanos - eventTime) * 0.000001f);
-            }
-            msg.append("ms, finished at +");
-            msg.append((now - eventTime) * 0.000001f);
-            msg.append("ms.");
-            Log.d(ViewDebug.DEBUG_LATENCY_TAG, msg.toString());
-        }
-
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
         } else {
@@ -4394,6 +4303,7 @@
 
             for (int i = 0; i < viewCount; i++) {
                 mTempViews[i].invalidate();
+                mTempViews[i] = null;
             }
 
             for (int i = 0; i < viewRectCount; i++) {
@@ -4608,6 +4518,31 @@
         if (mView == null) {
             return false;
         }
+        // Watch for accessibility focus change events from virtual nodes
+        // to keep track of accessibility focus being on a virtual node.
+        final int eventType = event.getEventType();
+        switch (eventType) {
+            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
+                final long sourceId = event.getSourceNodeId();
+                // If the event is not from a virtual node we are not interested.
+                final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId);
+                if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) {
+                    break;
+                }
+                final int realViewId = AccessibilityNodeInfo.getAccessibilityViewId(sourceId);
+                View focusHost = mView.findViewByAccessibilityId(realViewId);
+                setAccessibilityFocusedHost(focusHost);
+            } break;
+            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
+                final long sourceId = event.getSourceNodeId();
+                // If the event is not from a virtual node we are not interested.
+                final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId);
+                if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) {
+                    break;
+                }
+                setAccessibilityFocusedHost(null);
+            } break;
+        }
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
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/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 0c5d6ea..ceb9fe6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -386,6 +386,12 @@
          */
         public InputChannel monitorInput(String name);
 
+        /**
+         * Switch the keyboard layout for the given device.
+         * Direction should be +1 or -1 to go to the next or previous keyboard layout.
+         */
+        public void switchKeyboardLayout(int deviceId, int direction);
+
         public void shutdown();
         public void rebootSafeMode();
     }
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index bd341d0..20b5f17 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -19,6 +19,7 @@
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
@@ -27,10 +28,14 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseArray;
+import android.util.SparseLongArray;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Queue;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -74,6 +79,8 @@
 
     private static final boolean DEBUG = false;
 
+    private static final boolean CHECK_INTEGRITY = true;
+
     private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
 
     private static final Object sStaticLock = new Object();
@@ -491,6 +498,9 @@
                 result = Collections.emptyList();
             }
             clearResultLocked();
+            if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
+                checkFindAccessibilityNodeInfoResultIntegrity(result);
+            }
             return result;
         }
     }
@@ -696,4 +706,56 @@
             sConnectionCache.remove(connectionId);
         }
     }
+
+    /**
+     * Checks whether the infos are a fully connected tree with no duplicates.
+     *
+     * @param infos The result list to check.
+     */
+    private void checkFindAccessibilityNodeInfoResultIntegrity(List<AccessibilityNodeInfo> infos) {
+        if (infos.size() == 0) {
+            return;
+        }
+        // Find the root node.
+        AccessibilityNodeInfo root = infos.get(0);
+        final int infoCount = infos.size();
+        for (int i = 1; i < infoCount; i++) {
+            for (int j = i; j < infoCount; j++) {
+                AccessibilityNodeInfo candidate = infos.get(j);
+                if (root.getParentNodeId() == candidate.getSourceNodeId()) {
+                    root = candidate;
+                    break;
+                }
+            }
+        }
+        if (root == null) {
+            Log.e(LOG_TAG, "No root.");
+        }
+        // Check for duplicates.
+        HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
+        Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+        fringe.add(root);
+        while (!fringe.isEmpty()) {
+            AccessibilityNodeInfo current = fringe.poll();
+            if (!seen.add(current)) {
+                Log.e(LOG_TAG, "Duplicate node.");
+                return;
+            }
+            SparseLongArray childIds = current.getChildNodeIds();
+            final int childCount = childIds.size();
+            for (int i = 0; i < childCount; i++) {
+                final long childId = childIds.valueAt(i);
+                for (int j = 0; j < infoCount; j++) {
+                    AccessibilityNodeInfo child = infos.get(j);
+                    if (child.getSourceNodeId() == childId) {
+                        fringe.add(child);
+                    }
+                }
+            }
+        }
+        final int disconnectedCount = infos.size() - seen.size();
+        if (disconnectedCount > 0) {
+            Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes.");
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index 52b7772..14954be 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -244,7 +244,7 @@
     /**
      * We are enforcing the invariant for a single accessibility focus.
      *
-     * @param currentInputFocusId The current input focused node.
+     * @param currentAccessibilityFocusId The current input focused node.
      */
     private void clearSubtreeWithOldAccessibilityFocusLocked(long currentAccessibilityFocusId) {
         final int cacheSize = mCacheImpl.size();
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1803352..d2cc2d8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -283,6 +283,7 @@
      * The InputConnection that was last retrieved from the served view.
      */
     InputConnection mServedInputConnection;
+    ControlledInputConnectionWrapper mServedInputConnectionWrapper;
     /**
      * The completions that were last provided by the served view.
      */
@@ -418,16 +419,22 @@
     
     private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
         private final InputMethodManager mParentInputMethodManager;
+        private boolean mActive;
 
         public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
                 final InputMethodManager inputMethodManager) {
             super(mainLooper, conn);
             mParentInputMethodManager = inputMethodManager;
+            mActive = true;
         }
 
         @Override
         public boolean isActive() {
-            return mParentInputMethodManager.mActive;
+            return mParentInputMethodManager.mActive && mActive;
+        }
+
+        void deactivate() {
+            mActive = false;
         }
     }
     
@@ -666,6 +673,10 @@
     void clearConnectionLocked() {
         mCurrentTextBoxAttribute = null;
         mServedInputConnection = null;
+        if (mServedInputConnectionWrapper != null) {
+            mServedInputConnectionWrapper.deactivate();
+            mServedInputConnectionWrapper = null;
+        }
     }
     
     /**
@@ -1060,7 +1071,7 @@
             // Notify the served view that its previous input connection is finished
             notifyInputConnectionFinished();
             mServedInputConnection = ic;
-            IInputContext servedContext;
+            ControlledInputConnectionWrapper servedContext;
             if (ic != null) {
                 mCursorSelStart = tba.initialSelStart;
                 mCursorSelEnd = tba.initialSelEnd;
@@ -1071,6 +1082,10 @@
             } else {
                 servedContext = null;
             }
+            if (mServedInputConnectionWrapper != null) {
+                mServedInputConnectionWrapper.deactivate();
+            }
+            mServedInputConnectionWrapper = servedContext;
             
             try {
                 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
@@ -1286,6 +1301,7 @@
         // we'll just do a window focus gain and call it a day.
         synchronized (mH) {
             try {
+                if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
                 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
                         controlFlags, softInputMode, windowFlags, null, null);
             } catch (RemoteException e) {
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 11bd815..cc490bd 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -16,484 +16,615 @@
 
 package android.webkit;
 
+import android.content.Context;
+import android.os.Bundle;
+import android.os.SystemClock;
 import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Log;
+import android.speech.tts.TextToSpeech;
 import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityEvent;
+import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.webkit.WebViewCore.EventHub;
 
-import java.util.ArrayList;
-import java.util.Stack;
+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;
 
 /**
- * This class injects accessibility into WebViews with disabled JavaScript or
- * WebViews with enabled JavaScript but for which we have no accessibility
- * script to inject.
- * </p>
- * Note: To avoid changes in the framework upon changing the available
- *       navigation axis, or reordering the navigation axis, or changing
- *       the key bindings, or defining sequence of actions to be bound to
- *       a given key this class is navigation axis agnostic. It is only
- *       aware of one navigation axis which is in fact the default behavior
- *       of webViews while using the DPAD/TrackBall.
- * </p>
- * In general a key binding is a mapping from modifiers + key code to
- * a sequence of actions. For more detail how to specify key bindings refer to
- * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}.
- * </p>
- * The possible actions are invocations to
- * {@link #setCurrentAxis(int, boolean, String)}, or
- * {@link #traverseCurrentAxis(int, boolean, String)}
- * {@link #traverseGivenAxis(int, int, boolean, String)}
- * {@link #prefromAxisTransition(int, int, boolean, String)}
- * referred via the values of:
- * {@link #ACTION_SET_CURRENT_AXIS},
- * {@link #ACTION_TRAVERSE_CURRENT_AXIS},
- * {@link #ACTION_TRAVERSE_GIVEN_AXIS},
- * {@link #ACTION_PERFORM_AXIS_TRANSITION},
- * respectively.
- * The arguments for the action invocation are specified as offset
- * hexademical pairs. Note the last argument of the invocation
- * should NOT be specified in the binding as it is provided by
- * this class. For details about the key binding implementation
- * refer to {@link AccessibilityWebContentKeyBinding}.
+ * Handles injecting accessibility JavaScript and related JavaScript -> Java
+ * APIs.
  */
 class AccessibilityInjector {
-    private static final String LOG_TAG = "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";
 
-    private static final boolean DEBUG = true;
+    // The WebViewClassic this injector is responsible for managing.
+    private final WebViewClassic mWebViewClassic;
 
-    private static final int ACTION_SET_CURRENT_AXIS = 0;
-    private static final int ACTION_TRAVERSE_CURRENT_AXIS = 1;
-    private static final int ACTION_TRAVERSE_GIVEN_AXIS = 2;
-    private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
-    private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4;
+    // Cached reference to mWebViewClassic.getContext(), for convenience.
+    private final Context mContext;
 
-    // the default WebView behavior abstracted as a navigation axis
-    private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;
+    // Cached reference to mWebViewClassic.getWebView(), for convenience.
+    private final WebView mWebView;
 
-    // these are the same for all instances so make them process wide
-    private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
-        new ArrayList<AccessibilityWebContentKeyBinding>();
+    // The Java objects that are exposed to JavaScript.
+    private TextToSpeech mTextToSpeech;
+    private CallbackHandler mCallback;
 
-    // handle to the WebViewClassic this injector is associated with.
-    private final WebViewClassic mWebView;
+    // Lazily loaded helper objects.
+    private AccessibilityManager mAccessibilityManager;
+    private AccessibilityInjectorFallback mAccessibilityInjectorFallback;
+    private JSONObject mAccessibilityJSONObject;
 
-    // events scheduled for sending as soon as we receive the selected text
-    private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
+    // Whether the accessibility script has been injected into the current page.
+    private boolean mAccessibilityScriptInjected;
 
-    // the current traversal axis
-    private int mCurrentAxis = 2; // sentence
+    // Constants for determining script injection strategy.
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
+    @SuppressWarnings("unused")
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
 
-    // we need to consume the up if we have handled the last down
-    private boolean mLastDownEventHandled;
+    // Alias for TTS API exposed to JavaScript.
+    private static final String ALIAS_TTS_JS_INTERFACE = "accessibility";
 
-    // getting two empty selection strings in a row we let the WebView handle the event
-    private boolean mIsLastSelectionStringNull;
+    // Alias for traversal callback exposed to JavaScript.
+    private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal";
 
-    // keep track of last direction
-    private int mLastDirection;
+    // Template for JavaScript that injects a screen-reader.
+    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
+            "javascript:(function() {" +
+                    "    var chooser = document.createElement('script');" +
+                    "    chooser.type = 'text/javascript';" +
+                    "    chooser.src = '%1s';" +
+                    "    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 a new injector associated with a given {@link WebViewClassic}.
+     * Creates an instance of the AccessibilityInjector based on
+     * {@code webViewClassic}.
      *
-     * @param webView The associated WebViewClassic.
+     * @param webViewClassic The WebViewClassic that this AccessibilityInjector
+     *            manages.
      */
-    public AccessibilityInjector(WebViewClassic webView) {
-        mWebView = webView;
-        ensureWebContentKeyBindings();
+    public AccessibilityInjector(WebViewClassic webViewClassic) {
+        mWebViewClassic = webViewClassic;
+        mWebView = webViewClassic.getWebView();
+        mContext = webViewClassic.getContext();
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
     }
 
     /**
-     * Processes a key down <code>event</code>.
-     *
-     * @return True if the event was processed.
+     * Attempts to load scripting interfaces for accessibility.
+     * <p>
+     * This should be called when the window is attached.
+     * </p>
      */
-    public boolean onKeyEvent(KeyEvent event) {
-        // We do not handle ENTER in any circumstances.
-        if (isEnterActionKey(event.getKeyCode())) {
+    public void addAccessibilityApisIfNecessary() {
+        if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
+            return;
+        }
+
+        addTtsApis();
+        addCallbackApis();
+    }
+
+    /**
+     * Attempts to unload scripting interfaces for accessibility.
+     * <p>
+     * This should be called when the window is detached.
+     * </p>
+     */
+    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 (event.getAction() == KeyEvent.ACTION_UP) {
-            return mLastDownEventHandled;
+        if (mAccessibilityScriptInjected) {
+            return sendActionToAndroidVox(action, arguments);
+        }
+        
+        if (mAccessibilityInjectorFallback != null) {
+            return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments);
         }
 
-        mLastDownEventHandled = false;
+        return false;
+    }
 
-        AccessibilityWebContentKeyBinding binding = null;
-        for (AccessibilityWebContentKeyBinding candidate : sBindings) {
-            if (event.getKeyCode() == candidate.getKeyCode()
-                    && event.hasModifiers(candidate.getModifiers())) {
-                binding = candidate;
-                break;
+    /**
+     * Attempts to handle key events when accessibility is turned on.
+     *
+     * @param event The key event to handle.
+     * @return {@code true} if the event was handled.
+     */
+    public boolean handleKeyEventIfNecessary(KeyEvent event) {
+        if (!isAccessibilityEnabled()) {
+            mAccessibilityScriptInjected = false;
+            toggleFallbackAccessibilityInjector(false);
+            return false;
+        }
+
+        if (mAccessibilityScriptInjected) {
+            // if an accessibility script is injected we delegate to it the key
+            // handling. this script is a screen reader which is a fully fledged
+            // solution for blind users to navigate in and interact with web
+            // pages.
+            if (event.getAction() == KeyEvent.ACTION_UP) {
+                mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
+            } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
+            } else {
+                return false;
             }
+
+            return true;
         }
 
-        if (binding == 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 mAccessibilityInjectorFallback.onKeyEvent(event);
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempts to handle selection change events when accessibility is using a
+     * non-JavaScript method.
+     *
+     * @param selectionString The selection string.
+     */
+    public void handleSelectionChangedIfNecessary(String selectionString) {
+        if (mAccessibilityInjectorFallback != null) {
+            mAccessibilityInjectorFallback.onSelectionStringChange(selectionString);
+        }
+    }
+
+    /**
+     * Prepares for injecting accessibility scripts into a new page.
+     *
+     * @param url The URL that will be loaded.
+     */
+    public void onPageStarted(String url) {
+        mAccessibilityScriptInjected = false;
+    }
+
+    /**
+     * Attempts to inject the accessibility script using a {@code <script>} tag.
+     * <p>
+     * This should be called after a page has finished loading.
+     * </p>
+     *
+     * @param url The URL that just finished loading.
+     */
+    public void onPageFinished(String url) {
+        if (!isAccessibilityEnabled()) {
+            mAccessibilityScriptInjected = false;
+            toggleFallbackAccessibilityInjector(false);
+            return;
+        }
+
+        if (!shouldInjectJavaScript(url)) {
+            toggleFallbackAccessibilityInjector(true);
+            return;
+        }
+
+        toggleFallbackAccessibilityInjector(false);
+
+        final String injectionUrl = getScreenReaderInjectionUrl();
+        mWebView.loadUrl(injectionUrl);
+
+        mAccessibilityScriptInjected = true;
+    }
+
+    /**
+     * Toggles the non-JavaScript method for handling accessibility.
+     *
+     * @param enabled {@code true} to enable the non-JavaScript method, or
+     *            {@code false} to disable it.
+     */
+    private void toggleFallbackAccessibilityInjector(boolean enabled) {
+        if (enabled && (mAccessibilityInjectorFallback == null)) {
+            mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic);
+        } else {
+            mAccessibilityInjectorFallback = null;
+        }
+    }
+
+    /**
+     * Determines whether it's okay to inject JavaScript into a given URL.
+     *
+     * @param url The URL to check.
+     * @return {@code true} if JavaScript should be injected, {@code false} if a
+     *         non-JavaScript method should be used.
+     */
+    private boolean shouldInjectJavaScript(String url) {
+        // Respect the WebView's JavaScript setting.
+        if (!isJavaScriptEnabled()) {
             return false;
         }
 
-        for (int i = 0, count = binding.getActionCount(); i < count; i++) {
-            int actionCode = binding.getActionCode(i);
-            String contentDescription = Integer.toHexString(binding.getAction(i));
-            switch (actionCode) {
-                case ACTION_SET_CURRENT_AXIS:
-                    int axis = binding.getFirstArgument(i);
-                    boolean sendEvent = (binding.getSecondArgument(i) == 1);
-                    setCurrentAxis(axis, sendEvent, contentDescription);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_TRAVERSE_CURRENT_AXIS:
-                    int direction = binding.getFirstArgument(i);
-                    // on second null selection string in same direction - WebView handles the event
-                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
-                        mIsLastSelectionStringNull = false;
-                        return false;
-                    }
-                    mLastDirection = direction;
-                    sendEvent = (binding.getSecondArgument(i) == 1);
-                    mLastDownEventHandled = traverseCurrentAxis(direction, sendEvent,
-                            contentDescription);
-                    break;
-                case ACTION_TRAVERSE_GIVEN_AXIS:
-                    direction = binding.getFirstArgument(i);
-                    // on second null selection string in same direction => WebView handle the event
-                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
-                        mIsLastSelectionStringNull = false;
-                        return false;
-                    }
-                    mLastDirection = direction;
-                    axis =  binding.getSecondArgument(i);
-                    sendEvent = (binding.getThirdArgument(i) == 1);
-                    traverseGivenAxis(direction, axis, sendEvent, contentDescription);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_PERFORM_AXIS_TRANSITION:
-                    int fromAxis = binding.getFirstArgument(i);
-                    int toAxis = binding.getSecondArgument(i);
-                    sendEvent = (binding.getThirdArgument(i) == 1);
-                    prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS:
-                    // This is a special case since we treat the default WebView navigation
-                    // behavior as one of the possible navigation axis the user can use.
-                    // If we are not on the default WebView navigation axis this is NOP.
-                    if (mCurrentAxis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
-                        // While WebVew handles navigation we do not get null selection
-                        // strings so do not check for that here as the cases above.
-                        mLastDirection = binding.getFirstArgument(i);
-                        sendEvent = (binding.getSecondArgument(i) == 1);
-                        traverseGivenAxis(mLastDirection, NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR,
-                            sendEvent, contentDescription);
-                        mLastDownEventHandled = false;
-                    } else {
-                        mLastDownEventHandled = true;
-                    }
-                    break;
-                default:
-                    Log.w(LOG_TAG, "Unknown action code: " + actionCode);
-            }
-        }
-
-        return mLastDownEventHandled;
-    }
-
-    /**
-     * Set the current navigation axis which will be used while
-     * calling {@link #traverseCurrentAxis(int, boolean, String)}.
-     *
-     * @param axis The axis to set.
-     * @param sendEvent Whether to send an accessibility event to
-     *        announce the change.
-     */
-    private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
-        mCurrentAxis = axis;
-        if (sendEvent) {
-            AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent();
-            event.getText().add(String.valueOf(axis));
-            event.setContentDescription(contentDescription);
-            sendAccessibilityEvent(event);
-        }
-    }
-
-    /**
-     * Performs conditional transition one axis to another.
-     *
-     * @param fromAxis The axis which must be the current for the transition to occur.
-     * @param toAxis The axis to which to transition.
-     * @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,
-            String contentDescription) {
-        if (mCurrentAxis == fromAxis) {
-            setCurrentAxis(toAxis, sendEvent, contentDescription);
-        }
-    }
-
-    /**
-     * Traverse the document along the current navigation axis.
-     *
-     * @param direction The direction of traversal.
-     * @param sendEvent Whether to send an accessibility event to
-     *        announce the change.
-     * @param contentDescription A description of the performed action.
-     * @see #setCurrentAxis(int, boolean, String)
-     */
-    private boolean traverseCurrentAxis(int direction, boolean sendEvent,
-            String contentDescription) {
-        return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription);
-    }
-
-    /**
-     * Traverse the document along the given navigation axis.
-     *
-     * @param direction The direction of traversal.
-     * @param axis The axis along which to traverse.
-     * @param sendEvent Whether to send an accessibility event to
-     *        announce the change.
-     * @param contentDescription A description of the performed action.
-     */
-    private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent,
-            String contentDescription) {
-        WebViewCore webViewCore = mWebView.getWebViewCore();
-        if (webViewCore == null) {
+        // Allow the page to opt out of Accessibility script injection.
+        if (getAxsUrlParameterValue(url) == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
             return false;
         }
 
-        AccessibilityEvent event = null;
-        if (sendEvent) {
-            event = getPartialyPopulatedAccessibilityEvent();
-            // the text will be set upon receiving the selection string
-            event.setContentDescription(contentDescription);
-        }
-        mScheduledEventStack.push(event);
-
-        // if the axis is the default let WebView handle the event which will
-        // result in cursor ring movement and selection of its content
-        if (axis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
+        // The user must explicitly enable Accessibility script injection.
+        if (!isScriptInjectionEnabled()) {
             return false;
         }
 
-        webViewCore.sendMessage(EventHub.MODIFY_SELECTION, direction, axis);
         return true;
     }
 
     /**
-     * Called when the <code>selectionString</code> has changed.
+     * @return {@code true} if the user has explicitly enabled Accessibility
+     *         script injection.
      */
-    public void onSelectionStringChange(String selectionString) {
-        if (DEBUG) {
-            Log.d(LOG_TAG, "Selection string: " + selectionString);
-        }
-        mIsLastSelectionStringNull = (selectionString == null);
-        if (mScheduledEventStack.isEmpty()) {
-            return;
-        }
-        AccessibilityEvent event = mScheduledEventStack.pop();
-        if (event != null) {
-            event.getText().add(selectionString);
-            sendAccessibilityEvent(event);
-        }
+    private boolean isScriptInjectionEnabled() {
+        final int injectionSetting = Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0);
+        return (injectionSetting == 1);
     }
 
     /**
-     * Sends an {@link AccessibilityEvent}.
+     * Attempts to initialize and add interfaces for TTS, if that hasn't already
+     * been done.
+     */
+    private void addTtsApis() {
+        if (mTextToSpeech != null) {
+            return;
+        }
+
+        final String pkgName = mContext.getPackageName();
+
+        mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
+        mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
+    }
+
+    /**
+     * Attempts to shutdown and remove interfaces for TTS, if that hasn't
+     * already been done.
+     */
+    private void removeTtsApis() {
+        if (mTextToSpeech == null) {
+            return;
+        }
+
+        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
+     * preference.
      *
-     * @param event The event to send.
+     * @param url The URL to check.
+     * @return A script injection preference.
      */
-    private void sendAccessibilityEvent(AccessibilityEvent event) {
-        if (DEBUG) {
-            Log.d(LOG_TAG, "Dispatching: " + event);
-        }
-        // accessibility may be disabled while waiting for the selection string
-        AccessibilityManager accessibilityManager =
-            AccessibilityManager.getInstance(mWebView.getContext());
-        if (accessibilityManager.isEnabled()) {
-            accessibilityManager.sendAccessibilityEvent(event);
-        }
-    }
-
-    /**
-     * @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());
-        return event;
-    }
-
-    /**
-     * Ensures that the Web content key bindings are loaded.
-     */
-    private void ensureWebContentKeyBindings() {
-        if (sBindings.size() > 0) {
-            return;
+    private int getAxsUrlParameterValue(String url) {
+        if (url == null) {
+            return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
         }
 
-        String webContentKeyBindingsString  = Settings.Secure.getString(
-                mWebView.getContext().getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
+        try {
+            final List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), null);
 
-        SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';');
-        semiColonSplitter.setString(webContentKeyBindingsString);
-
-        while (semiColonSplitter.hasNext()) {
-            String bindingString = semiColonSplitter.next();
-            if (TextUtils.isEmpty(bindingString)) {
-                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: "
-                        + webContentKeyBindingsString);
-                continue;
-            }
-            String[] keyValueArray = bindingString.split("=");
-            if (keyValueArray.length != 2) {
-                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " + bindingString);
-                continue;
-            }
-            try {
-                long keyCodeAndModifiers = Long.decode(keyValueArray[0].trim());
-                String[] actionStrings = keyValueArray[1].split(":");
-                int[] actions = new int[actionStrings.length];
-                for (int i = 0, count = actions.length; i < count; i++) {
-                    actions[i] = Integer.decode(actionStrings[i].trim());
+            for (NameValuePair param : params) {
+                if ("axs".equals(param.getName())) {
+                    return verifyInjectionValue(param.getValue());
                 }
-                sBindings.add(new AccessibilityWebContentKeyBinding(keyCodeAndModifiers, actions));
-            } catch (NumberFormatException nfe) {
-                Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString);
             }
+        } catch (URISyntaxException e) {
+            // Do nothing.
         }
+
+        return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
     }
 
-    private boolean isEnterActionKey(int keyCode) {
-        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER
-                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
+    private int verifyInjectionValue(String value) {
+        try {
+            final int parsed = Integer.parseInt(value);
+
+            switch (parsed) {
+                case ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT:
+                    return ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT;
+                case ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED:
+                    return ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED;
+            }
+        } catch (NumberFormatException e) {
+            // Do nothing.
+        }
+
+        return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
     }
 
     /**
-     * Represents a web content key-binding.
+     * @return The URL for injecting the screen reader.
      */
-    private static final class AccessibilityWebContentKeyBinding {
+    private String getScreenReaderInjectionUrl() {
+        final String screenReaderUrl = Settings.Secure.getString(
+                mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
+        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
+    }
 
-        private static final int MODIFIERS_OFFSET = 32;
-        private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L;
+    /**
+     * @return {@code true} if JavaScript is enabled in the {@link WebView}
+     *         settings.
+     */
+    private boolean isJavaScriptEnabled() {
+        return mWebView.getSettings().getJavaScriptEnabled();
+    }
 
-        private static final int KEY_CODE_OFFSET = 0;
-        private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL;
+    /**
+     * @return {@code true} if accessibility is enabled.
+     */
+    private boolean isAccessibilityEnabled() {
+        return mAccessibilityManager.isEnabled();
+    }
 
-        private static final int ACTION_OFFSET = 24;
-        private static final int ACTION_MASK = 0xFF000000;
-
-        private static final int FIRST_ARGUMENT_OFFSET = 16;
-        private static final int FIRST_ARGUMENT_MASK = 0x00FF0000;
-
-        private static final int SECOND_ARGUMENT_OFFSET = 8;
-        private static final int SECOND_ARGUMENT_MASK = 0x0000FF00;
-
-        private static final int THIRD_ARGUMENT_OFFSET = 0;
-        private static final int THIRD_ARGUMENT_MASK = 0x000000FF;
-
-        private final long mKeyCodeAndModifiers;
-
-        private final int [] mActionSequence;
-
-        /**
-         * @return The key code of the binding key.
-         */
-        public int getKeyCode() {
-            return (int) ((mKeyCodeAndModifiers & KEY_CODE_MASK) >> KEY_CODE_OFFSET);
-        }
-
-        /**
-         * @return The meta state of the binding key.
-         */
-        public int getModifiers() {
-            return (int) ((mKeyCodeAndModifiers & MODIFIERS_MASK) >> MODIFIERS_OFFSET);
-        }
-
-        /**
-         * @return The number of actions in the key binding.
-         */
-        public int getActionCount() {
-            return mActionSequence.length;
-        }
-
-        /**
-         * @param index The action for a given action <code>index</code>.
-         */
-        public int getAction(int index) {
-            return mActionSequence[index];
-        }
-
-        /**
-         * @param index The action code for a given action <code>index</code>.
-         */
-        public int getActionCode(int index) {
-            return (mActionSequence[index] & ACTION_MASK) >> ACTION_OFFSET;
-        }
-
-        /**
-         * @param index The first argument for a given action <code>index</code>.
-         */
-        public int getFirstArgument(int index) {
-            return (mActionSequence[index] & FIRST_ARGUMENT_MASK) >> FIRST_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * @param index The second argument for a given action <code>index</code>.
-         */
-        public int getSecondArgument(int index) {
-            return (mActionSequence[index] & SECOND_ARGUMENT_MASK) >> SECOND_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * @param index The third argument for a given action <code>index</code>.
-         */
-        public int getThirdArgument(int index) {
-            return (mActionSequence[index] & THIRD_ARGUMENT_MASK) >> THIRD_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * Creates a new instance.
-         * @param keyCodeAndModifiers The key for the binding (key and modifiers).
-         * @param actionSequence The sequence of action for the binding.
-         */
-        public AccessibilityWebContentKeyBinding(long keyCodeAndModifiers, int[] actionSequence) {
-            mKeyCodeAndModifiers = keyCodeAndModifiers;
-            mActionSequence = actionSequence;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("modifiers: ");
-            builder.append(getModifiers());
-            builder.append(", keyCode: ");
-            builder.append(getKeyCode());
-            builder.append(", actions[");
-            for (int i = 0, count = getActionCount(); i < count; i++) {
-                builder.append("{actionCode");
-                builder.append(i);
-                builder.append(": ");
-                builder.append(getActionCode(i));
-                builder.append(", firstArgument: ");
-                builder.append(getFirstArgument(i));
-                builder.append(", secondArgument: ");
-                builder.append(getSecondArgument(i));
-                builder.append(", thirdArgument: ");
-                builder.append(getThirdArgument(i));
-                builder.append("}");
+    /**
+     * 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();
             }
-            builder.append("]");
-            return builder.toString();
+        }
+
+        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
new file mode 100644
index 0000000..4d9c26c
--- /dev/null
+++ b/core/java/android/webkit/AccessibilityInjectorFallback.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Log;
+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;
+import java.util.Stack;
+
+/**
+ * This class injects accessibility into WebViews with disabled JavaScript or
+ * WebViews with enabled JavaScript but for which we have no accessibility
+ * script to inject.
+ * </p>
+ * Note: To avoid changes in the framework upon changing the available
+ *       navigation axis, or reordering the navigation axis, or changing
+ *       the key bindings, or defining sequence of actions to be bound to
+ *       a given key this class is navigation axis agnostic. It is only
+ *       aware of one navigation axis which is in fact the default behavior
+ *       of webViews while using the DPAD/TrackBall.
+ * </p>
+ * In general a key binding is a mapping from modifiers + key code to
+ * a sequence of actions. For more detail how to specify key bindings refer to
+ * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}.
+ * </p>
+ * The possible actions are invocations to
+ * {@link #setCurrentAxis(int, boolean, String)}, or
+ * {@link #traverseCurrentAxis(int, boolean, String)}
+ * {@link #traverseGivenAxis(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},
+ * {@link #ACTION_TRAVERSE_GIVEN_AXIS},
+ * {@link #ACTION_PERFORM_AXIS_TRANSITION},
+ * respectively.
+ * The arguments for the action invocation are specified as offset
+ * hexademical pairs. Note the last argument of the invocation
+ * should NOT be specified in the binding as it is provided by
+ * this class. For details about the key binding implementation
+ * refer to {@link AccessibilityWebContentKeyBinding}.
+ */
+class AccessibilityInjectorFallback {
+    private static final String LOG_TAG = "AccessibilityInjector";
+
+    private static final boolean DEBUG = true;
+
+    private static final int ACTION_SET_CURRENT_AXIS = 0;
+    private static final int ACTION_TRAVERSE_CURRENT_AXIS = 1;
+    private static final int ACTION_TRAVERSE_GIVEN_AXIS = 2;
+    private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
+    private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4;
+
+    // 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>();
+
+    // the current traversal axis
+    private int mCurrentAxis = 2; // sentence
+
+    // we need to consume the up if we have handled the last down
+    private boolean mLastDownEventHandled;
+
+    // getting two empty selection strings in a row we let the WebView handle the event
+    private boolean mIsLastSelectionStringNull;
+
+    // keep track of last direction
+    private int mLastDirection;
+
+    /**
+     * Creates a new injector associated with a given {@link WebViewClassic}.
+     *
+     * @param webView The associated WebViewClassic.
+     */
+    public AccessibilityInjectorFallback(WebViewClassic webView) {
+        mWebView = webView;
+        mWebViewInternal = mWebView.getWebView();
+        ensureWebContentKeyBindings();
+    }
+
+    /**
+     * Processes a key down <code>event</code>.
+     *
+     * @return True if the event was processed.
+     */
+    public boolean onKeyEvent(KeyEvent event) {
+        // We do not handle ENTER in any circumstances.
+        if (isEnterActionKey(event.getKeyCode())) {
+            return false;
+        }
+
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            return mLastDownEventHandled;
+        }
+
+        mLastDownEventHandled = false;
+
+        AccessibilityWebContentKeyBinding binding = null;
+        for (AccessibilityWebContentKeyBinding candidate : sBindings) {
+            if (event.getKeyCode() == candidate.getKeyCode()
+                    && event.hasModifiers(candidate.getModifiers())) {
+                binding = candidate;
+                break;
+            }
+        }
+
+        if (binding == null) {
+            return false;
+        }
+
+        for (int i = 0, count = binding.getActionCount(); i < count; i++) {
+            int actionCode = binding.getActionCode(i);
+            String contentDescription = Integer.toHexString(binding.getAction(i));
+            switch (actionCode) {
+                case ACTION_SET_CURRENT_AXIS:
+                    int axis = binding.getFirstArgument(i);
+                    boolean sendEvent = (binding.getSecondArgument(i) == 1);
+                    setCurrentAxis(axis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                case ACTION_TRAVERSE_CURRENT_AXIS:
+                    int direction = binding.getFirstArgument(i);
+                    // on second null selection string in same direction - WebView handles the event
+                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
+                        mIsLastSelectionStringNull = false;
+                        return false;
+                    }
+                    mLastDirection = direction;
+                    sendEvent = (binding.getSecondArgument(i) == 1);
+                    mLastDownEventHandled = traverseCurrentAxis(direction, sendEvent,
+                            contentDescription);
+                    break;
+                case ACTION_TRAVERSE_GIVEN_AXIS:
+                    direction = binding.getFirstArgument(i);
+                    // on second null selection string in same direction => WebView handle the event
+                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
+                        mIsLastSelectionStringNull = false;
+                        return false;
+                    }
+                    mLastDirection = direction;
+                    axis =  binding.getSecondArgument(i);
+                    sendEvent = (binding.getThirdArgument(i) == 1);
+                    traverseGivenAxis(direction, axis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                case ACTION_PERFORM_AXIS_TRANSITION:
+                    int fromAxis = binding.getFirstArgument(i);
+                    int toAxis = binding.getSecondArgument(i);
+                    sendEvent = (binding.getThirdArgument(i) == 1);
+                    performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS:
+                    // This is a special case since we treat the default WebView navigation
+                    // behavior as one of the possible navigation axis the user can use.
+                    // If we are not on the default WebView navigation axis this is NOP.
+                    if (mCurrentAxis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
+                        // While WebVew handles navigation we do not get null selection
+                        // strings so do not check for that here as the cases above.
+                        mLastDirection = binding.getFirstArgument(i);
+                        sendEvent = (binding.getSecondArgument(i) == 1);
+                        traverseGivenAxis(mLastDirection, NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR,
+                            sendEvent, contentDescription);
+                        mLastDownEventHandled = false;
+                    } else {
+                        mLastDownEventHandled = true;
+                    }
+                    break;
+                default:
+                    Log.w(LOG_TAG, "Unknown action code: " + actionCode);
+            }
+        }
+
+        return mLastDownEventHandled;
+    }
+
+    /**
+     * Set the current navigation axis which will be used while
+     * calling {@link #traverseCurrentAxis(int, boolean, String)}.
+     *
+     * @param axis The axis to set.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     */
+    private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
+        mCurrentAxis = axis;
+        if (sendEvent) {
+            final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(
+                    AccessibilityEvent.TYPE_ANNOUNCEMENT);
+            event.getText().add(String.valueOf(axis));
+            event.setContentDescription(contentDescription);
+            sendAccessibilityEvent(event);
+        }
+    }
+
+    /**
+     * Performs conditional transition one axis to another.
+     *
+     * @param fromAxis The axis which must be the current for the transition to occur.
+     * @param toAxis The axis to which to transition.
+     * @param sendEvent Flag if to send an event to announce successful transition.
+     * @param contentDescription A description of the performed action.
+     */
+    private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
+            String contentDescription) {
+        if (mCurrentAxis == fromAxis) {
+            setCurrentAxis(toAxis, sendEvent, contentDescription);
+        }
+    }
+
+    /**
+     * Traverse the document along the current navigation axis.
+     *
+     * @param direction The direction of traversal.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     * @param contentDescription A description of the performed action.
+     * @see #setCurrentAxis(int, boolean, String)
+     */
+    private boolean traverseCurrentAxis(int direction, boolean sendEvent,
+            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.
+     *
+     * @param direction The direction of traversal.
+     * @param axis The axis along which to traverse.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     * @param contentDescription A description of the performed action.
+     */
+    private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent,
+            String contentDescription) {
+        WebViewCore webViewCore = mWebView.getWebViewCore();
+        if (webViewCore == null) {
+            return false;
+        }
+
+        AccessibilityEvent event = null;
+        if (sendEvent) {
+            event = getPartialyPopulatedAccessibilityEvent(
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
+            // the text will be set upon receiving the selection string
+            event.setContentDescription(contentDescription);
+        }
+        mScheduledEventStack.push(event);
+
+        // if the axis is the default let WebView handle the event which will
+        // result in cursor ring movement and selection of its content
+        if (axis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
+            return false;
+        }
+
+        webViewCore.sendMessage(EventHub.MODIFY_SELECTION, direction, axis);
+        return true;
+    }
+
+    /**
+     * Called when the <code>selectionString</code> has changed.
+     */
+    public void onSelectionStringChange(String selectionString) {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "Selection string: " + selectionString);
+        }
+        mIsLastSelectionStringNull = (selectionString == null);
+        if (mScheduledEventStack.isEmpty()) {
+            return;
+        }
+        AccessibilityEvent event = mScheduledEventStack.pop();
+        if ((event != null) && (selectionString != null)) {
+            event.getText().add(selectionString);
+            event.setFromIndex(0);
+            event.setToIndex(selectionString.length());
+            sendAccessibilityEvent(event);
+        }
+    }
+
+    /**
+     * Sends an {@link AccessibilityEvent}.
+     *
+     * @param event The event to send.
+     */
+    private void sendAccessibilityEvent(AccessibilityEvent event) {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "Dispatching: " + event);
+        }
+        // accessibility may be disabled while waiting for the selection string
+        AccessibilityManager accessibilityManager =
+            AccessibilityManager.getInstance(mWebView.getContext());
+        if (accessibilityManager.isEnabled()) {
+            accessibilityManager.sendAccessibilityEvent(event);
+        }
+    }
+
+    /**
+     * @return An accessibility event whose members are populated except its
+     *         text and content description.
+     */
+    private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) {
+        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        mWebViewInternal.onInitializeAccessibilityEvent(event);
+        return event;
+    }
+
+    /**
+     * Ensures that the Web content key bindings are loaded.
+     */
+    private void ensureWebContentKeyBindings() {
+        if (sBindings.size() > 0) {
+            return;
+        }
+
+        String webContentKeyBindingsString  = Settings.Secure.getString(
+                mWebView.getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
+
+        SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';');
+        semiColonSplitter.setString(webContentKeyBindingsString);
+
+        while (semiColonSplitter.hasNext()) {
+            String bindingString = semiColonSplitter.next();
+            if (TextUtils.isEmpty(bindingString)) {
+                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: "
+                        + webContentKeyBindingsString);
+                continue;
+            }
+            String[] keyValueArray = bindingString.split("=");
+            if (keyValueArray.length != 2) {
+                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " + bindingString);
+                continue;
+            }
+            try {
+                long keyCodeAndModifiers = Long.decode(keyValueArray[0].trim());
+                String[] actionStrings = keyValueArray[1].split(":");
+                int[] actions = new int[actionStrings.length];
+                for (int i = 0, count = actions.length; i < count; i++) {
+                    actions[i] = Integer.decode(actionStrings[i].trim());
+                }
+                sBindings.add(new AccessibilityWebContentKeyBinding(keyCodeAndModifiers, actions));
+            } catch (NumberFormatException nfe) {
+                Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString);
+            }
+        }
+    }
+
+    private boolean isEnterActionKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
+                || keyCode == KeyEvent.KEYCODE_ENTER
+                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
+    }
+
+    /**
+     * Represents a web content key-binding.
+     */
+    private static final class AccessibilityWebContentKeyBinding {
+
+        private static final int MODIFIERS_OFFSET = 32;
+        private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L;
+
+        private static final int KEY_CODE_OFFSET = 0;
+        private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL;
+
+        private static final int ACTION_OFFSET = 24;
+        private static final int ACTION_MASK = 0xFF000000;
+
+        private static final int FIRST_ARGUMENT_OFFSET = 16;
+        private static final int FIRST_ARGUMENT_MASK = 0x00FF0000;
+
+        private static final int SECOND_ARGUMENT_OFFSET = 8;
+        private static final int SECOND_ARGUMENT_MASK = 0x0000FF00;
+
+        private static final int THIRD_ARGUMENT_OFFSET = 0;
+        private static final int THIRD_ARGUMENT_MASK = 0x000000FF;
+
+        private final long mKeyCodeAndModifiers;
+
+        private final int [] mActionSequence;
+
+        /**
+         * @return The key code of the binding key.
+         */
+        public int getKeyCode() {
+            return (int) ((mKeyCodeAndModifiers & KEY_CODE_MASK) >> KEY_CODE_OFFSET);
+        }
+
+        /**
+         * @return The meta state of the binding key.
+         */
+        public int getModifiers() {
+            return (int) ((mKeyCodeAndModifiers & MODIFIERS_MASK) >> MODIFIERS_OFFSET);
+        }
+
+        /**
+         * @return The number of actions in the key binding.
+         */
+        public int getActionCount() {
+            return mActionSequence.length;
+        }
+
+        /**
+         * @param index The action for a given action <code>index</code>.
+         */
+        public int getAction(int index) {
+            return mActionSequence[index];
+        }
+
+        /**
+         * @param index The action code for a given action <code>index</code>.
+         */
+        public int getActionCode(int index) {
+            return (mActionSequence[index] & ACTION_MASK) >> ACTION_OFFSET;
+        }
+
+        /**
+         * @param index The first argument for a given action <code>index</code>.
+         */
+        public int getFirstArgument(int index) {
+            return (mActionSequence[index] & FIRST_ARGUMENT_MASK) >> FIRST_ARGUMENT_OFFSET;
+        }
+
+        /**
+         * @param index The second argument for a given action <code>index</code>.
+         */
+        public int getSecondArgument(int index) {
+            return (mActionSequence[index] & SECOND_ARGUMENT_MASK) >> SECOND_ARGUMENT_OFFSET;
+        }
+
+        /**
+         * @param index The third argument for a given action <code>index</code>.
+         */
+        public int getThirdArgument(int index) {
+            return (mActionSequence[index] & THIRD_ARGUMENT_MASK) >> THIRD_ARGUMENT_OFFSET;
+        }
+
+        /**
+         * Creates a new instance.
+         * @param keyCodeAndModifiers The key for the binding (key and modifiers).
+         * @param actionSequence The sequence of action for the binding.
+         */
+        public AccessibilityWebContentKeyBinding(long keyCodeAndModifiers, int[] actionSequence) {
+            mKeyCodeAndModifiers = keyCodeAndModifiers;
+            mActionSequence = actionSequence;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("modifiers: ");
+            builder.append(getModifiers());
+            builder.append(", keyCode: ");
+            builder.append(getKeyCode());
+            builder.append(", actions[");
+            for (int i = 0, count = getActionCount(); i < count; i++) {
+                builder.append("{actionCode");
+                builder.append(i);
+                builder.append(": ");
+                builder.append(getActionCode(i));
+                builder.append(", firstArgument: ");
+                builder.append(getFirstArgument(i));
+                builder.append(", secondArgument: ");
+                builder.append(getSecondArgument(i));
+                builder.append(", thirdArgument: ");
+                builder.append(getThirdArgument(i));
+                builder.append("}");
+            }
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+}
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 57628d3..f9f5b03 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.app.Activity;
 import android.app.SearchManager;
 import android.content.ClipboardManager;
 import android.content.Context;
@@ -122,6 +123,9 @@
                 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
                 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
                 i.putExtra(SearchManager.QUERY, mWebView.getSelection());
+                if (!(mWebView.getContext() instanceof Activity)) {
+                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                }
                 mWebView.getContext().startActivity(i);
                 break;
 
diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java
index 655db31..a22e6e8 100644
--- a/core/java/android/webkit/WebCoreThreadWatchdog.java
+++ b/core/java/android/webkit/WebCoreThreadWatchdog.java
@@ -26,6 +26,10 @@
 import android.os.Process;
 import android.webkit.WebViewCore.EventHub;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 // A Runnable that will monitor if the WebCore thread is still
 // processing messages by pinging it every so often. It is safe
 // to call the public methods of this class from any thread.
@@ -51,25 +55,31 @@
     // After the first timeout, use a shorter period before re-prompting the user.
     private static final int SUBSEQUENT_TIMEOUT_PERIOD = 15 * 1000;
 
-    private Context mContext;
     private Handler mWebCoreThreadHandler;
     private Handler mHandler;
     private boolean mPaused;
 
+    private Set<WebViewClassic> mWebViews;
+
     private static WebCoreThreadWatchdog sInstance;
 
-    public synchronized static WebCoreThreadWatchdog start(Context context,
-            Handler webCoreThreadHandler) {
+    public synchronized static WebCoreThreadWatchdog start(Handler webCoreThreadHandler) {
         if (sInstance == null) {
-            sInstance = new WebCoreThreadWatchdog(context, webCoreThreadHandler);
+            sInstance = new WebCoreThreadWatchdog(webCoreThreadHandler);
             new Thread(sInstance, "WebCoreThreadWatchdog").start();
         }
         return sInstance;
     }
 
-    public synchronized static void updateContext(Context context) {
+    public synchronized static void registerWebView(WebViewClassic w) {
         if (sInstance != null) {
-            sInstance.setContext(context);
+            sInstance.addWebView(w);
+        }
+    }
+
+    public synchronized static void unregisterWebView(WebViewClassic w) {
+        if (sInstance != null) {
+            sInstance.removeWebView(w);
         }
     }
 
@@ -85,12 +95,18 @@
         }
     }
 
-    private void setContext(Context context) {
-        mContext = context;
+    private void addWebView(WebViewClassic w) {
+        if (mWebViews == null) {
+            mWebViews = new HashSet<WebViewClassic>();
+        }
+        mWebViews.add(w);
     }
 
-    private WebCoreThreadWatchdog(Context context, Handler webCoreThreadHandler) {
-        mContext = context;
+    private void removeWebView(WebViewClassic w) {
+        mWebViews.remove(w);
+    }
+
+    private WebCoreThreadWatchdog(Handler webCoreThreadHandler) {
         mWebCoreThreadHandler = webCoreThreadHandler;
     }
 
@@ -147,39 +163,41 @@
                         break;
 
                     case TIMED_OUT:
-                        if ((mContext == null) || !(mContext instanceof Activity)) return;
-                        new AlertDialog.Builder(mContext)
-                            .setMessage(com.android.internal.R.string.webpage_unresponsive)
-                            .setPositiveButton(com.android.internal.R.string.force_close,
-                                    new DialogInterface.OnClickListener() {
-                                        @Override
-                                        public void onClick(DialogInterface dialog, int which) {
-                                        // User chose to force close.
-                                        Process.killProcess(Process.myPid());
+                        boolean postedDialog = false;
+                        synchronized (WebCoreThreadWatchdog.class) {
+                            Iterator<WebViewClassic> it = mWebViews.iterator();
+                            // Check each WebView we are aware of and find one that is capable of
+                            // showing the user a prompt dialog.
+                            while (it.hasNext()) {
+                                WebView activeView = it.next().getWebView();
+
+                                if (activeView.getWindowToken() != null &&
+                                        activeView.getViewRootImpl() != null) {
+                                    postedDialog = activeView.post(new PageNotRespondingRunnable(
+                                            activeView.getContext(), this));
+
+                                    if (postedDialog) {
+                                        // We placed the message into the UI thread for an attached
+                                        // WebView so we've made our best attempt to display the
+                                        // "page not responding" dialog to the user. Although the
+                                        // message is in the queue, there is no guarantee when/if
+                                        // the runnable will execute. In the case that the runnable
+                                        // never executes, the user will need to terminate the
+                                        // process manually.
+                                        break;
                                     }
-                                })
-                            .setNegativeButton(com.android.internal.R.string.wait,
-                                    new DialogInterface.OnClickListener() {
-                                        @Override
-                                        public void onClick(DialogInterface dialog, int which) {
-                                            // The user chose to wait. The last HEARTBEAT message
-                                            // will still be in the WebCore thread's queue, so all
-                                            // we need to do is post another TIMED_OUT so that the
-                                            // user will get prompted again if the WebCore thread
-                                            // doesn't sort itself out.
-                                            sendMessageDelayed(obtainMessage(TIMED_OUT),
-                                                    SUBSEQUENT_TIMEOUT_PERIOD);
-                                       }
-                                    })
-                            .setOnCancelListener(new DialogInterface.OnCancelListener() {
-                                    @Override
-                                    public void onCancel(DialogInterface dialog) {
-                                        sendMessageDelayed(obtainMessage(TIMED_OUT),
-                                                SUBSEQUENT_TIMEOUT_PERIOD);
-                                    }
-                            })
-                            .setIcon(android.R.drawable.ic_dialog_alert)
-                            .show();
+                                }
+                            }
+
+                            if (!postedDialog) {
+                                // There's no active webview we can use to show the dialog, so
+                                // wait again. If we never get a usable view, the user will
+                                // never get the chance to terminate the process, and will
+                                // need to do it manually.
+                                sendMessageDelayed(obtainMessage(TIMED_OUT),
+                                        SUBSEQUENT_TIMEOUT_PERIOD);
+                            }
+                        }
                         break;
                     }
                 }
@@ -205,4 +223,55 @@
 
         Looper.loop();
     }
+
+    private class PageNotRespondingRunnable implements Runnable {
+        Context mContext;
+        private Handler mWatchdogHandler;
+
+        public PageNotRespondingRunnable(Context context, Handler watchdogHandler) {
+            mContext = context;
+            mWatchdogHandler = watchdogHandler;
+        }
+
+        @Override
+        public void run() {
+            // This must run on the UI thread as it is displaying an AlertDialog.
+            assert Looper.getMainLooper().getThread() == Thread.currentThread();
+            new AlertDialog.Builder(mContext)
+                    .setMessage(com.android.internal.R.string.webpage_unresponsive)
+                    .setPositiveButton(com.android.internal.R.string.force_close,
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    // User chose to force close.
+                                    Process.killProcess(Process.myPid());
+                                }
+                            })
+                    .setNegativeButton(com.android.internal.R.string.wait,
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    // The user chose to wait. The last HEARTBEAT message
+                                    // will still be in the WebCore thread's queue, so all
+                                    // we need to do is post another TIMED_OUT so that the
+                                    // user will get prompted again if the WebCore thread
+                                    // doesn't sort itself out.
+                                    mWatchdogHandler.sendMessageDelayed(
+                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
+                                            SUBSEQUENT_TIMEOUT_PERIOD);
+                                }
+                            })
+                    .setOnCancelListener(
+                            new DialogInterface.OnCancelListener() {
+                                @Override
+                                public void onCancel(DialogInterface dialog) {
+                                    mWatchdogHandler.sendMessageDelayed(
+                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
+                                            SUBSEQUENT_TIMEOUT_PERIOD);
+                                }
+                            })
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .show();
+        }
+    }
 }
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 f4796d5..58dd82e 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -60,9 +60,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.security.KeyChain;
-import android.speech.tts.TextToSpeech;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.Selection;
@@ -102,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;
@@ -276,6 +273,7 @@
                 newCursorPosition -= text.length() - limitedText.length();
             }
             super.setComposingText(limitedText, newCursorPosition);
+            updateSelection();
             if (limitedText != text) {
                 restartInput();
                 int lastCaret = start + limitedText.length();
@@ -288,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);
         }
 
@@ -413,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);
+            }
         }
 
         /**
@@ -431,14 +494,18 @@
             boolean isCharacterDelete = false;
             int textLength = text.length();
             int originalLength = original.length();
-            if (textLength > originalLength) {
-                isCharacterAdd = (textLength == originalLength + 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                originalLength);
-            } else if (originalLength > textLength) {
-                isCharacterDelete = (textLength == originalLength - 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                textLength);
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            if (selectionStart == selectionEnd) {
+                if (textLength > originalLength) {
+                    isCharacterAdd = (textLength == originalLength + 1)
+                            && TextUtils.regionMatches(text, 0, original, 0,
+                                    originalLength);
+                } else if (originalLength > textLength) {
+                    isCharacterDelete = (textLength == originalLength - 1)
+                            && TextUtils.regionMatches(text, 0, original, 0,
+                                    textLength);
+                }
             }
             if (isCharacterAdd) {
                 sendCharacter(text.charAt(textLength - 1));
@@ -867,15 +934,9 @@
     private static final int MOTIONLESS_IGNORE          = 3;
     private int mHeldMotionless;
 
-    // An instance for injecting accessibility in WebViews with disabled
-    // JavaScript or ones for which no accessibility script exists
+    // Lazily-instantiated instance for injecting accessibility.
     private AccessibilityInjector mAccessibilityInjector;
 
-    // flag indicating if accessibility script is injected so we
-    // know to handle Shift and arrows natively first
-    private boolean mAccessibilityScriptInjected;
-
-
     /**
      * How long the caret handle will last without being touched.
      */
@@ -934,7 +995,6 @@
     private static final int RELEASE_SINGLE_TAP         = 5;
     private static final int REQUEST_FORM_DATA          = 6;
     private static final int DRAG_HELD_MOTIONLESS       = 8;
-    private static final int AWAKEN_SCROLL_BARS         = 9;
     private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
     private static final int SCROLL_SELECT_TEXT         = 11;
 
@@ -1002,7 +1062,7 @@
         "REQUEST_FORM_DATA", //              = 6;
         "RESUME_WEBCORE_PRIORITY", //        = 7;
         "DRAG_HELD_MOTIONLESS", //           = 8;
-        "AWAKEN_SCROLL_BARS", //             = 9;
+        "", //             = 9;
         "PREVENT_DEFAULT_TIMEOUT", //        = 10;
         "SCROLL_SELECT_TEXT" //              = 11;
     };
@@ -1085,34 +1145,6 @@
     private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
     private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
 
-    // constants for determining script injection strategy
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
-
-    // the alias via which accessibility JavaScript interface is exposed
-    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
-
-    // Template for JavaScript that injects a screen-reader.
-    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
-        "javascript:(function() {" +
-        "    var chooser = document.createElement('script');" +
-        "    chooser.type = 'text/javascript';" +
-        "    chooser.src = '%1s';" +
-        "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
-        "  })();";
-
-    // Regular expression that matches the "axs" URL parameter.
-    // The value of 0 means the accessibility script is opted out
-    // The value of 1 means the accessibility script is already injected
-    private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
-
-    // TextToSpeech instance exposed to JavaScript to the injected screenreader.
-    private TextToSpeech mTextToSpeech;
-
-    // variable to cache the above pattern in case accessibility is enabled.
-    private Pattern mMatchAxsUrlParameterPattern;
-
     /**
      * Max distance to overscroll by in pixels.
      * This how far content can be pulled beyond its normal bounds by the user.
@@ -1598,8 +1630,6 @@
     private void init() {
         OnTrimMemoryListener.init(mContext);
         mWebView.setWillNotDraw(false);
-        mWebView.setFocusable(true);
-        mWebView.setFocusableInTouchMode(true);
         mWebView.setClickable(true);
         mWebView.setLongClickable(true);
 
@@ -1633,43 +1663,66 @@
         return true;
     }
 
-    /**
-     * Adds accessibility APIs to JavaScript.
-     *
-     * Note: This method is responsible to performing the necessary
-     *       check if the accessibility APIs should be exposed.
-     */
-    private void addAccessibilityApisToJavaScript() {
-        if (AccessibilityManager.getInstance(mContext).isEnabled()
-                && getSettings().getJavaScriptEnabled()) {
-            // exposing the TTS for now ...
-            final Context ctx = mContext;
-            if (ctx != null) {
-                final String packageName = ctx.getPackageName();
-                if (packageName != null) {
-                    mTextToSpeech = new TextToSpeech(ctx, null, null,
-                            packageName + ".**webview**", true);
-                    addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+    @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 (getAccessibilityInjector().supportsAccessibilityAction(action)) {
+            return getAccessibilityInjector().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;
             }
         }
-    }
 
-    /**
-     * Removes accessibility APIs from JavaScript.
-     */
-    private void removeAccessibilityApisFromJavaScript() {
-        // exposing the TTS for now ...
-        if (mTextToSpeech != null) {
-            removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
-            mTextToSpeech.shutdown();
-            mTextToSpeech = null;
-        }
+        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);
+        }
+
+        getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info);
     }
 
     @Override
@@ -1687,6 +1740,17 @@
         event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
     }
 
+    private boolean isAccessibilityEnabled() {
+        return AccessibilityManager.getInstance(mContext).isEnabled();
+    }
+
+    private AccessibilityInjector getAccessibilityInjector() {
+        if (mAccessibilityInjector == null) {
+            mAccessibilityInjector = new AccessibilityInjector(this);
+        }
+        return mAccessibilityInjector;
+    }
+
     private boolean isScrollableForAccessibility() {
         return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
                 - mWebView.getPaddingRight()
@@ -1716,6 +1780,10 @@
         mZoomManager.updateDefaultZoomDensity(density);
     }
 
+    /* package */ int getScaledNavSlop() {
+        return viewToContentDimension(mNavSlop);
+    }
+
     /* package */ boolean onSavePassword(String schemePlusHost, String username,
             String password, final Message resumeMsg) {
         boolean rVal = false;
@@ -2001,6 +2069,20 @@
     }
 
     private void destroyImpl() {
+        ViewRootImpl viewRoot = mWebView.getViewRootImpl();
+        if (viewRoot != null) {
+            Log.e(LOGTAG, "Error: WebView.destroy() called while still attached!");
+        }
+
+        if (mWebView.isHardwareAccelerated()) {
+            int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
+            if (drawGLFunction != 0 && viewRoot != null) {
+                // functor should have been detached in onDetachedFromWindow, do
+                // additionally here for safety
+                viewRoot.detachFunctor(drawGLFunction);
+            }
+        }
+
         mCallbackProxy.blockMessages();
         clearHelpers();
         if (mListBoxDialog != null) {
@@ -2939,6 +3021,7 @@
 
         // premature data from webkit, ignore
         if ((w | h) == 0) {
+            invalidate();
             return;
         }
 
@@ -2951,10 +3034,7 @@
             // updated when we get out of that mode.
             if (!mDrawHistory) {
                 // repin our scroll, taking into account the new content size
-                if (updateScrollCoordinates(pinLocX(getScrollX()),
-                        pinLocY(getScrollY()))) {
-                    invalidate();
-                }
+                updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY()));
                 if (!mScroller.isFinished()) {
                     // We are in the middle of a scroll.  Repin the final scroll
                     // position.
@@ -2962,6 +3042,7 @@
                     mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
                 }
             }
+            invalidate();
         }
         contentSizeChanged(updateLayout);
     }
@@ -3365,11 +3446,6 @@
                 nativeSetPauseDrawing(mNativeClass, false);
             }
         }
-        // Ensure that the watchdog has a currently valid Context to be able to display
-        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
-        // thread was blocked and the Activity is started again, we may reuse the blocked
-        // thread, but we'll have a new Activity.
-        WebCoreThreadWatchdog.updateContext(mContext);
         // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
         // to ensure that the Watchdog thread is running for the new WebView, so call
         // it outside the if block above.
@@ -3772,7 +3848,6 @@
             //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
             mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
                     animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
-            mWebViewPrivate.awakenScrollBars(mScroller.getDuration());
             invalidate();
         } else {
             mWebView.scrollTo(x, y);
@@ -3825,7 +3900,9 @@
 
         // reset the flag since we set to true in if need after
         // loading is see onPageFinished(Url)
-        mAccessibilityScriptInjected = false;
+        if (isAccessibilityEnabled()) {
+            getAccessibilityInjector().onPageStarted(url);
+        }
 
         // Don't start out editing.
         mIsEditingText = false;
@@ -3837,114 +3914,10 @@
      */
     /* package */ void onPageFinished(String url) {
         mZoomManager.onPageFinished(url);
-        injectAccessibilityForUrl(url);
-    }
 
-    /**
-     * This method injects accessibility in the loaded document if accessibility
-     * is enabled. If JavaScript is enabled we try to inject a URL specific script.
-     * If no URL specific script is found or JavaScript is disabled we fallback to
-     * the default {@link AccessibilityInjector} implementation.
-     * </p>
-     * If the URL has the "axs" paramter set to 1 it has already done the
-     * script injection so we do nothing. If the parameter is set to 0
-     * the URL opts out accessibility script injection so we fall back to
-     * the default {@link AccessibilityInjector}.
-     * </p>
-     * Note: If the user has not opted-in the accessibility script injection no scripts
-     * are injected rather the default {@link AccessibilityInjector} implementation
-     * is used.
-     *
-     * @param url The URL loaded by this {@link WebView}.
-     */
-    private void injectAccessibilityForUrl(String url) {
-        if (mWebViewCore == null) {
-            return;
+        if (isAccessibilityEnabled()) {
+            getAccessibilityInjector().onPageFinished(url);
         }
-        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-
-        if (!accessibilityManager.isEnabled()) {
-            // it is possible that accessibility was turned off between reloads
-            ensureAccessibilityScriptInjectorInstance(false);
-            return;
-        }
-
-        if (!getSettings().getJavaScriptEnabled()) {
-            // no JS so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-            return;
-        }
-
-        // check the URL "axs" parameter to choose appropriate action
-        int axsParameterValue = getAxsUrlParameterValue(url);
-        if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
-            boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
-                    .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
-            if (onDeviceScriptInjectionEnabled) {
-                ensureAccessibilityScriptInjectorInstance(false);
-                // neither script injected nor script injection opted out => we inject
-                mWebView.loadUrl(getScreenReaderInjectingJs());
-                // TODO: Set this flag after successfull script injection. Maybe upon injection
-                // the chooser should update the meta tag and we check it to declare success
-                mAccessibilityScriptInjected = true;
-            } else {
-                // injection disabled so we fallback to the basic built-in support
-                ensureAccessibilityScriptInjectorInstance(true);
-            }
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
-            // injection opted out so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
-            ensureAccessibilityScriptInjectorInstance(false);
-            // the URL provides accessibility but we still need to add our generic script
-            mWebView.loadUrl(getScreenReaderInjectingJs());
-        } else {
-            Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
-        }
-    }
-
-    /**
-     * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
-     *
-     * @param present True to ensure an insance, false to ensure no instance.
-     */
-    private void ensureAccessibilityScriptInjectorInstance(boolean present) {
-        if (present) {
-            if (mAccessibilityInjector == null) {
-                mAccessibilityInjector = new AccessibilityInjector(this);
-            }
-        } else {
-            mAccessibilityInjector = null;
-        }
-    }
-
-    /**
-     * Gets JavaScript that injects a screen-reader.
-     *
-     * @return The JavaScript snippet.
-     */
-    private String getScreenReaderInjectingJs() {
-        String screenReaderUrl = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
-        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
-    }
-
-    /**
-     * Gets the "axs" URL parameter value.
-     *
-     * @param url A url to fetch the paramter from.
-     * @return The parameter value if such, -1 otherwise.
-     */
-    private int getAxsUrlParameterValue(String url) {
-        if (mMatchAxsUrlParameterPattern == null) {
-            mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
-        }
-        Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
-        if (matcher.find()) {
-            String keyValuePair = url.substring(matcher.start(), matcher.end());
-            return Integer.parseInt(keyValuePair.split("=")[1]);
-        }
-        return -1;
     }
 
     // scale from content to view coordinates, and pin
@@ -4155,15 +4128,11 @@
         if (mTouchMode == TOUCH_DRAG_MODE) {
             if (mHeldMotionless == MOTIONLESS_PENDING) {
                 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
                 mHeldMotionless = MOTIONLESS_FALSE;
             }
             if (mHeldMotionless == MOTIONLESS_FALSE) {
                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
                         .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
                 mHeldMotionless = MOTIONLESS_PENDING;
             }
         }
@@ -4339,10 +4308,6 @@
     }
 
     private void removeTouchHighlight() {
-        if (mWebViewCore != null) {
-            mWebViewCore.removeMessages(EventHub.HIT_TEST);
-        }
-        mPrivateHandler.removeMessages(HIT_TEST_RESULT);
         setTouchHighlightRects(null);
     }
 
@@ -4910,30 +4875,10 @@
             return false;
         }
 
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // 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 true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
+        // See if the accessibility injector needs to handle this event.
+        if (isAccessibilityEnabled()
+                && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
+            return true;
         }
 
         if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
@@ -5037,30 +4982,10 @@
             return false;
         }
 
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // 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 true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
+        // See if the accessibility injector needs to handle this event.
+        if (isAccessibilityEnabled()
+                && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
+            return true;
         }
 
         if (isEnterActionKey(keyCode)) {
@@ -5176,6 +5101,7 @@
 
     private void adjustSelectionCursors() {
         if (mIsCaretSelection) {
+            syncSelectionCursors();
             return; // no need to swap left and right handles.
         }
 
@@ -5361,7 +5287,9 @@
     public void onAttachedToWindow() {
         if (mWebView.hasWindowFocus()) setActive(true);
 
-        addAccessibilityApisToJavaScript();
+        if (isAccessibilityEnabled()) {
+            getAccessibilityInjector().addAccessibilityApisIfNecessary();
+        }
 
         updateHwAccelerated();
     }
@@ -5372,7 +5300,14 @@
         mZoomManager.dismissZoomPicker();
         if (mWebView.hasWindowFocus()) setActive(false);
 
-        removeAccessibilityApisFromJavaScript();
+        if (isAccessibilityEnabled()) {
+            getAccessibilityInjector().removeAccessibilityApisIfNecessary();
+        } else {
+            // Ensure the injector is cleared if we're detaching from the window
+            // and accessibility is disabled.
+            mAccessibilityInjector = null;
+        }
+
         updateHwAccelerated();
 
         if (mWebView.isHardwareAccelerated()) {
@@ -5584,6 +5519,7 @@
             return; // no need to scroll
         }
         syncSelectionCursors();
+        nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect);
         final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER));
         Rect showRect = new Rect(
                 Math.max(0, mEditTextContentBounds.left - buffer),
@@ -5616,17 +5552,19 @@
             return; // no need to scroll
         }
 
-        int scrollX = visibleRect.left;
+        int scrollX = viewToContentX(getScrollX());
         if (visibleRect.left > showRect.left) {
-            scrollX = showRect.left;
+            // We are scrolled too far
+            scrollX += showRect.left - visibleRect.left;
         } else if (visibleRect.right < showRect.right) {
-            scrollX = Math.max(0, showRect.right - visibleRect.width());
+            // We aren't scrolled enough to include the right
+            scrollX += showRect.right - visibleRect.right;
         }
-        int scrollY = visibleRect.top;
+        int scrollY = viewToContentY(getScrollY());
         if (visibleRect.top > showRect.top) {
-            scrollY = showRect.top;
+            scrollY += showRect.top - visibleRect.top;
         } else if (visibleRect.bottom < showRect.bottom) {
-            scrollY = Math.max(0, showRect.bottom - visibleRect.height());
+            scrollY += showRect.bottom - visibleRect.bottom;
         }
 
         contentScrollTo(scrollX, scrollY, false);
@@ -5768,14 +5706,15 @@
             return false;
         }
 
-        if (!mWebView.isFocused()) {
-            mWebView.requestFocus();
-        }
-
         if (mInputDispatcher == null) {
             return false;
         }
 
+        if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode()
+                && !mWebView.isFocused()) {
+            mWebView.requestFocus();
+        }
+
         if (mInputDispatcher.postPointerEvent(ev, getScrollX(),
                 getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) {
             mInputDispatcher.dispatchUiEvents();
@@ -5817,7 +5756,6 @@
         switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 mConfirmMove = false;
-                mInitialHitTestResult = null;
                 if (!mEditTextScroller.isFinished()) {
                     mEditTextScroller.abortAnimation();
                 }
@@ -5839,23 +5777,6 @@
                     }
                 } else { // the normal case
                     mTouchMode = TOUCH_INIT_MODE;
-                    // TODO: Have WebViewInputDispatch handle this
-                    TouchHighlightData data = new TouchHighlightData();
-                    data.mX = contentX;
-                    data.mY = contentY;
-                    data.mNativeLayerRect = new Rect();
-                    if (mNativeClass != 0) {
-                        data.mNativeLayer = nativeScrollableLayer(mNativeClass,
-                                contentX, contentY, data.mNativeLayerRect, null);
-                    } else {
-                        data.mNativeLayer = 0;
-                    }
-                    data.mSlop = viewToContentDimension(mNavSlop);
-                    removeTouchHighlight();
-                    if (!mBlockWebkitViewMessages && mWebViewCore != null) {
-                        mWebViewCore.sendMessageAtFrontOfQueue(
-                                EventHub.HIT_TEST, data);
-                    }
                     if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
                                 (eventTime - mLastTouchUpTime), eventTime);
@@ -6067,27 +5988,6 @@
                     }
                 }
 
-                // Turn off scrollbars when dragging a layer.
-                if (keepScrollBarsVisible &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE &&
-                        mTouchMode != TOUCH_DRAG_TEXT_MODE) {
-                    if (mHeldMotionless != MOTIONLESS_TRUE) {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        invalidate();
-                    }
-                    // keep the scrollbar on the screen even there is no scroll
-                    mWebViewPrivate.awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
-                            false);
-                    // Post a message so that we'll keep them alive while we're not scrolling.
-                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                            .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
-                    // return false to indicate that we can't pan out of the
-                    // view space
-                    return;
-                } else {
-                    mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                }
                 break;
             }
             case MotionEvent.ACTION_UP: {
@@ -6135,7 +6035,6 @@
                     case TOUCH_DRAG_LAYER_MODE:
                     case TOUCH_DRAG_TEXT_MODE:
                         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
                         // if the user waits a while w/o moving before the
                         // up, we don't want to do a fling
                         if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
@@ -6401,7 +6300,6 @@
         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
         removeTouchHighlight();
         mHeldMotionless = MOTIONLESS_TRUE;
         mTouchMode = TOUCH_DONE_MODE;
@@ -6833,17 +6731,6 @@
         // no horizontal overscroll if the content just fits
         mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
                 maxX == 0 ? 0 : overflingDistance, overflingDistance);
-        // Duration is calculated based on velocity. With range boundaries and overscroll
-        // we may not know how long the final animation will take. (Hence the deprecation
-        // warning on the call below.) It's not a big deal for scroll bars but if webcore
-        // resumes during this effect we will take a performance hit. See computeScroll;
-        // we resume webcore there when the animation is finished.
-        final int time = mScroller.getDuration();
-
-        // Suppress scrollbars for layer scrolling.
-        if (mTouchMode != TOUCH_DRAG_LAYER_MODE && mTouchMode != TOUCH_DRAG_TEXT_MODE) {
-            mWebViewPrivate.awakenScrollBars(time);
-        }
 
         invalidate();
     }
@@ -7381,17 +7268,6 @@
                 case DRAG_HELD_MOTIONLESS:
                     mHeldMotionless = MOTIONLESS_TRUE;
                     invalidate();
-                    // fall through to keep scrollbars awake
-
-                case AWAKEN_SCROLL_BARS:
-                    if (mTouchMode == TOUCH_DRAG_MODE
-                            && mHeldMotionless == MOTIONLESS_TRUE) {
-                        mWebViewPrivate.awakenScrollBars(ViewConfiguration
-                                .getScrollDefaultDelay(), false);
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(AWAKEN_SCROLL_BARS),
-                                ViewConfiguration.getScrollDefaultDelay());
-                    }
                     break;
 
                 case SCREEN_ON:
@@ -7486,9 +7362,9 @@
                     break;
 
                 case SELECTION_STRING_CHANGED:
-                    if (mAccessibilityInjector != null) {
-                        String selectionString = (String) msg.obj;
-                        mAccessibilityInjector.onSelectionStringChange(selectionString);
+                    if (isAccessibilityEnabled()) {
+                        getAccessibilityInjector()
+                                .handleSelectionChangedIfNecessary((String) msg.obj);
                     }
                     break;
 
@@ -7557,6 +7433,7 @@
                     int cursorPosition = start + text.length();
                     replaceTextfieldText(start, end, text,
                             cursorPosition, cursorPosition);
+                    selectionDone();
                     break;
                 }
 
@@ -7583,7 +7460,9 @@
                 }
 
                 case CLEAR_CARET_HANDLE:
-                    selectionDone();
+                    if (mIsCaretSelection) {
+                        selectionDone();
+                    }
                     break;
 
                 case KEY_PRESS:
@@ -7671,6 +7550,11 @@
                 invalidate();
             }
         }
+
+        @Override
+        public void clearPreviousHitTest() {
+            setHitTestResult(null);
+        }
     }
 
     private void setHitTestTypeFromUrl(String url) {
@@ -7956,7 +7840,8 @@
         }
 
         // update the zoom information based on the new picture
-        mZoomManager.onNewPicture(draw);
+        if (mZoomManager.onNewPicture(draw))
+            invalidate();
 
         if (isPictureAfterFirstLayout) {
             mViewManager.postReadyToDrawAll();
@@ -7986,7 +7871,9 @@
         }
         nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
 
-        if (data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR) {
+        if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR)
+                || (!mSelectingText
+                        && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) {
             selectionDone();
             mShowTextSelectionExtra = true;
             invalidate();
@@ -8041,7 +7928,7 @@
         mIsBatchingTextChanges = false;
     }
 
-    private void sendBatchableInputMessage(int what, int arg1, int arg2,
+    void sendBatchableInputMessage(int what, int arg1, int arg2,
             Object obj) {
         if (mWebViewCore == null) {
             return;
@@ -8462,16 +8349,6 @@
     }
 
     /**
-     * @return Whether accessibility script has been injected.
-     */
-    private boolean accessibilityScriptInjected() {
-        // TODO: Maybe the injected script should announce its presence in
-        // the page meta-tag so the nativePageShouldHandleShiftAndArrows
-        // will check that as one of the conditions it looks for
-        return mAccessibilityScriptInjected;
-    }
-
-    /**
      * See {@link WebView#setBackgroundColor(int)}
      */
     @Override
@@ -8677,4 +8554,6 @@
             Rect rect);
     // Returns 1 if a layer sync is needed, else 0
     private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
+    private static native void nativeFindMaxVisibleRect(int instance, int layerId,
+            Rect visibleContentRect);
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4adfd6a..af7914e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -22,7 +22,6 @@
 import android.database.Cursor;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.media.MediaFile;
 import android.net.ProxyProperties;
 import android.net.Uri;
@@ -179,8 +178,10 @@
 
                 // Start the singleton watchdog which will monitor the WebCore thread
                 // to verify it's still processing messages.
-                WebCoreThreadWatchdog.start(context, sWebCoreHandler);
+                WebCoreThreadWatchdog.start(sWebCoreHandler);
             }
+            // Make sure the Watchdog is aware of this new WebView.
+            WebCoreThreadWatchdog.registerWebView(w);
         }
         // Create an EventHub to handle messages before and after the thread is
         // ready.
@@ -864,6 +865,7 @@
     static class TextSelectionData {
         static final int REASON_UNKNOWN = 0;
         static final int REASON_ACCESSIBILITY_INJECTOR = 1;
+        static final int REASON_SELECT_WORD = 2;
         public TextSelectionData(int start, int end, int selectTextPtr) {
             mStart = start;
             mEnd = end;
@@ -1143,8 +1145,6 @@
         static final int ADD_PACKAGE_NAME = 185;
         static final int REMOVE_PACKAGE_NAME = 186;
 
-        static final int HIT_TEST = 187;
-
         // accessibility support
         static final int MODIFY_SELECTION = 190;
 
@@ -1648,18 +1648,6 @@
                                     (Set<String>) msg.obj);
                             break;
 
-                        case HIT_TEST:
-                            TouchHighlightData d = (TouchHighlightData) msg.obj;
-                            if (d.mNativeLayer != 0) {
-                                nativeScrollLayer(mNativeClass,
-                                        d.mNativeLayer, d.mNativeLayerRect);
-                            }
-                            WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true);
-                            mWebViewClassic.mPrivateHandler.obtainMessage(
-                                    WebViewClassic.HIT_TEST_RESULT, hit)
-                                    .sendToTarget();
-                            break;
-
                         case SET_USE_MOCK_DEVICE_ORIENTATION:
                             setUseMockDeviceOrientation();
                             break;
@@ -1718,12 +1706,16 @@
                             break;
                         }
                         case SELECT_WORD_AT: {
+                            mTextSelectionChangeReason
+                                    = TextSelectionData.REASON_SELECT_WORD;
                             int x = msg.arg1;
                             int y = msg.arg2;
                             if (!nativeSelectWordAt(mNativeClass, x, y)) {
                                 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE)
                                     .sendToTarget();
                             }
+                            mTextSelectionChangeReason
+                                    = TextSelectionData.REASON_UNKNOWN;
                             break;
                         }
                         case SELECT_ALL:
@@ -1788,6 +1780,15 @@
                 return false;
             }
             switch (eventType) {
+                case WebViewInputDispatcher.EVENT_TYPE_HIT_TEST:
+                    int x = Math.round(event.getX());
+                    int y = Math.round(event.getY());
+                    WebKitHitTest hit = performHitTest(x, y,
+                            mWebViewClassic.getScaledNavSlop(), true);
+                    mWebViewClassic.mPrivateHandler.obtainMessage(
+                            WebViewClassic.HIT_TEST_RESULT, hit).sendToTarget();
+                    return false;
+
                 case WebViewInputDispatcher.EVENT_TYPE_CLICK:
                     return nativeMouseClick(mNativeClass);
 
@@ -1980,6 +1981,7 @@
             mEventHub.sendMessageAtFrontOfQueue(
                     Message.obtain(null, EventHub.DESTROY));
             mEventHub.blockMessages();
+            WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic);
         }
     }
 
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index 9328d8c..feff16e 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -204,6 +204,11 @@
     public static final int EVENT_TYPE_DOUBLE_TAP = 5;
 
     /**
+     * Event type: Indicates that a hit test should be performed
+     */
+    public static final int EVENT_TYPE_HIT_TEST = 6;
+
+    /**
      * Flag: This event is private to this queue.  Do not forward it.
      */
     public static final int FLAG_PRIVATE = 1 << 0;
@@ -394,7 +399,6 @@
         unscheduleHideTapHighlightLocked();
         unscheduleShowTapHighlightLocked();
         mUiCallbacks.showTapHighlight(true);
-        scheduleHideTapHighlightLocked();
     }
 
     private void scheduleShowTapHighlightLocked() {
@@ -461,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);
@@ -499,13 +503,18 @@
     }
 
     private void enqueueDoubleTapLocked(MotionEvent event) {
-        unscheduleClickLocked();
-        hideTapCandidateLocked();
         MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
         DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_DOUBLE_TAP, 0,
                 mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
         enqueueEventLocked(d);
-        mIsDoubleTapCandidate = false;
+    }
+
+    private void enqueueHitTestLocked(MotionEvent event) {
+        mUiCallbacks.clearPreviousHitTest();
+        MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
+        DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0,
+                mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
+        enqueueEventLocked(d);
     }
 
     private void checkForSlopLocked(MotionEvent event) {
@@ -544,11 +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();
@@ -656,6 +671,10 @@
                 if (event != null && recycleEvent) {
                     event.recycle();
                 }
+
+                if (eventType == EVENT_TYPE_CLICK) {
+                    scheduleHideTapHighlightLocked();
+                }
             }
         }
     }
@@ -792,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.
@@ -812,20 +835,31 @@
     }
 
     private void enqueueEventLocked(DispatchEvent d) {
-        if (!shouldSkipWebKit(d.mEventType)) {
+        if (!shouldSkipWebKit(d)) {
             enqueueWebKitEventLocked(d);
         } else {
             enqueueUiEventLocked(d);
         }
     }
 
-    private boolean shouldSkipWebKit(int eventType) {
-        switch (eventType) {
+    private boolean shouldSkipWebKit(DispatchEvent d) {
+        switch (d.mEventType) {
             case EVENT_TYPE_CLICK:
             case EVENT_TYPE_HOVER:
             case EVENT_TYPE_SCROLL:
+            case EVENT_TYPE_HIT_TEST:
                 return false;
             case EVENT_TYPE_TOUCH:
+                // TODO: This should be cleaned up. We now have WebViewInputDispatcher
+                // and WebViewClassic both checking for slop and doing their own
+                // thing - they should be consolidated. And by consolidated, I mean
+                // WebViewClassic's version should just be deleted.
+                // The reason this is done is because webpages seem to expect
+                // that they only get an ontouchmove if the slop has been exceeded.
+                if (mIsTapCandidate && d.mEvent != null
+                        && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
+                    return true;
+                }
                 return !mPostSendTouchEventsToWebKit
                         || mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
         }
@@ -1029,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/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 2247678..1da59e4 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1008,8 +1008,10 @@
     /**
      * Updates zoom values when Webkit produces a new picture. This method
      * should only be called from the UI thread's message handler.
+     *
+     * @return True if zoom value has changed
      */
-    public void onNewPicture(WebViewCore.DrawData drawData) {
+    public boolean onNewPicture(WebViewCore.DrawData drawData) {
         final int viewWidth = mWebView.getViewWidth();
         final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
         final float newZoomOverviewScale = getZoomOverviewScale();
@@ -1056,6 +1058,8 @@
             // so next new picture could be forced into overview mode if it's true.
             mInitialZoomOverview = mInZoomOverview;
         }
+
+        return scaleHasDiff;
     }
 
     /**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ab9d370..6cee0f3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1352,24 +1352,23 @@
             case ACCESSIBILITY_FOCUS_FORWARD: {
                 ViewRootImpl viewRootImpl = getViewRootImpl();
                 if (viewRootImpl == null) {
-                    break;
+                    return null;
                 }
                 View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
                 if (currentFocus == null) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // If we have the focus try giving it to the first child.
                 if (currentFocus == this) {
-                    final int firstVisiblePosition = getFirstVisiblePosition();
-                    if (firstVisiblePosition >= 0) {
+                    if (getChildCount() > 0) {
                         return getChildAt(0);
                     }
-                    return null;
+                    return super.focusSearch(this, direction);
                 }
                 // Find the item that has accessibility focus.
                 final int currentPosition = getPositionForView(currentFocus);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // Try to advance focus in the current item.
                 View currentItem = getChildAt(currentPosition - getFirstVisiblePosition());
@@ -1386,25 +1385,31 @@
                 final int nextPosition = currentPosition - getFirstVisiblePosition() + 1;
                 if (nextPosition < getChildCount()) {
                     return getChildAt(nextPosition);
+                } else {
+                    return super.focusSearch(this, direction);
                 }
-            } break;
+            }
             case ACCESSIBILITY_FOCUS_BACKWARD: {
                 ViewRootImpl viewRootImpl = getViewRootImpl();
                 if (viewRootImpl == null) {
-                    break;
+                    return null;
                 }
                 View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
                 if (currentFocus == null) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // If we have the focus do a generic search.
                 if (currentFocus == this) {
+                    final int lastChildIndex = getChildCount() - 1;
+                    if (lastChildIndex >= 0) {
+                        return getChildAt(lastChildIndex);
+                    }
                     return super.focusSearch(this, direction);
                 }
                 // Find the item that has accessibility focus.
                 final int currentPosition = getPositionForView(currentFocus);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
-                    break;
+                    return super.focusSearch(this, direction);
                 }
                 // Try to advance focus in the current item.
                 View currentItem = getChildAt(currentPosition - getFirstVisiblePosition());
@@ -1422,7 +1427,7 @@
                 if (nextPosition >= 0) {
                     return getChildAt(nextPosition);
                 } else {
-                    return this;
+                    return super.focusSearch(this, direction);
                 }
             }
         }
@@ -1451,7 +1456,7 @@
             final int lastVisiblePosition = getLastVisiblePosition();
             if (mLastAccessibilityScrollEventFromIndex == firstVisiblePosition
                     && mLastAccessibilityScrollEventToIndex == lastVisiblePosition) {
-                return;   
+                return;
             } else {
                 mLastAccessibilityScrollEventFromIndex = firstVisiblePosition;
                 mLastAccessibilityScrollEventToIndex = lastVisiblePosition;
@@ -1470,33 +1475,38 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(AbsListView.class.getName());
-        if (getFirstVisiblePosition() > 0) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-        }
-        if (getLastVisiblePosition() < getCount() - 1) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        if (isEnabled()) {
+            if (getFirstVisiblePosition() > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+            if (getLastVisiblePosition() < getCount() - 1) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
         }
     }
 
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                if (getLastVisiblePosition() < getCount() - 1) {
+                if (isEnabled() && getLastVisiblePosition() < getCount() - 1) {
                     final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
                     smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
                     return true;
                 }
             } return false;
             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                if (mFirstPosition > 0) {
+                if (isEnabled() && mFirstPosition > 0) {
                     final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
                     smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
                     return true;
                 }
             } return false;
         }
-        return super.performAccessibilityAction(action, arguments);
+        return false;
     }
 
     /**
@@ -2211,31 +2221,17 @@
 
         View child;
         if (scrapView != null) {
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP,
-                        position, -1);
-            }
-
             child = mAdapter.getView(position, scrapView, this);
 
             if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                 child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
             }
 
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
-                        position, getChildCount());
-            }
-
             if (child != scrapView) {
                 mRecycler.addScrapView(scrapView, position);
                 if (mCacheColorHint != 0) {
                     child.setDrawingCacheBackgroundColor(mCacheColorHint);
                 }
-                if (ViewDebug.TRACE_RECYCLER) {
-                    ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                            position, -1);
-                }
             } else {
                 isScrap[0] = true;
                 child.dispatchFinishTemporaryDetach();
@@ -2250,10 +2246,6 @@
             if (mCacheColorHint != 0) {
                 child.setDrawingCacheBackgroundColor(mCacheColorHint);
             }
-            if (ViewDebug.TRACE_RECYCLER) {
-                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW,
-                        position, getChildCount());
-            }
         }
 
         if (mAdapterHasStableIds) {
@@ -2286,11 +2278,27 @@
             super.onInitializeAccessibilityNodeInfo(host, info);
 
             final int position = getPositionForView(host);
+            final ListAdapter adapter = getAdapter();
 
-            if (position == INVALID_POSITION) {
+            if ((position == INVALID_POSITION) || (adapter == null)) {
+                // Cannot perform actions on invalid items.
+                info.setEnabled(false);
                 return;
             }
 
+            if (!isEnabled() || !adapter.isEnabled(position)) {
+                // Cannot perform actions on invalid items.
+                info.setEnabled(false);
+                return;
+            }
+
+            if (position == getSelectedItemPosition()) {
+                info.setSelected(true);
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+            } else {
+                info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+            }
+
             if (isClickable()) {
                 info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
                 info.setClickable(true);
@@ -2301,43 +2309,55 @@
                 info.setLongClickable(true);
             }
 
-            info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
-
-            if (position == getSelectedItemPosition()) {
-                info.setSelected(true);
-            }
         }
 
         @Override
         public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
-            final int position = getPositionForView(host);
+            if (super.performAccessibilityAction(host, action, arguments)) {
+                return true;
+            }
 
-            if (position == INVALID_POSITION) {
+            final int position = getPositionForView(host);
+            final ListAdapter adapter = getAdapter();
+
+            if ((position == INVALID_POSITION) || (adapter == null)) {
+                // Cannot perform actions on invalid items.
+                return false;
+            }
+
+            if (!isEnabled() || !adapter.isEnabled(position)) {
+                // Cannot perform actions on disabled items.
                 return false;
             }
 
             final long id = getItemIdAtPosition(position);
 
             switch (action) {
-                case AccessibilityNodeInfo.ACTION_SELECT:
-                    setSelection(position);
-                    return true;
-                case AccessibilityNodeInfo.ACTION_CLICK:
-                    if (!super.performAccessibilityAction(host, action, arguments)) {
+                case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+                    if (getSelectedItemPosition() == position) {
+                        setSelection(INVALID_POSITION);
+                        return true;
+                    }
+                } return false;
+                case AccessibilityNodeInfo.ACTION_SELECT: {
+                    if (getSelectedItemPosition() != position) {
+                        setSelection(position);
+                        return true;
+                    }
+                } return false;
+                case AccessibilityNodeInfo.ACTION_CLICK: {
+                    if (isClickable()) {
                         return performItemClick(host, position, id);
                     }
-                    return true;
-                case AccessibilityNodeInfo.ACTION_LONG_CLICK:
-                    if (!super.performAccessibilityAction(host, action, arguments)) {
+                } return false;
+                case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
+                    if (isLongClickable()) {
                         return performLongPress(host, position, id);
                     }
-                    return true;
-                case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
-                    smoothScrollToPosition(position);
-                    break;
+                } return false;
             }
 
-            return super.performAccessibilityAction(host, action, arguments);
+            return false;
         }
     }
 
@@ -4926,12 +4946,6 @@
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         mRecycler.addScrapView(child, position);
-
-                        if (ViewDebug.TRACE_RECYCLER) {
-                            ViewDebug.trace(child,
-                                    ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                                    firstPosition + i, -1);
-                        }
                     }
                 }
             }
@@ -4950,12 +4964,6 @@
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         mRecycler.addScrapView(child, position);
-
-                        if (ViewDebug.TRACE_RECYCLER) {
-                            ViewDebug.trace(child,
-                                    ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
-                                    firstPosition + i, -1);
-                        }
                     }
                 }
             }
@@ -5899,63 +5907,6 @@
         removeAllViewsInLayout();
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean onConsistencyCheck(int consistency) {
-        boolean result = super.onConsistencyCheck(consistency);
-
-        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
-
-        if (checkLayout) {
-            // The active recycler must be empty
-            final View[] activeViews = mRecycler.mActiveViews;
-            int count = activeViews.length;
-            for (int i = 0; i < count; i++) {
-                if (activeViews[i] != null) {
-                    result = false;
-                    Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
-                            "AbsListView " + this + " has a view in its active recycler: " +
-                                    activeViews[i]);
-                }
-            }
-
-            // All views in the recycler must NOT be on screen and must NOT have a parent
-            final ArrayList<View> scrap = mRecycler.mCurrentScrap;
-            if (!checkScrap(scrap)) result = false;
-            final ArrayList<View>[] scraps = mRecycler.mScrapViews;
-            count = scraps.length;
-            for (int i = 0; i < count; i++) {
-                if (!checkScrap(scraps[i])) result = false;
-            }
-        }
-
-        return result;
-    }
-
-    private boolean checkScrap(ArrayList<View> scrap) {
-        if (scrap == null) return true;
-        boolean result = true;
-
-        final int count = scrap.size();
-        for (int i = 0; i < count; i++) {
-            final View view = scrap.get(i);
-            if (view.getParent() != null) {
-                result = false;
-                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
-                        " has a view in its scrap heap still attached to a parent: " + view);
-            }
-            if (indexOfChild(view) >= 0) {
-                result = false;
-                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
-                        " has a view in its scrap heap that is also a direct child: " + view);
-            }
-        }
-
-        return result;
-    }
-
     private void finishGlows() {
         if (mEdgeGlowTop != null) {
             mEdgeGlowTop.finish();
@@ -6511,12 +6462,6 @@
                     if (hasListener) {
                         mRecyclerListener.onMovedToScrapHeap(victim);
                     }
-
-                    if (ViewDebug.TRACE_RECYCLER) {
-                        ViewDebug.trace(victim,
-                                ViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP,
-                                mFirstActivePosition + i, -1);
-                    }
                 }
             }
 
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index ae68794..e217e4f 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -486,5 +487,46 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(AbsSeekBar.class.getName());
+
+        if (isEnabled()) {
+            final int progress = getProgress();
+            if (progress > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+            if (progress < getMax()) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        final int progress = getProgress();
+        final int increment = Math.max(1, Math.round((float) getMax() / 5));
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                if (progress <= 0) {
+                    return false;
+                }
+                setProgress(progress - increment, true);
+                onKeyChange();
+                return true;
+            }
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                if (progress >= getMax()) {
+                    return false;
+                }
+                setProgress(progress + increment, true);
+                onKeyChange();
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
index c6104bc..fe6c4f5 100644
--- a/core/java/android/widget/ActivityChooserModel.java
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -765,16 +765,6 @@
     }
 
     /**
-     * Gets whether the given observer is already registered.
-     *
-     * @param observer The observer.
-     * @return True if already registered.
-     */
-    public boolean isRegisteredObserver(DataSetObserver observer) {
-        return mObservers.contains(observer);
-    }
-
-    /**
      * Represents a record in the history.
      */
     public final static class HistoricalRecord {
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 0c0bb1e..be6b4e2 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -20,10 +20,8 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
@@ -176,11 +174,6 @@
     private int mDefaultActionButtonContentDescription;
 
     /**
-     * Whether this view has a default activity affordance.
-     */
-    private boolean mHasDefaultActivity;
-
-    /**
      * Create a new instance.
      *
      * @param context The application environment.
@@ -252,8 +245,6 @@
         Resources resources = context.getResources();
         mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2,
               resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
-
-        updateHasDefaultActivity();
     }
 
     /**
@@ -267,21 +258,6 @@
         }
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        Configuration oldConfig = mContext.getResources().getConfiguration();
-        final int changed = oldConfig.diff(newConfig);
-        if ((changed & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
-                || (changed & ActivityInfo.CONFIG_ORIENTATION) != 0) {
-            updateHasDefaultActivity();
-        }
-    }
-
-    private void updateHasDefaultActivity() {
-        mHasDefaultActivity = mContext.getResources().getBoolean(
-                R.bool.activity_chooser_view_has_default_activity);
-    }
-
     /**
      * Sets the background for the button that expands the activity
      * overflow list.
@@ -407,8 +383,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         ActivityChooserModel dataModel = mAdapter.getDataModel();
-        if (dataModel != null
-                && !dataModel.isRegisteredObserver(mModelDataSetOberver)) {
+        if (dataModel != null) {
             dataModel.registerObserver(mModelDataSetOberver);
         }
         mIsAttachedToWindow = true;
@@ -418,8 +393,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         ActivityChooserModel dataModel = mAdapter.getDataModel();
-        if (dataModel != null
-                && dataModel.isRegisteredObserver(mModelDataSetOberver)) {
+        if (dataModel != null) {
             dataModel.unregisterObserver(mModelDataSetOberver);
         }
         ViewTreeObserver viewTreeObserver = getViewTreeObserver();
@@ -522,7 +496,7 @@
         // Default activity button.
         final int activityCount = mAdapter.getActivityCount();
         final int historySize = mAdapter.getHistorySize();
-        if (mHasDefaultActivity && activityCount > 0 && historySize > 0) {
+        if (activityCount > 0 && historySize > 0) {
             mDefaultActivityButton.setVisibility(VISIBLE);
             ResolveInfo activity = mAdapter.getDefaultActivity();
             PackageManager packageManager = mContext.getPackageManager();
@@ -538,9 +512,9 @@
         }
         // Activity chooser content.
         if (mDefaultActivityButton.getVisibility() == VISIBLE) {
-            mActivityChooserContent.setBackground(mActivityChooserContentBackground);
+            mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
         } else {
-            mActivityChooserContent.setBackground(null);
+            mActivityChooserContent.setBackgroundDrawable(null);
         }
     }
 
@@ -603,7 +577,7 @@
         // OnLongClickListener#onLongClick
         @Override
         public boolean onLongClick(View view) {
-            if (mHasDefaultActivity && view == mDefaultActivityButton) {
+            if (view == mDefaultActivityButton) {
                 if (mAdapter.getCount() > 0) {
                     mIsSelectingDefaultActivity = true;
                     showPopupUnchecked(mInitialActivityCount);
@@ -656,16 +630,14 @@
 
         public void setDataModel(ActivityChooserModel dataModel) {
             ActivityChooserModel oldDataModel = mAdapter.getDataModel();
-            if (oldDataModel != null) {
+            if (oldDataModel != null && isShown()) {
                 oldDataModel.unregisterObserver(mModelDataSetOberver);
             }
             mDataModel = dataModel;
-            if (dataModel != null) {
+            if (dataModel != null && isShown()) {
                 dataModel.registerObserver(mModelDataSetOberver);
-                notifyDataSetChanged();
-            } else {
-                notifyDataSetInvalidated();
             }
+            notifyDataSetChanged();
         }
 
         @Override
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 03fdc39..e4e7239 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -55,7 +56,12 @@
  * @attr ref android.R.styleable#Gallery_animationDuration
  * @attr ref android.R.styleable#Gallery_spacing
  * @attr ref android.R.styleable#Gallery_gravity
+ * 
+ * @deprecated This widget is no longer supported. Other horizontally scrolling
+ * widgets include {@link HorizontalScrollView} and {@link android.support.v4.view.ViewPager}
+ * from the support library.
  */
+@Deprecated
 @Widget
 public class Gallery extends AbsSpinner implements GestureDetector.OnGestureListener {
 
@@ -1367,6 +1373,37 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(Gallery.class.getName());
+        info.setScrollable(mItemCount > 1);
+        if (isEnabled()) {
+            if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+            if (isEnabled() && mItemCount > 0 && mSelectedPosition > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                if (isEnabled() && mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
+                    final int currentChildIndex = mSelectedPosition - mFirstPosition;
+                    return scrollToChild(currentChildIndex + 1);
+                }
+            } return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                if (isEnabled() && mItemCount > 0 && mSelectedPosition > 0) {
+                    final int currentChildIndex = mSelectedPosition - mFirstPosition;
+                    return scrollToChild(currentChildIndex - 1);
+                }
+            } return false;
+        }
+        return false;
     }
 
     /**
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 60a1d15..772d748 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -294,8 +294,32 @@
     }
 
     /**
-     * Orientation is used only to generate default row/column indices when
-     * they are not specified by a component's layout parameters.
+     *
+     * GridLayout uses the orientation property for two purposes:
+     * <ul>
+     *  <li>
+     *      To control the 'direction' in which default row/column indices are generated
+     *      when they are not specified in a component's layout parameters.
+     *  </li>
+     *  <li>
+     *      To control which axis should be processed first during the layout operation:
+     *      when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
+     *  </li>
+     * </ul>
+     *
+     * The order in which axes are laid out is important if, for example, the height of
+     * one of GridLayout's children is dependent on its width - and its width is, in turn,
+     * dependent on the widths of other components.
+     * <p>
+     * If your layout contains a {@link TextView} (or derivative:
+     * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
+     * in multi-line mode (the default) it is normally best to leave GridLayout's
+     * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
+     * deriving its height for a given width, but not the other way around.
+     * <p>
+     * Other than the effects above, orientation does not affect the actual layout operation of
+     * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
+     * the height of the intended layout greatly exceeds its width.
      * <p>
      * The default value of this property is {@link #HORIZONTAL}.
      *
@@ -1373,6 +1397,7 @@
                             break;
                         }
                         case PENDING: {
+                            // le singe est dans l'arbre
                             assert false;
                             break;
                         }
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ffabd1d9..8c6ce19 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -739,8 +739,14 @@
 
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                if (!isEnabled()) {
+                    return false;
+                }
                 final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
                 final int targetScrollX = Math.min(mScrollX + viewportWidth, getScrollRange());
                 if (targetScrollX != mScrollX) {
@@ -749,6 +755,9 @@
                 }
             } return false;
             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                if (!isEnabled()) {
+                    return false;
+                }
                 final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
                 final int targetScrollX = Math.max(0, mScrollX - viewportWidth);
                 if (targetScrollX != mScrollX) {
@@ -757,7 +766,7 @@
                 }
             } return false;
         }
-        return super.performAccessibilityAction(action, arguments);
+        return false;
     }
 
     @Override
@@ -767,10 +776,10 @@
         final int scrollRange = getScrollRange();
         if (scrollRange > 0) {
             info.setScrollable(true);
-            if (mScrollX > 0) {
+            if (isEnabled() && mScrollX > 0) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
             }
-            if (mScrollX < scrollRange) {
+            if (isEnabled() && mScrollX < scrollRange) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
             }
         }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5098523..c62b62b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1557,10 +1557,6 @@
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
                     recycleBin.addScrapView(getChildAt(i), firstPosition+i);
-                    if (ViewDebug.TRACE_RECYCLER) {
-                        ViewDebug.trace(getChildAt(i),
-                                ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
-                    }
                 }
             } else {
                 recycleBin.fillActiveViews(childCount, firstPosition);
@@ -1757,11 +1753,6 @@
             // Try to use an existing view for this position
             child = mRecycler.getActiveView(position);
             if (child != null) {
-                if (ViewDebug.TRACE_RECYCLER) {
-                    ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
-                            position, getChildCount());
-                }
-
                 // Found it -- we're using an existing child
                 // This just needs to be positioned
                 setupChild(child, position, y, flow, childrenLeft, selected, true);
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7c809b3..b60ffc5 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -950,6 +950,8 @@
                     provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                     mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+                    provider.performAction(hoveredVirtualViewId,
+                            AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
                 } break;
                 case MotionEvent.ACTION_HOVER_MOVE: {
                     if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
@@ -960,6 +962,8 @@
                         provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
                                 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                         mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+                        provider.performAction(hoveredVirtualViewId,
+                                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
                     }
                 } break;
                 case MotionEvent.ACTION_HOVER_EXIT: {
@@ -1413,9 +1417,16 @@
     }
 
     @Override
-    public void sendAccessibilityEvent(int eventType) {
-        // Do not send accessibility events - we want the user to
-        // perceive this widget as several controls rather as a whole.
+    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        // We do not want the real descendant to be considered focus search
+        // since it is managed by the accessibility node provider.
+        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
+            if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
+                views.add(this);
+                return;
+            }
+        }
+        super.addFocusables(views, direction, focusableMode);
     }
 
     @Override
@@ -2072,7 +2083,12 @@
         }
     }
 
+    /**
+     * Class for managing virtual view tree rooted at this picker.
+     */
     class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
+        private static final int UNDEFINED = Integer.MIN_VALUE;
+
         private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
 
         private static final int VIRTUAL_VIEW_ID_INPUT = 2;
@@ -2083,6 +2099,8 @@
 
         private final int[] mTempArray = new int[2];
 
+        private int mAccessibilityFocusedView = UNDEFINED;
+
         @Override
         public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
             switch (virtualViewId) {
@@ -2137,37 +2155,236 @@
         @Override
         public boolean performAction(int virtualViewId, int action, Bundle arguments) {
             switch (virtualViewId) {
+                case View.NO_ID: {
+                    switch (action) {
+                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView != virtualViewId) {
+                                mAccessibilityFocusedView = virtualViewId;
+                                requestAccessibilityFocus();
+                                return true;
+                            }
+                        } return false;
+                        case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView == virtualViewId) {
+                                mAccessibilityFocusedView = UNDEFINED;
+                                clearAccessibilityFocus();
+                                return true;
+                            }
+                            return false;
+                        }
+                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                            if (NumberPicker.this.isEnabled()
+                                    && (getWrapSelectorWheel() || getValue() < getMaxValue())) {
+                                changeValueByOne(true);
+                                return true;
+                            }
+                        } return false;
+                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                            if (NumberPicker.this.isEnabled()
+                                    && (getWrapSelectorWheel() || getValue() > getMinValue())) {
+                                changeValueByOne(false);
+                                return true;
+                            }
+                        } return false;
+                    }
+                } break;
                 case VIRTUAL_VIEW_ID_INPUT: {
                     switch (action) {
                         case AccessibilityNodeInfo.ACTION_FOCUS: {
-                            if (!mInputText.isFocused()) {
+                            if (NumberPicker.this.isEnabled() && !mInputText.isFocused()) {
                                 return mInputText.requestFocus();
                             }
                         } break;
                         case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
-                            if (mInputText.isFocused()) {
+                            if (NumberPicker.this.isEnabled() && mInputText.isFocused()) {
                                 mInputText.clearFocus();
                                 return true;
                             }
-                        } break;
+                            return false;
+                        }
+                        case AccessibilityNodeInfo.ACTION_CLICK: {
+                            if (NumberPicker.this.isEnabled()) {
+                                showSoftInput();
+                                return true;
+                            }
+                            return false;
+                        }
+                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView != virtualViewId) {
+                                mAccessibilityFocusedView = virtualViewId;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+                                mInputText.invalidate();
+                                return true;
+                            }
+                        } return false;
+                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView == virtualViewId) {
+                                mAccessibilityFocusedView = UNDEFINED;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+                                mInputText.invalidate();
+                                return true;
+                            }
+                        } return false;
+                        default: {
+                            return mInputText.performAccessibilityAction(action, arguments);
+                        }
+                    }
+                } return false;
+                case VIRTUAL_VIEW_ID_INCREMENT: {
+                    switch (action) {
+                        case AccessibilityNodeInfo.ACTION_CLICK: {
+                            if (NumberPicker.this.isEnabled()) {
+                                NumberPicker.this.changeValueByOne(true);
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
+                                return true;
+                            }
+                        } return false;
+                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView != virtualViewId) {
+                                mAccessibilityFocusedView = virtualViewId;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
+                                return true;
+                            }
+                        } return false;
+                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView == virtualViewId) {
+                                mAccessibilityFocusedView = UNDEFINED;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
+                                return true;
+                            }
+                        } return false;
+                    }
+                } return false;
+                case VIRTUAL_VIEW_ID_DECREMENT: {
+                    switch (action) {
+                        case AccessibilityNodeInfo.ACTION_CLICK: {
+                            if (NumberPicker.this.isEnabled()) {
+                                final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
+                                NumberPicker.this.changeValueByOne(increment);
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
+                                return true;
+                            }
+                        } return false;
+                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView != virtualViewId) {
+                                mAccessibilityFocusedView = virtualViewId;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
+                                return true;
+                            }
+                        } return false;
+                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+                            if (mAccessibilityFocusedView == virtualViewId) {
+                                mAccessibilityFocusedView = UNDEFINED;
+                                sendAccessibilityEventForVirtualView(virtualViewId,
+                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
+                                return true;
+                            }
+                        } return false;
+                    }
+                } return false;
+            }
+            return super.performAction(virtualViewId, action, arguments);
+        }
+
+        @Override
+        public AccessibilityNodeInfo findAccessibilityFocus(int virtualViewId) {
+            return createAccessibilityNodeInfo(mAccessibilityFocusedView);
+        }
+
+        @Override
+        public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
+            switch (direction) {
+                case View.ACCESSIBILITY_FOCUS_DOWN:
+                case View.ACCESSIBILITY_FOCUS_FORWARD: {
+                    switch (mAccessibilityFocusedView) {
+                        case UNDEFINED: {
+                            return createAccessibilityNodeInfo(View.NO_ID);
+                        }
+                        case View.NO_ID: {
+                            if (hasVirtualDecrementButton()) {
+                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
+                            }
+                        }
+                        //$FALL-THROUGH$
+                        case VIRTUAL_VIEW_ID_DECREMENT: {
+                            return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
+                        }
+                        case VIRTUAL_VIEW_ID_INPUT: {
+                            if (hasVirtualIncrementButton()) {
+                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
+                            }
+                        }
+                        //$FALL-THROUGH$
+                        case VIRTUAL_VIEW_ID_INCREMENT: {
+                            View nextFocus = NumberPicker.this.focusSearch(direction);
+                            if (nextFocus != null) {
+                                return nextFocus.createAccessibilityNodeInfo();
+                            }
+                            return null;
+                        }
+                    }
+                } break;
+                case View.ACCESSIBILITY_FOCUS_UP:
+                case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+                    switch (mAccessibilityFocusedView) {
+                        case UNDEFINED: {
+                            return createAccessibilityNodeInfo(View.NO_ID);
+                        }
+                        case View.NO_ID: {
+                            if (hasVirtualIncrementButton()) {
+                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
+                            }
+                        }
+                        //$FALL-THROUGH$
+                        case VIRTUAL_VIEW_ID_INCREMENT: {
+                            return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
+                        }
+                        case VIRTUAL_VIEW_ID_INPUT: {
+                            if (hasVirtualDecrementButton()) {
+                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
+                            }
+                        }
+                        //$FALL-THROUGH$
+                        case VIRTUAL_VIEW_ID_DECREMENT: {
+                            View nextFocus = NumberPicker.this.focusSearch(direction);
+                            if (nextFocus != null) {
+                                return nextFocus.createAccessibilityNodeInfo();
+                            }
+                            return null;
+                        }
                     }
                 } break;
             }
-            return super.performAction(virtualViewId, action, arguments);
+            return null;
         }
 
         public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
             switch (virtualViewId) {
                 case VIRTUAL_VIEW_ID_DECREMENT: {
-                    sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
-                            getVirtualDecrementButtonText());
+                    if (hasVirtualDecrementButton()) {
+                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+                                getVirtualDecrementButtonText());
+                    }
                 } break;
                 case VIRTUAL_VIEW_ID_INPUT: {
                     sendAccessibilityEventForVirtualText(eventType);
                 } break;
                 case VIRTUAL_VIEW_ID_INCREMENT: {
-                    sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
-                            getVirtualIncrementButtonText());
+                    if (hasVirtualIncrementButton()) {
+                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+                                getVirtualIncrementButtonText());
+                    }
                 } break;
             }
         }
@@ -2227,8 +2444,13 @@
 
         private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
             AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
-            info.setLongClickable(true);
             info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+            if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
+                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            }
+            if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+            }
             return info;
         }
 
@@ -2252,6 +2474,17 @@
             getLocationOnScreen(locationOnScreen);
             boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
             info.setBoundsInScreen(boundsInScreen);
+
+            if (mAccessibilityFocusedView != virtualViewId) {
+                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            }
+            if (mAccessibilityFocusedView == virtualViewId) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+            }
+            if (NumberPicker.this.isEnabled()) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+            }
+
             return info;
         }
 
@@ -2261,10 +2494,16 @@
             info.setClassName(NumberPicker.class.getName());
             info.setPackageName(mContext.getPackageName());
             info.setSource(NumberPicker.this);
-            info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+
+            if (hasVirtualDecrementButton()) {
+                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+            }
             info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
-            info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
-            info.setParent((View) getParent());
+            if (hasVirtualIncrementButton()) {
+                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
+            }
+
+            info.setParent((View) getParentForAccessibility());
             info.setEnabled(NumberPicker.this.isEnabled());
             info.setScrollable(true);
             Rect boundsInParent = mTempRect;
@@ -2276,9 +2515,33 @@
             getLocationOnScreen(locationOnScreen);
             boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
             info.setBoundsInScreen(boundsInScreen);
+
+            if (mAccessibilityFocusedView != View.NO_ID) {
+                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            }
+            if (mAccessibilityFocusedView == View.NO_ID) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+            }
+            if (NumberPicker.this.isEnabled()) {
+                if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
+                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                }
+                if (getWrapSelectorWheel() || getValue() > getMinValue()) {
+                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                }
+            }
+
             return info;
         }
 
+        private boolean hasVirtualDecrementButton() {
+            return getWrapSelectorWheel() || getValue() > getMinValue();
+        }
+
+        private boolean hasVirtualIncrementButton() {
+            return getWrapSelectorWheel() || getValue() < getMaxValue();
+        }
+
         private String getVirtualDecrementButtonText() {
             int value = mValue - 1;
             if (mWrapSelectorWheel) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 5fa4ad0..f442912 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1253,13 +1253,13 @@
             unregisterForScrollChanged();
 
             try {
-                mWindowManager.removeView(mPopupView);                
+                mWindowManager.removeViewImmediate(mPopupView);
             } finally {
                 if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
                     ((ViewGroup) mPopupView).removeView(mContentView);
                 }
                 mPopupView = null;
-    
+
                 if (mOnDismissListener != null) {
                     mOnDismissListener.onDismiss();
                 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 56c4bd8..1985792 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -37,6 +37,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
@@ -1182,6 +1183,87 @@
     }
 
     /**
+     * Helper action to set text size on a TextView in any supported units.
+     */
+    private class TextViewSizeAction extends Action {
+        public TextViewSizeAction(int viewId, int units, float size) {
+            this.viewId = viewId;
+            this.units = units;
+            this.size = size;
+        }
+
+        public TextViewSizeAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            units = parcel.readInt();
+            size  = parcel.readFloat();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(units);
+            dest.writeFloat(size);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent) {
+            final Context context = root.getContext();
+            final TextView target = (TextView) root.findViewById(viewId);
+            if (target == null) return;
+            target.setTextSize(units, size);
+        }
+
+        int viewId;
+        int units;
+        float size;
+
+        public final static int TAG = 13;
+    }
+
+    /**
+     * Helper action to set padding on a View.
+     */
+    private class ViewPaddingAction extends Action {
+        public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
+            this.viewId = viewId;
+            this.left = left;
+            this.top = top;
+            this.right = right;
+            this.bottom = bottom;
+        }
+
+        public ViewPaddingAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            left = parcel.readInt();
+            top = parcel.readInt();
+            right = parcel.readInt();
+            bottom = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(left);
+            dest.writeInt(top);
+            dest.writeInt(right);
+            dest.writeInt(bottom);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent) {
+            final Context context = root.getContext();
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+            target.setPadding(left, top, right, bottom);
+        }
+
+        int viewId;
+        int left, top, right, bottom;
+
+        public final static int TAG = 14;
+    }
+
+    /**
      * Simple class used to keep track of memory usage in a RemoteViews.
      *
      */
@@ -1334,6 +1416,12 @@
                     case TextViewDrawableAction.TAG:
                         mActions.add(new TextViewDrawableAction(parcel));
                         break;
+                    case TextViewSizeAction.TAG:
+                        mActions.add(new TextViewSizeAction(parcel));
+                        break;
+                    case ViewPaddingAction.TAG:
+                        mActions.add(new ViewPaddingAction(parcel));
+                        break;
                     case BitmapReflectionAction.TAG:
                         mActions.add(new BitmapReflectionAction(parcel));
                         break;
@@ -1445,7 +1533,8 @@
     /**
      * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
      */
-    int estimateMemoryUsage() {
+    /** @hide */
+    public int estimateMemoryUsage() {
         return mMemoryUsageCounter.getMemoryUsage();
     }
 
@@ -1540,7 +1629,19 @@
     public void setTextViewText(int viewId, CharSequence text) {
         setCharSequence(viewId, "setText", text);
     }
-    
+
+    /**
+     * @hide
+     * Equivalent to calling {@link TextView#setTextSize(int, float)}
+     * 
+     * @param viewId The id of the view whose text size should change
+     * @param units The units of size (e.g. COMPLEX_UNIT_SP)
+     * @param size The size of the text
+     */
+    public void setTextViewTextSize(int viewId, int units, float size) {
+        addAction(new TextViewSizeAction(viewId, units, size));
+    }
+
     /**
      * Equivalent to calling 
      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
@@ -1798,6 +1899,20 @@
     }
 
     /**
+     * @hide
+     * Equivalent to calling {@link View#setPadding(int, int, int, int)}.
+     *
+     * @param viewId The id of the view to change
+     * @param left the left padding in pixels
+     * @param top the top padding in pixels
+     * @param right the right padding in pixels
+     * @param bottom the bottom padding in pixels
+     */
+    public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
+        addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
+    }
+
+    /**
      * Call a method taking one boolean on a view in the layout for this RemoteViews.
      *
      * @param viewId The id of the view on which to call the method.
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index f266d50..46ec923a 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -614,7 +614,7 @@
                     maxDistIndexNonRequested = i;
                     maxDistNonRequested = dist;
                 }
-                if (dist > maxDist) {
+                if (dist >= maxDist) {
                     // maxDist/maxDistIndex will store the index of the farthest position
                     // regardless of whether it was directly requested or not
                     maxDistIndex = i;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index b398ce4..2a20c56 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -742,6 +742,12 @@
 
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
                 final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
@@ -760,21 +766,23 @@
                 }
             } return false;
         }
-        return super.performAccessibilityAction(action, arguments);
+        return false;
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(ScrollView.class.getName());
-        final int scrollRange = getScrollRange();
-        if (scrollRange > 0) {
-            info.setScrollable(true);
-            if (mScrollY > 0) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            }
-            if (mScrollY < scrollRange) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        if (isEnabled()) {
+            final int scrollRange = getScrollRange();
+            if (scrollRange > 0) {
+                info.setScrollable(true);
+                if (mScrollY > 0) {
+                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                }
+                if (mScrollY < scrollRange) {
+                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                }
             }
         }
     }
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 21840ca..4045497 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -44,7 +44,6 @@
  * <code>
  *  // In Activity#onCreateOptionsMenu
  *  public boolean onCreateOptionsMenu(Menu menu) {
- *      getManuInflater().inflate(R.menu.my_menu, menu);
  *      // Get the menu item.
  *      MenuItem menuItem = menu.findItem(R.id.my_menu_item);
  *      // Get the provider and hold onto it to set/change the share intent.
@@ -246,7 +245,7 @@
      * call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the
      * action view. You should <strong>not</strong> call
      * {@link android.app.Activity#invalidateOptionsMenu()} from
-     * {@link android.app.Activity#onCreateOptionsMenu(Menu)}.
+     * {@link android.app.Activity#onCreateOptionsMenu(Menu)}."
      * <p>
      * <code>
      * private void doShare(Intent intent) {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 22df3bc..293eda1 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -32,6 +32,7 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.TableMaskFilter;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -1228,6 +1229,40 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(StackView.class.getName());
+        info.setScrollable(getChildCount() > 1);
+        if (isEnabled()) {
+            if (getDisplayedChild() < getChildCount() - 1) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+            if (getDisplayedChild() > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                if (getDisplayedChild() < getChildCount() - 1) {
+                    showNext();
+                    return true;
+                }
+            } return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                if (getDisplayedChild() > 0) {
+                    showPrevious();
+                    return true;
+                }
+            } return false;
+        }
+        return false;
     }
 
     class LayoutParams extends ViewGroup.LayoutParams {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fc56e11..81a44fd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -39,6 +39,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.text.BoringLayout;
 import android.text.DynamicLayout;
 import android.text.Editable;
@@ -5625,6 +5626,7 @@
                       physicalWidth, false);
     }
 
+    /** @hide */
     @Override
     public void onResolvedLayoutDirectionReset() {
         if (mLayoutAlignment != null) {
@@ -7704,14 +7706,23 @@
         super.onPopulateAccessibilityEvent(event);
 
         final boolean isPassword = hasPasswordTransformationMethod();
-        if (!isPassword) {
-            CharSequence text = getTextForAccessibility();
+        if (!isPassword || shouldSpeakPasswordsForAccessibility()) {
+            final CharSequence text = getTextForAccessibility();
             if (!TextUtils.isEmpty(text)) {
                 event.getText().add(text);
             }
         }
     }
 
+    /**
+     * @return true if the user has explicitly allowed accessibility services
+     * to speak passwords.
+     */
+    private boolean shouldSpeakPasswordsForAccessibility() {
+        return (Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) == 1);
+    }
+
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
@@ -7739,8 +7750,7 @@
             info.setText(getTextForAccessibility());
         }
 
-        if (TextUtils.isEmpty(getContentDescription())
-                && !TextUtils.isEmpty(mText)) {
+        if (TextUtils.isEmpty(getContentDescription()) && !TextUtils.isEmpty(mText)) {
             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
             info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
@@ -8162,6 +8172,7 @@
         return mEditor.mInBatchEditControllers;
     }
 
+    /** @hide */
     @Override
     public void onResolvedTextDirectionChanged() {
         if (hasPasswordTransformationMethod()) {
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 88d7e05..fafc113 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -120,7 +120,12 @@
      */
     public void cancel() {
         mTN.hide();
-        // TODO this still needs to cancel the inflight notification if any
+
+        try {
+            getService().cancelToast(mContext.getPackageName(), mTN);
+        } catch (RemoteException e) {
+            // Empty
+        }
     }
     
     /**
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 2cbd9cc902..234cb71 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -708,7 +708,15 @@
             anim.start();
         } else {
             mTopVisibilityView.setAlpha(1);
-            mContainerView.setTranslationY(0);
+            mTopVisibilityView.setTranslationY(0);
+            if (mContentView != null) {
+                mContentView.setTranslationY(0);
+            }
+            if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
+                mSplitView.setAlpha(1);
+                mSplitView.setTranslationY(0);
+                mSplitView.setVisibility(View.VISIBLE);
+            }
             mShowListener.onAnimationEnd(null);
         }
         if (mOverlayLayout != null) {
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 89f2187..3a2b647 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -19,13 +19,17 @@
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Vibrator;
-import android.view.MotionEvent;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
 import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 import android.widget.Toast;
 
 public class PlatLogoActivity extends Activity {
@@ -34,21 +38,71 @@
     int mCount;
     final Handler mHandler = new Handler();
 
+    private View makeView() {
+        DisplayMetrics metrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+        LinearLayout view = new LinearLayout(this);
+        view.setOrientation(LinearLayout.VERTICAL);
+        view.setLayoutParams(
+                new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT
+                ));
+        final int p = (int)(8 * metrics.density);
+        view.setPadding(p, p, p, p);
+
+        Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL);
+        Typeface normal = Typeface.create("sans-serif", Typeface.BOLD);
+
+        final float size = 14 * metrics.density;
+        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        lp.gravity = Gravity.CENTER_HORIZONTAL;
+        lp.bottomMargin = (int) (-4*metrics.density);
+
+        TextView tv = new TextView(this);
+        if (light != null) tv.setTypeface(light);
+        tv.setTextSize(1.25f*size);
+        tv.setTextColor(0xFFFFFFFF);
+        tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000);
+        tv.setText("Android " + Build.VERSION.RELEASE);
+        view.addView(tv, lp);
+   
+        tv = new TextView(this);
+        if (normal != null) tv.setTypeface(normal);
+        tv.setTextSize(size);
+        tv.setTextColor(0xFFFFFFFF);
+        tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000);
+        tv.setText("JELLY BEAN");
+        view.addView(tv, lp);
+
+        return view;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mToast = Toast.makeText(this, "Android X.X: Jelly Bean", Toast.LENGTH_SHORT);
+        mToast = Toast.makeText(this, "", Toast.LENGTH_LONG);
+        mToast.setView(makeView());
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
         mContent = new ImageView(this);
-        mContent.setImageResource(com.android.internal.R.drawable.platlogo);
+        mContent.setImageResource(com.android.internal.R.drawable.platlogo_alt);
         mContent.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        
+        final int p = (int)(32 * metrics.density);
+        mContent.setPadding(p, p, p, p);
 
         mContent.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 mToast.show();
-                mContent.setImageResource(com.android.internal.R.drawable.platlogo_alt);
+                mContent.setImageResource(com.android.internal.R.drawable.platlogo);
             }
         });
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 614f73f..7334ac3 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,6 +20,7 @@
 import com.android.internal.content.PackageMonitor;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +35,9 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PatternMatcher;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserId;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,6 +65,7 @@
 public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {
     private static final String TAG = "ResolverActivity";
 
+    private int mLaunchedFromUid;
     private ResolveListAdapter mAdapter;
     private PackageManager mPm;
     private boolean mAlwaysUseOption;
@@ -102,6 +107,12 @@
             boolean alwaysUseOption) {
         setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
         super.onCreate(savedInstanceState);
+        try {
+            mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
+                    getActivityToken());
+        } catch (RemoteException e) {
+            mLaunchedFromUid = -1;
+        }
         mPm = getPackageManager();
         mAlwaysUseOption = alwaysUseOption;
         mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
@@ -118,9 +129,14 @@
         mIconDpi = am.getLauncherLargeIconDensity();
         mIconSize = am.getLauncherLargeIconSize();
 
-        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList);
+        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
+                mLaunchedFromUid);
         int count = mAdapter.getCount();
-        if (count > 1) {
+        if (mLaunchedFromUid < 0 || UserId.isIsolated(mLaunchedFromUid)) {
+            // Gulp!
+            finish();
+            return;
+        } else if (count > 1) {
             ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
             mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
             mGrid.setAdapter(mAdapter);
@@ -146,9 +162,13 @@
 
         if (alwaysUseOption) {
             final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
-            buttonLayout.setVisibility(View.VISIBLE);
-            mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
-            mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            if (buttonLayout != null) {
+                buttonLayout.setVisibility(View.VISIBLE);
+                mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
+                mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            } else {
+                mAlwaysUseOption = false;
+            }
         }
     }
 
@@ -207,6 +227,18 @@
             mPackageMonitor.unregister();
             mRegistered = false;
         }
+        if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // This resolver is in the unusual situation where it has been
+            // launched at the top of a new task.  We don't let it be added
+            // to the recent tasks shown to the user, and we need to make sure
+            // that each time we are launched we get the correct launching
+            // uid (not re-using the same resolver from an old launching uid),
+            // so we will now finish ourself since being no longer visible,
+            // the user probably can't get back to us.
+            if (!isChangingConfigurations()) {
+                finish();
+            }
+        }
     }
 
     @Override
@@ -363,17 +395,19 @@
         private final Intent[] mInitialIntents;
         private final List<ResolveInfo> mBaseResolveList;
         private final Intent mIntent;
+        private final int mLaunchedFromUid;
         private final LayoutInflater mInflater;
 
         private List<ResolveInfo> mCurrentResolveList;
         private List<DisplayResolveInfo> mList;
 
         public ResolveListAdapter(Context context, Intent intent,
-                Intent[] initialIntents, List<ResolveInfo> rList) {
+                Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
             mIntent = new Intent(intent);
             mIntent.setComponent(null);
             mInitialIntents = initialIntents;
             mBaseResolveList = rList;
+            mLaunchedFromUid = launchedFromUid;
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             rebuildList();
         }
@@ -400,6 +434,23 @@
                 mCurrentResolveList = mPm.queryIntentActivities(
                         mIntent, PackageManager.MATCH_DEFAULT_ONLY
                         | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
+                // Filter out any activities that the launched uid does not
+                // have permission for.  We don't do this when we have an explicit
+                // list of resolved activities, because that only happens when
+                // we are being subclassed, so we can safely launch whatever
+                // they gave us.
+                if (mCurrentResolveList != null) {
+                    for (int i=mCurrentResolveList.size()-1; i >= 0; i--) {
+                        ActivityInfo ai = mCurrentResolveList.get(i).activityInfo;
+                        int granted = ActivityManager.checkComponentPermission(
+                                ai.permission, mLaunchedFromUid,
+                                ai.applicationInfo.uid, ai.exported);
+                        if (granted != PackageManager.PERMISSION_GRANTED) {
+                            // Access not allowed!
+                            mCurrentResolveList.remove(i);
+                        }
+                    }
+                }
             }
             int N;
             if ((mCurrentResolveList != null) && ((N = mCurrentResolveList.size()) > 0)) {
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 60cd895..d6fb847 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -67,6 +67,7 @@
         public void onReleased(View v, int handle);
         public void onTrigger(View v, int target);
         public void onGrabbedStateChange(View v, int handle);
+        public void onFinishFinalAnimation();
     }
 
     // Tuneable parameters for animation
@@ -77,20 +78,23 @@
     private static final int HIDE_ANIMATION_DELAY = 200;
     private static final int HIDE_ANIMATION_DURATION = 200;
     private static final int SHOW_ANIMATION_DURATION = 200;
-    private static final int SHOW_ANIMATION_DELAY = 0;
+    private static final int SHOW_ANIMATION_DELAY = 50;
     private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
-    private static final long RING_EXPAND_DURATION = 200;
-    private static final float TARGET_INITIAL_POSITION_SCALE = 0.8f;
+    private static final float TARGET_SCALE_SELECTED = 0.8f;
+    private static final long INITIAL_SHOW_HANDLE_DURATION = 200;
+    private static final float TARGET_SCALE_UNSELECTED = 1.0f;
+    private static final float RING_SCALE_UNSELECTED = 0.5f;
+    private static final float RING_SCALE_SELECTED = 1.5f;
 
     private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
 
     private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
     private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
-    private ArrayList<Tweener> mChevronAnimations = new ArrayList<Tweener>();
-    private ArrayList<Tweener> mTargetAnimations = new ArrayList<Tweener>();
+    private AnimationBundle mChevronAnimations = new AnimationBundle();
+    private AnimationBundle mTargetAnimations = new AnimationBundle();
+    private AnimationBundle mHandleAnimations = new AnimationBundle();
     private ArrayList<String> mTargetDescriptions;
     private ArrayList<String> mDirectionDescriptions;
-    private Tweener mHandleAnimation;
     private OnTriggerListener mOnTriggerListener;
     private TargetDrawable mHandleDrawable;
     private TargetDrawable mOuterRing;
@@ -114,9 +118,46 @@
     private boolean mDragging;
     private int mNewTargetResources;
 
+    private class AnimationBundle extends ArrayList<Tweener> {
+        private static final long serialVersionUID = 0xA84D78726F127468L;
+        private boolean mSuspended;
+
+        public void start() {
+            if (mSuspended) return; // ignore attempts to start animations
+            final int count = size();
+            for (int i = 0; i < count; i++) {
+                Tweener anim = get(i);
+                anim.animator.start();
+            }
+        }
+
+        public void cancel() {
+            final int count = size();
+            for (int i = 0; i < count; i++) {
+                Tweener anim = get(i);
+                anim.animator.cancel();
+            }
+            clear();
+        }
+
+        public void stop() {
+            final int count = size();
+            for (int i = 0; i < count; i++) {
+                Tweener anim = get(i);
+                anim.animator.end();
+            }
+            clear();
+        }
+
+        public void setSuspended(boolean suspend) {
+            mSuspended = suspend;
+        }
+    };
+
     private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
         public void onAnimationEnd(Animator animator) {
             switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
+            dispatchOnFinishFinalAnimation();
         }
     };
 
@@ -124,6 +165,7 @@
         public void onAnimationEnd(Animator animator) {
             ping();
             switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
+            dispatchOnFinishFinalAnimation();
         }
     };
 
@@ -239,6 +281,7 @@
 
         a.recycle();
         setVibrateEnabled(mVibrationDuration > 0);
+        assignDefaultsIfNeeded();
     }
 
     private void dump() {
@@ -254,6 +297,21 @@
         Log.v(TAG, "VerticalOffset = " + mVerticalOffset);
     }
 
+    public void suspendAnimations() {
+        mChevronAnimations.setSuspended(true);
+        mTargetAnimations.setSuspended(true);
+        mHandleAnimations.setSuspended(true);
+    }
+
+    public void resumeAnimations() {
+        mChevronAnimations.setSuspended(false);
+        mTargetAnimations.setSuspended(false);
+        mHandleAnimations.setSuspended(false);
+        mChevronAnimations.start();
+        mTargetAnimations.start();
+        mHandleAnimations.start();
+    }
+
     @Override
     protected int getSuggestedMinimumWidth() {
         // View should be large enough to contain the background + handle and
@@ -307,7 +365,7 @@
                 stopHandleAnimation();
                 deactivateTargets();
                 showTargets(true);
-                mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
+                activateHandle();
                 setGrabbedState(OnTriggerListener.CENTER_HANDLE);
                 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                     announceTargets();
@@ -326,6 +384,19 @@
         }
     }
 
+    private void activateHandle() {
+        mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
+        if (mAlwaysTrackFinger) {
+            mHandleAnimations.stop();
+            mHandleDrawable.setAlpha(0.0f);
+            mHandleAnimations.add(Tweener.to(mHandleDrawable, INITIAL_SHOW_HANDLE_DURATION,
+                    "ease", Ease.Cubic.easeIn,
+                    "alpha", 1.0f,
+                    "onUpdate", mUpdateListener));
+            mHandleAnimations.start();
+        }
+    }
+
     /**
      * Animation used to attract user's attention to the target button.
      * Assumes mChevronDrawables is an a list with an even number of chevrons filled with
@@ -334,12 +405,12 @@
     private void startChevronAnimation() {
         final float chevronStartDistance = mHandleDrawable.getWidth() * 0.8f;
         final float chevronStopDistance = mOuterRadius * 0.9f / 2.0f;
-        mChevronAnimations.clear();
         final float startScale = 0.5f;
         final float endScale = 2.0f;
-
         final int directionCount = mFeedbackCount > 0 ? mChevronDrawables.size()/mFeedbackCount : 0;
 
+        mChevronAnimations.stop();
+
         // Add an animation for all chevron drawables.  There are mFeedbackCount drawables
         // in each direction and directionCount directions.
         for (int direction = 0; direction < directionCount; direction++) {
@@ -367,24 +438,21 @@
                         "onUpdate", mUpdateListener));
             }
         }
+        mChevronAnimations.start();
     }
 
     private void stopChevronAnimation() {
-        for (Tweener anim : mChevronAnimations) {
-            anim.animator.end();
-        }
-        mChevronAnimations.clear();
+        mChevronAnimations.stop();
     }
 
     private void stopHandleAnimation() {
-        if (mHandleAnimation != null) {
-            mHandleAnimation.animator.end();
-            mHandleAnimation = null;
-        }
+        mHandleAnimations.stop();
     }
 
     private void deactivateTargets() {
-        for (TargetDrawable target : mTargetDrawables) {
+        final int count = mTargetDrawables.size();
+        for (int i = 0; i < count; i++) {
+            TargetDrawable target = mTargetDrawables.get(i);
             target.setState(TargetDrawable.STATE_INACTIVE);
         }
         mActiveTarget = -1;
@@ -424,6 +492,12 @@
         }
     }
 
+    private void dispatchOnFinishFinalAnimation() {
+        if (mOnTriggerListener != null) {
+            mOnTriggerListener.onFinishFinalAnimation();
+        }
+    }
+
     private void doFinish() {
         final int activeTarget = mActiveTarget;
         boolean targetHit =  activeTarget != -1;
@@ -432,8 +506,9 @@
         hideTargets(true);
 
         // Highlight the selected one
-        mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f);
+        mHandleAnimations.cancel();
         if (targetHit) {
+            mHandleDrawable.setAlpha(0.0f);
             mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
             hideUnselected(activeTarget);
 
@@ -444,15 +519,16 @@
 
         // Animate handle back to the center based on current state.
         int delay = targetHit ? RETURN_TO_HOME_DELAY : 0;
-        int duration = targetHit ? 0 : RETURN_TO_HOME_DURATION;
-        mHandleAnimation = Tweener.to(mHandleDrawable, duration,
+        int duration = RETURN_TO_HOME_DURATION;
+        mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
                 "ease", Ease.Quart.easeOut,
                 "delay", delay,
-                "alpha", 1.0f,
+                "alpha", mAlwaysTrackFinger ? 0.0f : 1.0f,
                 "x", 0,
                 "y", 0,
                 "onUpdate", mUpdateListener,
-                "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener);
+                "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener));
+        mHandleAnimations.start();
 
         setGrabbedState(OnTriggerListener.NO_HANDLE);
     }
@@ -467,14 +543,15 @@
     }
 
     private void hideTargets(boolean animate) {
-        if (mTargetAnimations.size() > 0) {
-            stopTargetAnimation();
-        }
+        mTargetAnimations.cancel();
         // Note: these animations should complete at the same time so that we can swap out
         // the target assets asynchronously from the setTargetResources() call.
         mAnimatingTargets = animate;
         final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
         final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
+        final boolean targetSelected = mActiveTarget != -1;
+
+        final float targetScale = targetSelected ? TARGET_SCALE_SELECTED : TARGET_SCALE_UNSELECTED;
         final int length = mTargetDrawables.size();
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
@@ -482,13 +559,13 @@
             mTargetAnimations.add(Tweener.to(target, duration,
                     "ease", Ease.Cubic.easeOut,
                     "alpha", 0.0f,
-                    "scaleX", TARGET_INITIAL_POSITION_SCALE,
-                    "scaleY", TARGET_INITIAL_POSITION_SCALE,
+                    "scaleX", targetScale,
+                    "scaleY", targetScale,
                     "delay", delay,
                     "onUpdate", mUpdateListener));
         }
 
-        float ringScaleTarget = mActiveTarget != -1 ? 1.5f : 0.5f;
+        final float ringScaleTarget = targetSelected ? RING_SCALE_SELECTED : RING_SCALE_UNSELECTED;
         mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 0.0f,
@@ -497,21 +574,22 @@
                 "delay", delay,
                 "onUpdate", mUpdateListener,
                 "onComplete", mTargetUpdateListener));
+
+        mTargetAnimations.start();
     }
 
     private void showTargets(boolean animate) {
-        if (mTargetAnimations.size() > 0) {
-            stopTargetAnimation();
-        }
+        mTargetAnimations.stop();
         mAnimatingTargets = animate;
         final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
+        final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
         final int length = mTargetDrawables.size();
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
             target.setState(TargetDrawable.STATE_INACTIVE);
-            target.setScaleX(TARGET_INITIAL_POSITION_SCALE);
-            target.setScaleY(TARGET_INITIAL_POSITION_SCALE);
-            mTargetAnimations.add(Tweener.to(target, animate ? SHOW_ANIMATION_DURATION : 0,
+            target.setScaleX(TARGET_SCALE_SELECTED);
+            target.setScaleY(TARGET_SCALE_SELECTED);
+            mTargetAnimations.add(Tweener.to(target, duration,
                     "ease", Ease.Cubic.easeOut,
                     "alpha", 1.0f,
                     "scaleX", 1.0f,
@@ -519,9 +597,7 @@
                     "delay", delay,
                     "onUpdate", mUpdateListener));
         }
-        mOuterRing.setScaleX(0.5f);
-        mOuterRing.setScaleY(0.5f);
-        mTargetAnimations.add(Tweener.to(mOuterRing, animate ? RING_EXPAND_DURATION : 0,
+        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 1.0f,
                 "scaleX", 1.0f,
@@ -529,13 +605,8 @@
                 "delay", delay,
                 "onUpdate", mUpdateListener,
                 "onComplete", mTargetUpdateListener));
-    }
 
-    private void stopTargetAnimation() {
-        for (Tweener anim : mTargetAnimations) {
-            anim.animator.end();
-        }
-        mTargetAnimations.clear();
+        mTargetAnimations.start();
     }
 
     private void vibrate() {
@@ -658,7 +729,6 @@
      *
      */
     public void ping() {
-        stopChevronAnimation();
         startChevronAnimation();
     }
 
@@ -671,7 +741,7 @@
     public void reset(boolean animate) {
         stopChevronAnimation();
         stopHandleAnimation();
-        stopTargetAnimation();
+        mTargetAnimations.stop();
         hideChevrons();
         hideTargets(animate);
         mHandleDrawable.setX(0);
@@ -721,9 +791,9 @@
     }
 
     private void handleDown(MotionEvent event) {
-       if (!trySwitchToFirstTouchState(event)) {
+       if (!trySwitchToFirstTouchState(event.getX(), event.getY())) {
             mDragging = false;
-            stopTargetAnimation();
+            mTargetAnimations.cancel();
             ping();
         }
     }
@@ -747,14 +817,11 @@
     }
 
     private void handleMove(MotionEvent event) {
-        if (!mDragging) {
-            trySwitchToFirstTouchState(event);
-            return;
-        }
-
         int activeTarget = -1;
         final int historySize = event.getHistorySize();
-        final boolean singleTarget = mTargetDrawables.size() == 1;
+        ArrayList<TargetDrawable> targets = mTargetDrawables;
+        int ntargets = targets.size();
+        final boolean singleTarget = ntargets == 1;
         float x = 0.0f;
         float y = 0.0f;
         for (int k = 0; k < historySize + 1; k++) {
@@ -768,25 +835,29 @@
             float limitX = tx * scale;
             float limitY = ty * scale;
 
-            if (singleTarget) {
-                // Snap to outer ring if there's only one target
-                float snapRadius = mOuterRadius - mSnapMargin;
-                if (touchRadius > snapRadius) {
-                    activeTarget = 0;
-                }
+            if (!mDragging) {
+                trySwitchToFirstTouchState(eventX, eventY);
             } else {
-                // If there's more than one target, snap to the closest one less than hitRadius away.
-                float best = Float.MAX_VALUE;
-                final float hitRadius2 = mHitRadius * mHitRadius;
-                for (int i = 0; i < mTargetDrawables.size(); i++) {
-                    // Snap to the first target in range
-                    TargetDrawable target = mTargetDrawables.get(i);
-                    float dx = limitX - target.getX();
-                    float dy = limitY - target.getY();
-                    float dist2 = dx*dx + dy*dy;
-                    if (target.isEnabled() && dist2 < hitRadius2 && dist2 < best) {
-                        activeTarget = i;
-                        best = dist2;
+                if (singleTarget) {
+                    // Snap to outer ring if there's only one target
+                    float snapRadius = mOuterRadius - mSnapMargin;
+                    if (touchRadius > snapRadius) {
+                        activeTarget = 0;
+                    }
+                } else {
+                    // For more than one target, snap to the closest one less than hitRadius away.
+                    float best = Float.MAX_VALUE;
+                    final float hitRadius2 = mHitRadius * mHitRadius;
+                    // Find first target in range
+                    for (int i = 0; i < ntargets; i++) {
+                        TargetDrawable target = targets.get(i);
+                        float dx = limitX - target.getX();
+                        float dy = limitY - target.getY();
+                        float dist2 = dx*dx + dy*dy;
+                        if (target.isEnabled() && dist2 < hitRadius2 && dist2 < best) {
+                            activeTarget = i;
+                            best = dist2;
+                        }
                     }
                 }
             }
@@ -794,16 +865,25 @@
             y = limitY;
         }
 
+        if (!mDragging) {
+            return;
+        }
+
         if (activeTarget != -1) {
             switchToState(STATE_SNAP, x,y);
-            TargetDrawable target = mTargetDrawables.get(activeTarget);
+            TargetDrawable target = targets.get(activeTarget);
             float newX = singleTarget ? x : target.getX();
             float newY = singleTarget ? y : target.getY();
             moveHandleTo(newX, newY, false);
+            mHandleAnimations.cancel();
+            mHandleDrawable.setAlpha(0.0f);
         } else {
             switchToState(STATE_TRACKING, x, y);
+            if (mActiveTarget != -1) {
+                mHandleAnimations.cancel();
+                mHandleDrawable.setAlpha(1.0f);
+            }
             moveHandleTo(x, y, false);
-            mHandleDrawable.setAlpha(1.0f);
         }
 
         // Draw handle outside parent's bounds
@@ -812,18 +892,16 @@
         if (mActiveTarget != activeTarget) {
             // Defocus the old target
             if (mActiveTarget != -1) {
-                TargetDrawable target = mTargetDrawables.get(mActiveTarget);
+                TargetDrawable target = targets.get(mActiveTarget);
                 if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                     target.setState(TargetDrawable.STATE_INACTIVE);
-                    mHandleDrawable.setAlpha(1.0f);
                 }
             }
             // Focus the new target
             if (activeTarget != -1) {
-                TargetDrawable target = mTargetDrawables.get(activeTarget);
+                TargetDrawable target = targets.get(activeTarget);
                 if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
                     target.setState(TargetDrawable.STATE_FOCUSED);
-                    mHandleDrawable.setAlpha(0.0f);
                 }
                 dispatchGrabbedEvent(activeTarget);
                 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
@@ -877,9 +955,7 @@
         }
     }
 
-    private boolean trySwitchToFirstTouchState(MotionEvent event) {
-        final float x = event.getX();
-        final float y = event.getY();
+    private boolean trySwitchToFirstTouchState(float x, float y) {
         final float tx = x - mWaveCenterX;
         final float ty = y - mWaveCenterY;
         if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledTapRadiusSquared()) {
@@ -892,9 +968,9 @@
         return false;
     }
 
-    private void assignDefaultsIfNeeded(float centerX, float centerY) {
+    private void assignDefaultsIfNeeded() {
         if (mOuterRadius == 0.0f) {
-            mOuterRadius = 0.5f*(float) Math.hypot(centerX, centerY);
+            mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
         }
         if (mHitRadius == 0.0f) {
             // Use the radius of inscribed circle of the first target.
@@ -941,6 +1017,7 @@
         super.onLayout(changed, left, top, right, bottom);
         final int width = right - left;
         final int height = bottom - top;
+
         // Target placement width/height. This puts the targets on the greater of the ring
         // width or the specified outer radius.
         final float placementWidth = Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
@@ -950,8 +1027,6 @@
         float newWaveCenterY = mVerticalOffset + mVerticalInset
                 + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
 
-        assignDefaultsIfNeeded(newWaveCenterX, newWaveCenterY);
-
         if (mInitialLayout) {
             hideChevrons();
             hideTargets(false);
@@ -976,9 +1051,12 @@
 
     private void updateTargetPositions(float centerX, float centerY) {
         // Reposition the target drawables if the view changed.
-        for (int i = 0; i < mTargetDrawables.size(); i++) {
-            final TargetDrawable targetIcon = mTargetDrawables.get(i);
-            double angle = -2.0f * Math.PI * i / mTargetDrawables.size();
+        ArrayList<TargetDrawable> targets = mTargetDrawables;
+        final int size = targets.size();
+        final float alpha = (float) (-2.0f * Math.PI / size);
+        for (int i = 0; i < size; i++) {
+            final TargetDrawable targetIcon = targets.get(i);
+            final float angle = alpha * i;
             targetIcon.setPositionX(centerX);
             targetIcon.setPositionY(centerY);
             targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
@@ -987,7 +1065,10 @@
     }
 
     private void updateChevronPositions(float centerX, float centerY) {
-        for (TargetDrawable target : mChevronDrawables) {
+        ArrayList<TargetDrawable> chevrons = mChevronDrawables;
+        final int size = chevrons.size();
+        for (int i = 0; i < size; i++) {
+            TargetDrawable target = chevrons.get(i);
             if (target != null) {
                 target.setPositionX(centerX);
                 target.setPositionY(centerY);
@@ -996,7 +1077,10 @@
     }
 
     private void hideChevrons() {
-        for (TargetDrawable chevron : mChevronDrawables) {
+        ArrayList<TargetDrawable> chevrons = mChevronDrawables;
+        final int size = chevrons.size();
+        for (int i = 0; i < size; i++) {
+            TargetDrawable chevron = chevrons.get(i);
             if (chevron != null) {
                 chevron.setAlpha(0.0f);
             }
@@ -1006,14 +1090,18 @@
     @Override
     protected void onDraw(Canvas canvas) {
         mOuterRing.draw(canvas);
-        for (TargetDrawable target : mTargetDrawables) {
+        final int ntargets = mTargetDrawables.size();
+        for (int i = 0; i < ntargets; i++) {
+            TargetDrawable target = mTargetDrawables.get(i);
             if (target != null) {
                 target.draw(canvas);
             }
         }
-        for (TargetDrawable target : mChevronDrawables) {
-            if (target != null) {
-                target.draw(canvas);
+        final int nchevrons = mChevronDrawables.size();
+        for (int i = 0; i < nchevrons; i++) {
+            TargetDrawable chevron = mChevronDrawables.get(i);
+            if (chevron != null) {
+                chevron.draw(canvas);
             }
         }
         mHandleDrawable.draw(canvas);
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
index bc8a62f..1d502ba 100644
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
@@ -122,7 +122,6 @@
             anim.addListener(listener);
         }
         anim.addListener(mCleanupListener);
-        anim.start();
 
         return tween;
     }
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/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 673c38d..c85b09c 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -332,15 +332,7 @@
 }
 
 TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
-    mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
-    mArabicTypeface = NULL;
-    mHebrewRegularTypeface = NULL;
-    mHebrewBoldTypeface = NULL;
-    mBengaliTypeface = NULL;
-    mThaiTypeface = NULL;
-    mDevanagariRegularTypeface = NULL;
-    mTamilRegularTypeface = NULL;
-    mTamilBoldTypeface = NULL;
+    init();
 
     mFontRec.klass = &harfbuzzSkiaClass;
     mFontRec.userData = 0;
@@ -359,7 +351,19 @@
     mShaperItem.font->userData = &mShapingPaint;
 }
 
-TextLayoutShaper::~TextLayoutShaper() {
+void TextLayoutShaper::init() {
+    mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
+    mArabicTypeface = NULL;
+    mHebrewRegularTypeface = NULL;
+    mHebrewBoldTypeface = NULL;
+    mBengaliTypeface = NULL;
+    mThaiTypeface = NULL;
+    mDevanagariRegularTypeface = NULL;
+    mTamilRegularTypeface = NULL;
+    mTamilBoldTypeface = NULL;
+}
+
+void TextLayoutShaper::unrefTypefaces() {
     SkSafeUnref(mDefaultTypeface);
     SkSafeUnref(mArabicTypeface);
     SkSafeUnref(mHebrewRegularTypeface);
@@ -369,6 +373,10 @@
     SkSafeUnref(mDevanagariRegularTypeface);
     SkSafeUnref(mTamilRegularTypeface);
     SkSafeUnref(mTamilBoldTypeface);
+}
+
+TextLayoutShaper::~TextLayoutShaper() {
+    unrefTypefaces();
     deleteShaperItemGlyphArrays();
 }
 
@@ -983,6 +991,12 @@
     return face;
 }
 
+void TextLayoutShaper::purgeCaches() {
+    mCachedHBFaces.clear();
+    unrefTypefaces();
+    init();
+}
+
 TextLayoutEngine::TextLayoutEngine() {
     mShaper = new TextLayoutShaper();
 #if USE_TEXT_LAYOUT_CACHE
@@ -1018,6 +1032,10 @@
 void TextLayoutEngine::purgeCaches() {
 #if USE_TEXT_LAYOUT_CACHE
     mTextLayoutCache->clear();
+    mShaper->purgeCaches();
+#if DEBUG_GLYPHS
+    ALOGD("Purged TextLayoutEngine caches");
+#endif
 #endif
 }
 
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 027e888..cb15a2a 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -169,6 +169,8 @@
     void computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
             size_t start, size_t count, size_t contextCount, int dirFlags);
 
+    void purgeCaches();
+
 private:
     /**
      * Harfbuzz shaper item
@@ -218,6 +220,9 @@
      */
     UnicodeString mBuffer;
 
+    void init();
+    void unrefTypefaces();
+
     SkTypeface* typefaceForUnichar(const SkPaint* paint, SkTypeface* typeface,
         SkUnichar unichar, HB_Script script);
 
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7146667..d422951 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -703,7 +703,7 @@
 #endif
     uint32_t ref = ident;
     if (resolve) {
-        block = res.resolveReference(&value, block, &ref);
+        block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
 #if THROW_ON_BAD_ID
         if (block == BAD_INDEX) {
             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index d8ec656..82f8984 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -357,19 +357,26 @@
 static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, jintArray colors, jint offset, jint stride,
         jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, SkPaint* paint) {
-    SkBitmap bitmap;
-    SkBitmap::Config config = hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config;
-    bitmap.setConfig(config, width, height);
+    SkBitmap* bitmap = new SkBitmap;
+    bitmap->setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config,
+            width, height);
 
-    if (!bitmap.allocPixels()) {
+    if (!bitmap->allocPixels()) {
+        delete bitmap;
         return;
     }
 
-    if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, bitmap)) {
+    if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap)) {
+        delete bitmap;
         return;
     }
 
-    renderer->drawBitmapData(&bitmap, left, top, paint);
+    renderer->drawBitmapData(bitmap, left, top, paint);
+
+    // If the renderer is a deferred renderer it will own the bitmap
+    if (!renderer->isDeferred()) {
+        delete bitmap;
+    }
 }
 
 static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
@@ -767,11 +774,12 @@
     float transform[16];
     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
 
-    surfaceTexture->updateTexImage();
-    surfaceTexture->getTransformMatrix(transform);
-    GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
+    if (surfaceTexture->updateTexImage() == NO_ERROR) {
+        surfaceTexture->getTransformMatrix(transform);
+        GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
 
-    LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+        LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+    }
 }
 
 static void android_view_GLES20Canvas_updateRenderLayer(JNIEnv* env, jobject clazz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dbc60f9..e3082ea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1551,6 +1551,14 @@
         android:description="@string/permdesc_bindInputMethod"
         android:protectionLevel="signature" />
 
+        <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
+         to ensure that only the system can bind to it.
+         @hide -->
+    <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
+        android:label="@string/permlab_bindAccessibilityService"
+        android:description="@string/permdesc_bindAccessibilityService"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a TextService (e.g. SpellCheckerService)
          to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_TEXT_SERVICE"
diff --git a/core/res/res/anim-land/task_close_enter.xml b/core/res/res/anim-land/task_close_enter.xml
index 805ff6c..facc42b 100644
--- a/core/res/res/anim-land/task_close_enter.xml
+++ b/core/res/res/anim-land/task_close_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromXDelta="-140%" android:toXDelta="0"
+    <translate android:fromXDelta="-120%" android:toXDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotY="50%p" android:pivotX="0%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_close_exit.xml b/core/res/res/anim-land/task_close_exit.xml
index 3e97149..e104c33 100644
--- a/core/res/res/anim-land/task_close_exit.xml
+++ b/core/res/res/anim-land/task_close_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromXDelta="0" android:toXDelta="140%"
+    <translate android:fromXDelta="0" android:toXDelta="120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotY="50%p" android:pivotX="100%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_open_enter.xml b/core/res/res/anim-land/task_open_enter.xml
index fb1c5d6..dc7c7a9 100644
--- a/core/res/res/anim-land/task_open_enter.xml
+++ b/core/res/res/anim-land/task_open_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromXDelta="140%" android:toXDelta="0"
+    <translate android:fromXDelta="120%" android:toXDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotY="50%p" android:pivotX="100%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_open_exit.xml b/core/res/res/anim-land/task_open_exit.xml
index 8d15324..701afa6 100644
--- a/core/res/res/anim-land/task_open_exit.xml
+++ b/core/res/res/anim-land/task_open_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromXDelta="0" android:toXDelta="-140%"
+    <translate android:fromXDelta="0" android:toXDelta="-120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotY="50%p" android:pivotX="0%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-port/task_close_enter.xml b/core/res/res/anim-port/task_close_enter.xml
index 1806eed..9a747a1 100644
--- a/core/res/res/anim-port/task_close_enter.xml
+++ b/core/res/res/anim-port/task_close_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromYDelta="-140%" android:toYDelta="0"
+    <translate android:fromYDelta="-120%" android:toYDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="0%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-port/task_close_exit.xml b/core/res/res/anim-port/task_close_exit.xml
index 958a7a2..35b1aa3 100644
--- a/core/res/res/anim-port/task_close_exit.xml
+++ b/core/res/res/anim-port/task_close_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="140%"
+    <translate android:fromYDelta="0" android:toYDelta="120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotX="50%p" android:pivotY="100%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-port/task_open_enter.xml b/core/res/res/anim-port/task_open_enter.xml
index 54a2d93..5e4ae18 100644
--- a/core/res/res/anim-port/task_open_enter.xml
+++ b/core/res/res/anim-port/task_open_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromYDelta="140%" android:toYDelta="0"
+    <translate android:fromYDelta="120%" android:toYDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="100%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-port/task_open_exit.xml b/core/res/res/anim-port/task_open_exit.xml
index 18e6550b..0ba35d7 100644
--- a/core/res/res/anim-port/task_open_exit.xml
+++ b/core/res/res/anim-port/task_open_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="-140%"
+    <translate android:fromYDelta="0" android:toYDelta="-120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotX="50%p" android:pivotY="0%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_close_enter.xml b/core/res/res/anim-sw720dp/task_close_enter.xml
index 1806eed..9a747a1 100644
--- a/core/res/res/anim-sw720dp/task_close_enter.xml
+++ b/core/res/res/anim-sw720dp/task_close_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromYDelta="-140%" android:toYDelta="0"
+    <translate android:fromYDelta="-120%" android:toYDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="0%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_close_exit.xml b/core/res/res/anim-sw720dp/task_close_exit.xml
index 958a7a2..35b1aa3 100644
--- a/core/res/res/anim-sw720dp/task_close_exit.xml
+++ b/core/res/res/anim-sw720dp/task_close_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="140%"
+    <translate android:fromYDelta="0" android:toYDelta="120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotX="50%p" android:pivotY="100%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_open_enter.xml b/core/res/res/anim-sw720dp/task_open_enter.xml
index 54a2d93..5e4ae18 100644
--- a/core/res/res/anim-sw720dp/task_open_enter.xml
+++ b/core/res/res/anim-sw720dp/task_open_enter.xml
@@ -23,21 +23,20 @@
     <alpha android:fromAlpha="0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:startOffset="300"
+            android:duration="400"/>
 
-    <translate android:fromYDelta="140%" android:toYDelta="0"
+    <translate android:fromYDelta="120%" android:toYDelta="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="150"
-            android:duration="350"/>
+            android:interpolator="@interpolator/decelerate_quint"
+            android:startOffset="300"
+            android:duration="400" />
 
-    <scale android:fromXScale=".6" android:toXScale="1.0"
-            android:fromYScale=".6" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="50%p"
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+            android:fromYScale=".5" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="100%p"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/decelerate_quad"
-            android:startOffset="150"
-            android:duration="350" />
-
+            android:startOffset="300"
+            android:duration="400" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_open_exit.xml b/core/res/res/anim-sw720dp/task_open_exit.xml
index 18e6550b..0ba35d7 100644
--- a/core/res/res/anim-sw720dp/task_open_exit.xml
+++ b/core/res/res/anim-sw720dp/task_open_exit.xml
@@ -19,25 +19,25 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
             android:interpolator="@interpolator/accelerate_quad"
-            android:duration="350"/>
+            android:duration="300"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="-140%"
+    <translate android:fromYDelta="0" android:toYDelta="-120%"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quad"
-            android:duration="350"/>
+            android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="300"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.6"
-            android:fromYScale="1.0" android:toYScale="0.6"
+    <scale android:fromXScale="1.0" android:toXScale="0.5"
+            android:fromYScale="1.0" android:toYScale="0.5"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="350" />
+            android:pivotX="50%p" android:pivotY="0%p"
+            android:interpolator="@interpolator/accelerate_quad"
+            android:duration="300" />
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:duration="500" />
+            android:duration="700" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index ee7ab60..12a1bdf 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -21,5 +21,5 @@
         android:detachWallpaper="true" android:shareInterpolator="false" android:zAdjustment="normal">
     <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
             android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:duration="300"/>
+            android:duration="375"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index 905743e..b0f97d1 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -20,13 +20,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false" android:zAdjustment="top">
         <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-                android:interpolator="@interpolator/linear"
+                android:interpolator="@interpolator/accelerate_decelerate"
                 android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
                 android:duration="200" />
-        <scale android:fromXScale="1.0" android:toXScale="0.4"
-                android:fromYScale="1.0" android:toYScale="0.4"
+        <scale android:fromXScale="1.0" android:toXScale="0.5"
+                android:fromYScale="1.0" android:toYScale="0.5"
                 android:pivotX="50%p" android:pivotY="50%p"
                 android:interpolator="@interpolator/decelerate_quad"
                 android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-                android:duration="250" />
+                android:duration="375" />
 </set>
\ No newline at end of file
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/ic_settings_language.png b/core/res/res/drawable-hdpi/ic_settings_language.png
new file mode 100755
index 0000000..f635b2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_settings_language.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-large-nodpi/default_wallpaper.jpg b/core/res/res/drawable-large-nodpi/default_wallpaper.jpg
deleted file mode 100644
index 355286e..0000000
--- a/core/res/res/drawable-large-nodpi/default_wallpaper.jpg
+++ /dev/null
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/ic_settings_language.png b/core/res/res/drawable-mdpi/ic_settings_language.png
new file mode 100644
index 0000000..f8aca67
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_settings_language.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-nodpi/default_wallpaper.jpg b/core/res/res/drawable-nodpi/default_wallpaper.jpg
index 7e92243..d7475b4c 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.jpg
+++ b/core/res/res/drawable-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
index f46c6c6..63b53b8 100644
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo_alt.png b/core/res/res/drawable-nodpi/platlogo_alt.png
index 63b53b8..f46c6c6 100644
--- a/core/res/res/drawable-nodpi/platlogo_alt.png
+++ b/core/res/res/drawable-nodpi/platlogo_alt.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg
new file mode 100644
index 0000000..03a14c0
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg
new file mode 100644
index 0000000..543d118
--- /dev/null
+++ b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.jpg
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/default_wallpaper.jpg b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
new file mode 100644
index 0000000..5b8d1d5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_settings_language.png b/core/res/res/drawable-xhdpi/ic_settings_language.png
new file mode 100644
index 0000000..2c42db3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_settings_language.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-xlarge-nodpi/default_wallpaper.jpg b/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
deleted file mode 100644
index 355286e..0000000
--- a/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
+++ /dev/null
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-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
index 68499f4b2..3bdc7b6 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
@@ -39,7 +39,16 @@
             android:layout_height="wrap_content"
             android:layout_marginBottom="24dip">
 
-            <!-- Music transport control underneath -->
+            <include layout="@layout/keyguard_screen_status_land"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="50dip"
+                android:layout_marginTop="50dip"
+                android:layout_marginBottom="50dip"
+                android:layout_marginRight="64dip"
+                android:layout_alignParentTop="true"
+                android:layout_alignParentLeft="true"/>
+
             <include android:id="@+id/transport"
                 layout="@layout/keyguard_transport_control"
                 android:layout_row="0"
@@ -51,16 +60,6 @@
                 android:layout_height="512dip"
                 />
 
-            <include layout="@layout/keyguard_screen_status_land"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="50dip"
-                android:layout_marginTop="50dip"
-                android:layout_marginBottom="50dip"
-                android:layout_marginRight="64dip"
-                android:layout_alignParentTop="true"
-                android:layout_alignParentLeft="true"/>
-
         </RelativeLayout>
 
     </RelativeLayout>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
index 086757d..bd9de20 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
@@ -26,7 +26,7 @@
     <!-- top: status -->
     <RelativeLayout
         android:layout_height="0dip"
-        android:layout_weight="1"
+        android:layout_weight="0.40"
         android:layout_width="match_parent"
         android:gravity="center">
 
@@ -35,6 +35,14 @@
             android:layout_height="wrap_content"
             android:gravity="center">
 
+            <include layout="@layout/keyguard_screen_status_land"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="50dip"
+                android:layout_marginTop="50dip"
+                android:layout_alignParentTop="true"
+                android:layout_alignParentLeft="true"/>
+
             <!-- Music transport control -->
             <include android:id="@+id/transport"
                 layout="@layout/keyguard_transport_control"
@@ -47,23 +55,13 @@
                 android:layout_height="512dip"
                 />
 
-            <include layout="@layout/keyguard_screen_status_land"
-                android:layout_width="wrap_content"
-                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"/>
-
         </RelativeLayout>
 
     </RelativeLayout>
 
     <!-- bottom: lock pattern, emergency dialer and forgot pattern button -->
     <RelativeLayout
-        android:layout_weight="1"
+        android:layout_weight="0.60"
         android:layout_width="match_parent"
         android:layout_height="0dip"
         android:gravity="center">
@@ -77,7 +75,6 @@
             <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
                 android:layout_width="354dip"
                 android:layout_height="354dip"
-                android:layout_marginTop="50dip"
             />
 
             <!-- Emergency and forgot pattern buttons. -->
diff --git a/core/res/res/layout/notification_action.xml b/core/res/res/layout/notification_action.xml
index 28812a9..33cbab9 100644
--- a/core/res/res/layout/notification_action.xml
+++ b/core/res/res/layout/notification_action.xml
@@ -17,11 +17,14 @@
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     style="?android:attr/borderlessButtonStyle" 
     android:id="@+id/action0"
-    android:layout_width="match_parent"
+    android:layout_width="0dp"
     android:layout_height="48dp"
+    android:layout_weight="1"
     android:gravity="left|center_vertical"
     android:drawablePadding="8dp"
     android:paddingLeft="8dp"
     android:textColor="#ccc"
     android:textSize="14dp"
+    android:singleLine="true"
+    android:ellipsize="end"
     />
diff --git a/core/res/res/values-w500dp/bools.xml b/core/res/res/layout/notification_action_list.xml
similarity index 62%
rename from core/res/res/values-w500dp/bools.xml
rename to core/res/res/layout/notification_action_list.xml
index f53fd39..fa0a8c8 100644
--- a/core/res/res/values-w500dp/bools.xml
+++ b/core/res/res/layout/notification_action_list.xml
@@ -14,6 +14,15 @@
      limitations under the License.
 -->
 
-<resources>
-    <bool name="activity_chooser_view_has_default_activity">true</bool>
-</resources>
\ No newline at end of file
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/actions"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:visibility="gone"
+    android:showDividers="middle"
+    android:divider="?android:attr/listDivider"
+    >
+    <!-- actions will be added here -->
+</LinearLayout>
diff --git a/core/res/res/layout/notification_action_tombstone.xml b/core/res/res/layout/notification_action_tombstone.xml
index e61e15f..992b37c 100644
--- a/core/res/res/layout/notification_action_tombstone.xml
+++ b/core/res/res/layout/notification_action_tombstone.xml
@@ -15,12 +15,18 @@
 -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
+    style="?android:attr/borderlessButtonStyle" 
     android:id="@+id/action0"
-    android:layout_width="match_parent"
+    android:layout_width="0dp"
     android:layout_height="48dp"
+    android:layout_weight="1"
     android:gravity="left|center_vertical"
     android:drawablePadding="8dp"
     android:paddingLeft="8dp"
-    android:textColor="#666"
+    android:textColor="#ccc"
     android:textSize="14dp"
+    android:singleLine="true"
+    android:ellipsize="end"
+    android:alpha="0.5"
+    android:enabled="false"
     />
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
index 3692a4d..ed680a9 100644
--- a/core/res/res/layout/notification_template_base.xml
+++ b/core/res/res/layout/notification_template_base.xml
@@ -38,14 +38,15 @@
         android:orientation="vertical"
         android:paddingLeft="12dp"
         android:paddingRight="12dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp"
-        android:gravity="center_vertical"
+        android:paddingTop="2dp"
+        android:paddingBottom="2dp"
+        android:gravity="top"
         >
         <LinearLayout
             android:id="@+id/line1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:paddingTop="6dp"
             android:orientation="horizontal"
             >
             <TextView android:id="@+id/title"
@@ -85,8 +86,15 @@
             android:ellipsize="marquee"
             android:visibility="gone"
             />
+        <ProgressBar
+            android:id="@android:id/progress"
+            android:layout_width="match_parent"
+            android:layout_height="12dp"
+            android:visibility="gone"
+            style="?android:attr/progressBarStyleHorizontal"
+            />
         <TextView android:id="@+id/overflow_title"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:singleLine="true"
@@ -123,21 +131,14 @@
                 />
             <ImageView android:id="@+id/right_icon"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_gravity="center"
                 android:layout_weight="0"
-                android:scaleType="center"
+                android:scaleType="centerInside"
                 android:paddingLeft="8dp"
                 android:visibility="gone"
                 android:drawableAlpha="180"
                 />
         </LinearLayout>
-        <ProgressBar
-            android:id="@android:id/progress"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            style="?android:attr/progressBarStyleHorizontal"
-            />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_big_base.xml b/core/res/res/layout/notification_template_big_base.xml
index 378161c..f2c204f 100644
--- a/core/res/res/layout/notification_template_big_base.xml
+++ b/core/res/res/layout/notification_template_big_base.xml
@@ -38,9 +38,9 @@
         android:orientation="vertical"
         android:paddingLeft="12dp"
         android:paddingRight="12dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp"
-        android:gravity="center_vertical"
+        android:paddingTop="2dp"
+        android:paddingBottom="2dp"
+        android:gravity="top"
         >
         <LinearLayout
             android:layout_width="match_parent"
@@ -52,6 +52,7 @@
                 android:id="@+id/line1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:paddingTop="6dp"
                 android:orientation="horizontal"
                 >
                 <TextView android:id="@+id/title"
@@ -126,10 +127,10 @@
                     />
                 <ImageView android:id="@+id/right_icon"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
+                    android:layout_height="match_parent"
                     android:layout_gravity="center"
                     android:layout_weight="0"
-                    android:scaleType="center"
+                    android:scaleType="centerInside"
                     android:paddingLeft="8dp"
                     android:visibility="gone"
                     android:drawableAlpha="180"
@@ -138,19 +139,16 @@
             <ProgressBar
                 android:id="@android:id/progress"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
+                android:layout_height="12dp"
                 android:visibility="gone"
                 style="?android:attr/progressBarStyleHorizontal"
                 />
         </LinearLayout>
-        <LinearLayout
+        <include
+            layout="@layout/notification_action_list"
             android:id="@+id/actions"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:visibility="gone"
-                >
-                <!-- actions will be added here -->
-        </LinearLayout>
+            />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_big_picture.xml b/core/res/res/layout/notification_template_big_picture.xml
index 51e46b2..9b896a4 100644
--- a/core/res/res/layout/notification_template_big_picture.xml
+++ b/core/res/res/layout/notification_template_big_picture.xml
@@ -27,11 +27,19 @@
         android:id="@+id/big_picture"
         android:layout_width="match_parent"
         android:layout_height="192dp"
+        android:layout_marginTop="64dp"
+        android:layout_gravity="bottom"
         android:scaleType="centerCrop"
         />
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="6dp"
+        android:layout_marginTop="64dp"
+        android:scaleType="fitXY"
+        android:src="@drawable/title_bar_shadow"
+        />
     <include layout="@layout/notification_template_base" 
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="192dp"
         />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_big_text.xml b/core/res/res/layout/notification_template_big_text.xml
index 77a5f11..304f2b5 100644
--- a/core/res/res/layout/notification_template_big_text.xml
+++ b/core/res/res/layout/notification_template_big_text.xml
@@ -36,9 +36,9 @@
         android:orientation="vertical"
         android:paddingLeft="12dp"
         android:paddingRight="12dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp"
-        android:gravity="center_vertical"
+        android:paddingTop="2dp"
+        android:paddingBottom="2dp"
+        android:gravity="top"
         >
         <LinearLayout
             android:layout_width="match_parent"
@@ -50,6 +50,7 @@
                 android:id="@+id/line1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:paddingTop="6dp"
                 android:orientation="horizontal"
                 android:layout_gravity="top"
                 android:layout_weight="0"
@@ -92,11 +93,18 @@
                 android:layout_weight="0"
                 android:visibility="gone"
                 />
+            <ProgressBar
+                android:id="@android:id/progress"
+                android:layout_width="match_parent"
+                android:layout_height="12dp"
+                android:visibility="gone"
+                android:layout_weight="0"
+                style="?android:attr/progressBarStyleHorizontal"
+                />
             <TextView android:id="@+id/big_text"
                 android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
-                android:layout_marginTop="2dp"
                 android:layout_marginBottom="2dp"
                 android:singleLine="false"
                 android:visibility="gone"
@@ -105,18 +113,15 @@
                 android:layout_weight="1"
                 />
         </LinearLayout>
-        <LinearLayout
-            android:id="@+id/actions"
+        <include
+            layout="@layout/notification_action_list"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:orientation="vertical"
             android:visibility="gone"
             android:layout_weight="1"
-                >
-                <!-- actions will be added here -->
-        </LinearLayout>
+            />
         <TextView android:id="@+id/overflow_title"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:singleLine="true"
@@ -130,7 +135,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:layout_weight="1"
+            android:layout_weight="0"
             >
             <TextView android:id="@+id/text"
                 android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
@@ -154,22 +159,14 @@
                 />
             <ImageView android:id="@+id/right_icon"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_gravity="center"
                 android:layout_weight="0"
-                android:scaleType="center"
+                android:scaleType="centerInside"
                 android:paddingLeft="8dp"
                 android:visibility="gone"
                 android:drawableAlpha="180"
                 />
         </LinearLayout>
-        <ProgressBar
-            android:id="@android:id/progress"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            android:layout_weight="0"
-            style="?android:attr/progressBarStyleHorizontal"
-            />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_inbox.xml b/core/res/res/layout/notification_template_inbox.xml
index 05ec1d8..121a81c 100644
--- a/core/res/res/layout/notification_template_inbox.xml
+++ b/core/res/res/layout/notification_template_inbox.xml
@@ -38,14 +38,15 @@
         android:orientation="vertical"
         android:paddingLeft="12dp"
         android:paddingRight="12dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp"
-        android:gravity="center_vertical"
+        android:paddingTop="2dp"
+        android:paddingBottom="2dp"
+        android:gravity="top"
         >
         <LinearLayout
             android:id="@+id/line1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:paddingTop="6dp"
             android:orientation="horizontal"
             android:layout_weight="0"
             >
@@ -87,14 +88,20 @@
             android:visibility="gone"
             android:layout_weight="0"
             />
+        <ProgressBar
+            android:id="@android:id/progress"
+            android:layout_width="match_parent"
+            android:layout_height="12dp"
+            android:visibility="gone"
+            android:layout_weight="0"
+            style="?android:attr/progressBarStyleHorizontal"
+            />
         <TextView android:id="@+id/inbox_text0"
             android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:singleLine="true"
             android:ellipsize="end"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
             android:visibility="gone"
             android:layout_weight="1"
             />
@@ -104,8 +111,6 @@
             android:layout_height="0dp"
             android:singleLine="true"
             android:ellipsize="end"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
             android:visibility="gone"
             android:layout_weight="1"
             />
@@ -115,8 +120,6 @@
             android:layout_height="0dp"
             android:singleLine="true"
             android:ellipsize="end"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
             android:visibility="gone"
             android:layout_weight="1"
             />
@@ -126,8 +129,6 @@
             android:layout_height="0dp"
             android:singleLine="true"
             android:ellipsize="end"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
             android:visibility="gone"
             android:layout_weight="1"
             />
@@ -137,23 +138,46 @@
             android:layout_height="0dp"
             android:singleLine="true"
             android:ellipsize="end"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
             android:visibility="gone"
             android:layout_weight="1"
             />
-        <LinearLayout
+        <TextView android:id="@+id/inbox_text5"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text6"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_more"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            android:text="@android:string/ellipsis"
+            />
+        <include
+            layout="@layout/notification_action_list"
             android:id="@+id/actions"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
             android:layout_weight="0"
-            android:visibility="gone"
-           >
-          <!-- actions will be added here -->
-        </LinearLayout>
+            />
         <TextView android:id="@+id/overflow_title"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:singleLine="true"
@@ -191,22 +215,14 @@
                 />
             <ImageView android:id="@+id/right_icon"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_gravity="center"
                 android:layout_weight="0"
-                android:scaleType="center"
+                android:scaleType="centerInside"
                 android:paddingLeft="8dp"
                 android:visibility="gone"
                 android:drawableAlpha="180"
                 />
         </LinearLayout>
-        <ProgressBar
-            android:id="@android:id/progress"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            android:layout_weight="0"
-            style="?android:attr/progressBarStyleHorizontal"
-            />
     </LinearLayout>
 </FrameLayout>
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/core/res/res/layout/time_picker_holo.xml b/core/res/res/layout/time_picker_holo.xml
index 91e66bc..7b91022 100644
--- a/core/res/res/layout/time_picker_holo.xml
+++ b/core/res/res/layout/time_picker_holo.xml
@@ -44,6 +44,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
+        android:importantForAccessibility="no"
         />
 
     <!-- minute -->
diff --git a/core/res/res/raw/accessibility_gestures.bin b/core/res/res/raw/accessibility_gestures.bin
index f7e6615..acd7993 100644
--- a/core/res/res/raw/accessibility_gestures.bin
+++ b/core/res/res/raw/accessibility_gestures.bin
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b739835..732b0ab 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -176,9 +176,9 @@
     <string name="permgrouplab_network" msgid="5808983377727109831">"Netwerkkommunikasie"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"Kry toegang tot verskeie netwerkfunksies."</string>
     <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"Bluetooth"</string>
-    <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Toegangstoestelle en netwerke deur Bluetooth."</string>
+    <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Kry toegang tot toestelle en netwerke deur Bluetooth."</string>
     <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Kortreeks-netwerke"</string>
-    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Toegangstoestelle met kortreeks-netwerke soos NFC."</string>
+    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Kry toegang tot toestelle met kortreeks-netwerke soos NFC."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"Oudio-instellings"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"Verander oudio-instellings."</string>
     <string name="permgrouplab_affectsBattery" msgid="6209246653424798033">"Affekteer battery"</string>
@@ -561,7 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"Laat die program toe om die USB-berging se inhoud te lees, wat foto\'s en media kan insluit."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"Laat die program toe om die SD-kaart se inhoud te lees, wat foto\'s en media kan insluit."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"verander of vee die inhoud van jou USB-berging uit"</string>
-    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Verander of skrap die inhoud van jou SD-kaart"</string>
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Verander of vee die inhoud van jou SD-kaart uit"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Laat die program toe om die USB-geheue te skryf."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"verander/vee uit interne mediabergingsinhoud"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 25cfb7f..4ec0197 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -185,10 +185,10 @@
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"Bruge funktioner, der hurtigt kan dræne batteriet."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"Direkte adgang til kalender og begivenheder."</string>
-    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Læs brugerordbog"</string>
-    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Læs ord i brugerordbogen."</string>
-    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Skriv brugerordbog"</string>
-    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Føj ord til brugerordbogen."</string>
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Læse brugerordbog"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Læse ord i brugerordbogen."</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Skrive brugerordbog"</string>
+    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Føje ord til brugerordbogen."</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Bogmærker og historik"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Direkte adgang til bogmærker og browserhistorik."</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Alarm"</string>
@@ -561,7 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"Tillader, at appen læser USB-lagerets indhold, herunder billeder og mediefiler."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"Tillader, at appen læser SD-kortets indhold, som kan omfatte billeder og mediefiler."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ændre eller slette indhold på USB-lager"</string>
-    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"skift eller slet indholdet på dit SD-kort"</string>
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ændre eller slette indholdet på dit SD-kort"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Lader appen skrive til USB."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Rediger/slet internt medielagringsindhold"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 3d51570..4ca2b86 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1091,7 +1091,7 @@
     <string name="usb_storage_error_message" product="nosdcard" msgid="3017045217365540658">"Bei der Verwendung Ihres USB-Speichers als USB-Massenspeicher ist ein Problem aufgetreten."</string>
     <string name="usb_storage_error_message" product="default" msgid="2876018512716970313">"Bei der Verwendung Ihrer SD-Karte als USB-Massenspeicher ist ein Problem aufgetreten."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB-Verbindung"</string>
-    <string name="usb_storage_notification_message" msgid="939822783828183763">"Zum Kopieren von Dateien auf den/von dem Computer berühren"</string>
+    <string name="usb_storage_notification_message" msgid="939822783828183763">"Zum Kopieren von Dateien berühren"</string>
     <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB-Speicher deaktivieren"</string>
     <string name="usb_storage_stop_notification_message" msgid="1656852098555623822">"Zum Deaktivieren des USB-Speichers berühren"</string>
     <string name="usb_storage_stop_title" msgid="660129851708775853">"USB-Speicher in Verwendung"</string>
@@ -1114,7 +1114,7 @@
     <string name="extmedia_format_message" product="default" msgid="14131895027543830">"Alle Daten auf Ihrer Karte gehen verloren."</string>
     <string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-Debugging"</string>
-    <string name="adb_active_notification_message" msgid="1016654627626476142">"Zum Deaktivieren von USB-Debugging tippen"</string>
+    <string name="adb_active_notification_message" msgid="1016654627626476142">"Zum Deaktivieren von USB-Debugging berühren"</string>
     <string name="select_input_method" msgid="4653387336791222978">"Eingabemethode wählen"</string>
     <string name="configure_input_methods" msgid="9091652157722495116">"Eingabemethoden einrichten"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Physische Tastatur"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0da38aa..72132dd 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1311,7 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"Enviando..."</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"¿Deseas iniciar el navegador?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"¿Aceptar la llamada?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Siempre"</string>
     <string name="activity_resolver_use_once" msgid="405646673463328329">"Solo una vez"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 616b9be..5bca912 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -185,12 +185,9 @@
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"Menggunakan fitur yang dapat menguras baterai dengan cepat."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"Akses langsung ke kalender dan acara."</string>
-    <!-- no translation found for permgrouplab_dictionary (4148597128843641379) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_dictionary (7921166355964764490) -->
-    <skip />
-    <!-- no translation found for permgrouplab_writeDictionary (8090237702432576788) -->
-    <skip />
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Membaca Kamus Pengguna"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Membaca kata dalam kamus pengguna."</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Menulis Kamus Pengguna"</string>
     <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Menambahkan kata ke kamus pengguna."</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Bookmark dan Riwayat"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Akses langsung ke bookmark dan riwayat browser."</string>
@@ -564,8 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"Izinkan aplikasi membaca konten penyimpanan USB, yang mungkin mencakup foto dan media."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"Izinkan aplikasi membaca konten kartu SD, yang mungkin mencakup foto dan media."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ubah/hapus konten pympanan USB"</string>
-    <!-- no translation found for permlab_sdcardWrite (8805693630050458763) -->
-    <skip />
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"mengubah atau menghapus konten kartu SD Anda"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Mengizinkan apl menulis ke penyimpanan USB."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubah/hapus konten penyimpanan media internal"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 3c705ec..f5132c1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1311,7 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"Siunčiama…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Paleisti naršyklę?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Priimti skambutį?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Visada"</string>
     <string name="activity_resolver_use_once" msgid="405646673463328329">"Tik kartą"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 0b96365..e6c8d99 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1311,7 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"Notiek sūtīšana…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Vai palaist pārlūkprogrammu?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vai atbildēt uz zvanu?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Vienmēr"</string>
     <string name="activity_resolver_use_once" msgid="405646673463328329">"Tikai vienreiz"</string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 47aa00a..1ab7002 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -177,7 +177,7 @@
     <string name="permgroupdesc_network" msgid="4478299413241861987">"Akses pelbagai ciri rangkaian."</string>
     <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"Bluetooth"</string>
     <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Akses peranti dan rangkaian melalui Bluetooth."</string>
-    <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Rangkaian jarak-pendek"</string>
+    <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Rangkaian jarak-dekat"</string>
     <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Akses peranti melalui rangkaian jarak dekat seperti NFC."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"Tetapan Audio"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"Tukar tetapan audio."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4bb3b2f..e7a809c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -178,7 +178,7 @@
     <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"Bluetooth"</string>
     <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Aceder a dispositivos e redes através de Bluetooth."</string>
     <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Redes de Curto Alcance"</string>
-    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Aceder a dispositivos através de redes de curto alcance como NFC."</string>
+    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Aceder a dispositivos através de redes de curto alcance tal como a NFC."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"Definições de Áudio"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"Alterar as definições de áudio."</string>
     <string name="permgrouplab_affectsBattery" msgid="6209246653424798033">"Afetar a Bateria"</string>
@@ -1311,7 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"A enviar..."</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Iniciar Navegador?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Aceitar chamada?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
     <string name="activity_resolver_use_once" msgid="405646673463328329">"Só Uma Vez"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4236d50..84ce4bb 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1104,7 +1104,7 @@
     <string name="dlg_error_title" msgid="7323658469626514207">"Falha na operação do USB"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
     <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Conectado como um dispositivo de mídia"</string>
-    <string name="usb_ptp_notification_title" msgid="1960817192216064833">"Conectadas como uma câmera"</string>
+    <string name="usb_ptp_notification_title" msgid="1960817192216064833">"Conectado como câmera"</string>
     <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Conectados como um instalador"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
     <string name="usb_notification_message" msgid="2290859399983720271">"Toque para obter outras opções USB."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index e943dda..3b416fe 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -175,28 +175,20 @@
     <string name="permgroupdesc_location" msgid="5704679763124170100">"Надгледајте своју физичку локацију."</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"Комуникација преко мреже"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"Приступајте разним функцијама мреже."</string>
-    <!-- no translation found for permgrouplab_bluetoothNetwork (1585403544162128109) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_bluetoothNetwork (5625288577164282391) -->
-    <skip />
-    <!-- no translation found for permgrouplab_shortrangeNetwork (130808676377486118) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_shortrangeNetwork (1884069062653436007) -->
-    <skip />
+    <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"Bluetooth"</string>
+    <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Приступање уређајима и мрежама преко Bluetooth-а."</string>
+    <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Мреже кратког домета"</string>
+    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Приступање уређајима преко мрежа кратког домета, као што је NFC."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"Аудио подешавања"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"Промена аудио подешавања."</string>
     <string name="permgrouplab_affectsBattery" msgid="6209246653424798033">"Утицај на батерију"</string>
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"Коришћење функција које могу брзо да истроше батерију."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"Директан приступ календару и догађајима."</string>
-    <!-- no translation found for permgrouplab_dictionary (4148597128843641379) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_dictionary (7921166355964764490) -->
-    <skip />
-    <!-- no translation found for permgrouplab_writeDictionary (8090237702432576788) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_writeDictionary (2711561994497361646) -->
-    <skip />
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Читање речника корисника"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Читање речи у речнику корисника."</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Уписивање у речник корисника"</string>
+    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Додавање речи у речник корисника."</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Обележивачи и историја"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Директан приступ обележивачима и историји прегледача."</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Аларм"</string>
@@ -569,8 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"Дозвољава апликацији читање садржаја USB меморије, што могу да буду слике и медиа датотеке."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"Дозвољава апликацији да чита садржај SD картице, који може да обухвата слике и медиа датотеке."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"измена или брисање садржаја USB меморије"</string>
-    <!-- no translation found for permlab_sdcardWrite (8805693630050458763) -->
-    <skip />
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"мењање или брисање садржаја SD картице"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Дозвољава апликацији да уписује податке на USB меморију."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"измена/брисање интерне меморије медија"</string>
@@ -1090,8 +1081,7 @@
     <string name="date_time_set" msgid="5777075614321087758">"Подеси"</string>
     <string name="date_time_done" msgid="2507683751759308828">"Готово"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff900000">"НОВО: "</font></string>
-    <!-- no translation found for perms_description_app (5139836143293299417) -->
-    <skip />
+    <string name="perms_description_app" msgid="5139836143293299417">"Омогућава <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string>
     <string name="usb_storage_activity_title" msgid="4465055157209648641">"USB великог капацитета"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"USB је повезан"</string>
@@ -1322,6 +1312,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Желите ли да покренете прегледач?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Желите ли да прихватите позив?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Увек"</string>
-    <!-- no translation found for activity_resolver_use_once (405646673463328329) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="405646673463328329">"Само једном"</string>
 </resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index c5727ea..d26666e 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -51,12 +51,6 @@
     <!-- Size of status line font in LockScreen. -->
     <dimen name="keyguard_pattern_unlock_status_line_font_size">14sp</dimen>
 
-    <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
-    <dimen name="default_app_widget_padding_left">12dp</dimen>
-    <dimen name="default_app_widget_padding_top">4dp</dimen>
-    <dimen name="default_app_widget_padding_right">12dp</dimen>
-    <dimen name="default_app_widget_padding_bottom">20dp</dimen>
-
     <!-- Minimum width for an action button in the menu area of an action bar -->
     <dimen name="action_button_min_width">64dip</dimen>
 
diff --git a/core/res/res/values-sw720dp-land/arrays.xml b/core/res/res/values-sw720dp-land/arrays.xml
new file mode 100644
index 0000000..d845875
--- /dev/null
+++ b/core/res/res/values-sw720dp-land/arrays.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <array name="lockscreen_chevron_drawables">
+        <item>@drawable/ic_lockscreen_chevron_right</item>
+        <item>@null</item>
+        <item>@null</item>
+        <item>@null</item>
+    </array>
+
+</resources>
diff --git a/core/res/res/values-sw720dp-port/arrays.xml b/core/res/res/values-sw720dp-port/arrays.xml
new file mode 100644
index 0000000..d845875
--- /dev/null
+++ b/core/res/res/values-sw720dp-port/arrays.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <array name="lockscreen_chevron_drawables">
+        <item>@drawable/ic_lockscreen_chevron_right</item>
+        <item>@null</item>
+        <item>@null</item>
+        <item>@null</item>
+    </array>
+
+</resources>
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 34c7ea3..fc336ae 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -50,6 +50,12 @@
     <dimen name="preference_screen_header_padding_side">0dip</dimen>
     <integer name="preference_screen_header_scrollbarStyle">0x0</integer> <!-- insideOverlay -->
 
+    <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
+    <dimen name="default_app_widget_padding_left">12dp</dimen>
+    <dimen name="default_app_widget_padding_top">4dp</dimen>
+    <dimen name="default_app_widget_padding_right">12dp</dimen>
+    <dimen name="default_app_widget_padding_bottom">20dp</dimen>
+
     <!-- Preference fragment padding, sides -->
     <dimen name="preference_fragment_padding_side">32dp</dimen>
     <!-- Padding to the left of the preference panel breadcrumb -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 46c58e3..17dcf64 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -175,28 +175,20 @@
     <string name="permgroupdesc_location" msgid="5704679763124170100">"ตรวจดูตำแหน่งทางกายภาพของคุณ"</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"การสื่อสารของเครือข่าย"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"เข้าถึงคุณลักษณะเครือข่ายต่างๆ"</string>
-    <!-- no translation found for permgrouplab_bluetoothNetwork (1585403544162128109) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_bluetoothNetwork (5625288577164282391) -->
-    <skip />
-    <!-- no translation found for permgrouplab_shortrangeNetwork (130808676377486118) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_shortrangeNetwork (1884069062653436007) -->
-    <skip />
+    <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"บลูทูธ"</string>
+    <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"เข้าถึงอุปกรณ์และเครือข่ายผ่านบลูทูธ"</string>
+    <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"เครือข่ายระยะใกล้"</string>
+    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"เข้าถึงอุปกรณ์ผ่านเครือข่ายระยะใกล้ เช่น NFC"</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"การตั้งค่าเสียง"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"เปลี่ยนการตั้งค่าเสียง"</string>
     <string name="permgrouplab_affectsBattery" msgid="6209246653424798033">"มีผลต่อแบตเตอรี่"</string>
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"ใช้คุณลักษณะที่ทำให้พลังงานแบตเตอรี่ลดลงอย่างรวดเร็ว"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ปฏิทิน"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"เข้าถึงปฏิทินและกิจกรรมโดยตรง"</string>
-    <!-- no translation found for permgrouplab_dictionary (4148597128843641379) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_dictionary (7921166355964764490) -->
-    <skip />
-    <!-- no translation found for permgrouplab_writeDictionary (8090237702432576788) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_writeDictionary (2711561994497361646) -->
-    <skip />
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"อ่านพจนานุกรมผู้ใช้"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"อ่านคำในพจนานุกรมผู้ใช้"</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"เขียนพจนานุกรมผู้ใช้"</string>
+    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"เพิ่มคำลงในพจนานุกรมผู้ใช้"</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"บุ๊กมาร์กและประวัติ"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"เข้าถึงบุ๊กมาร์กและประวัติของเบราว์เซอร์โดยตรง"</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"เตือน"</string>
@@ -569,8 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"อนุญาตให้แอปอ่านเนื้อหาในที่จัดเก็บข้อมูล USB ซึ่งอาจรวมไปถึงรูปภาพและสื่อ"</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"อนุญาตให้แอปอ่านเนื้อหาในการ์ด SD ซึ่งอาจรวมไปถึงรูปภาพและสื่อ"</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"แก้ไขหรือลบเนื้อหาใน USB"</string>
-    <!-- no translation found for permlab_sdcardWrite (8805693630050458763) -->
-    <skip />
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"แก้ไขหรือลบเนื้อหาในการ์ด SD ของคุณ"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"อนุญาตให้แอปฯ เขียนลงใน USB"</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"แก้/ลบเนื้อหาข้อมูลสื่อภายใน"</string>
@@ -1090,8 +1081,7 @@
     <string name="date_time_set" msgid="5777075614321087758">"ตั้งค่า"</string>
     <string name="date_time_done" msgid="2507683751759308828">"เสร็จสิ้น"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff900000">"ใหม่: "</font></string>
-    <!-- no translation found for perms_description_app (5139836143293299417) -->
-    <skip />
+    <string name="perms_description_app" msgid="5139836143293299417">"โดย <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string>
     <string name="usb_storage_activity_title" msgid="4465055157209648641">"ที่จัดเก็บข้อมูลจำนวนมากแบบ USB"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"เชื่อมต่อ USB แล้ว"</string>
@@ -1321,8 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"กำลังส่ง…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"เปิดเบราว์เซอร์หรือไม่"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"รับสายหรือไม่"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
-    <!-- no translation found for activity_resolver_use_once (405646673463328329) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"ทุกครั้ง"</string>
+    <string name="activity_resolver_use_once" msgid="405646673463328329">"เพียงแค่ครั้งเดียว"</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9aa2df3..9222823 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -175,28 +175,20 @@
     <string name="permgroupdesc_location" msgid="5704679763124170100">"Fiziksel konumunuzu izleme."</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"Ağ iletişimi"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"Çeşitli ağ özelliklerine erişme."</string>
-    <!-- no translation found for permgrouplab_bluetoothNetwork (1585403544162128109) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_bluetoothNetwork (5625288577164282391) -->
-    <skip />
-    <!-- no translation found for permgrouplab_shortrangeNetwork (130808676377486118) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_shortrangeNetwork (1884069062653436007) -->
-    <skip />
+    <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"Bluetooth"</string>
+    <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"Cihazlara ve ağlara Bluetooth ile eriş."</string>
+    <string name="permgrouplab_shortrangeNetwork" msgid="130808676377486118">"Kısa Mesafeli Ağlar"</string>
+    <string name="permgroupdesc_shortrangeNetwork" msgid="1884069062653436007">"Cihazlara NFC gibi kısa mesafeli ağları kullanarak eriş."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"Ses Ayarları"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"Ses ayarlarını değiştirme."</string>
     <string name="permgrouplab_affectsBattery" msgid="6209246653424798033">"Pili Etkileyenler"</string>
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"Pili çok çabuk tüketebilen özellikleri kullanma."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Takvim"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"Takvime ve etkinliklere doğrudan erişim."</string>
-    <!-- no translation found for permgrouplab_dictionary (4148597128843641379) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_dictionary (7921166355964764490) -->
-    <skip />
-    <!-- no translation found for permgrouplab_writeDictionary (8090237702432576788) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_writeDictionary (2711561994497361646) -->
-    <skip />
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Kullanıcı Sözlüğünü Oku"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Kelimeleri kullanıcı sözlüğünde oku."</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Kullanıcı Sözlüğüne Yaz"</string>
+    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Kelimeleri kullanıcı sözlüğüne ekle."</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Yer İşaretleri ve Geçmiş"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Yer işaretlerine ve tarayıcı geçmişine doğrudan erişim."</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Alarm"</string>
@@ -569,8 +561,7 @@
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3530894470637667917">"Uygulamaya USB depolamanın içeriğini okuma izni verir. Bu izin fotoğrafları ve medyayı da içerebilir."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="2555811422562526606">"Uygulamaya SD kartın içeriğini okuma izni verir. Bu izin fotoğrafları ve medyayı da içerebilir."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB depolamamın içeriğini değiştir veya sil"</string>
-    <!-- no translation found for permlab_sdcardWrite (8805693630050458763) -->
-    <skip />
+    <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kartın içeriğini değiştir veya sil"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Uygulamaya USB depolama birimine yazma izni verir."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"dahili medya depolama birimi içeriğini değiştir/sil"</string>
@@ -1090,8 +1081,7 @@
     <string name="date_time_set" msgid="5777075614321087758">"Ayarla"</string>
     <string name="date_time_done" msgid="2507683751759308828">"Tamamlandı"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff900000">"YENİ: "</font></string>
-    <!-- no translation found for perms_description_app (5139836143293299417) -->
-    <skip />
+    <string name="perms_description_app" msgid="5139836143293299417">"Sağlayan: <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
     <string name="usb_storage_activity_title" msgid="4465055157209648641">"USB yığın depolama"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"USB bağlandı"</string>
@@ -1321,8 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"Gönderiliyor…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Tarayıcı Başlatılsın mı?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Çağrı kabul edilsin mi?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
-    <!-- no translation found for activity_resolver_use_once (405646673463328329) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Her zaman"</string>
+    <string name="activity_resolver_use_once" msgid="405646673463328329">"Sadece Bir Defa"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 148713237..542f048 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1311,7 +1311,6 @@
     <string name="sending" msgid="3245653681008218030">"Đang gửi…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Khởi chạy trình duyệt?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Chấp nhận cuộc gọi?"</string>
-    <!-- no translation found for activity_resolver_use_always (8017770747801494933) -->
-    <skip />
+    <string name="activity_resolver_use_always" msgid="8017770747801494933">"Luôn bật"</string>
     <string name="activity_resolver_use_once" msgid="405646673463328329">"Chỉ một lần"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index dce0525..0c9717f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -185,7 +185,7 @@
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"Sebenzisa izici ezingakhipha ngokushesha ibhethri."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Ikhalenda"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"Ukufinyelela okuqondile kukhalenda nezehlakalo."</string>
-    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Funda isichzamazwi somsebenzisi"</string>
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"Funda isichazamazwi somsebenzisi"</string>
     <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"Funda amagama kusichazamazwi somsebenzisi."</string>
     <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Bhala isichazamazwi somsebenzisi"</string>
     <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Engeza amagama kusichazamazwi somsebenzisi."</string>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 6910ebe..f9762b1 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -22,5 +22,4 @@
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="action_bar_expanded_action_views_exclusive">true</bool>
     <bool name="target_honeycomb_needs_options_menu">true</bool>
-    <bool name="activity_chooser_view_has_default_activity">false</bool>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5fa7b7e..98e7769 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -512,7 +512,6 @@
             0 - Nothing
             1 - Recent apps dialog
             2 - Recent apps view in SystemUI
-            3 - Voice search
          This needs to match the constants in
          policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
     -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 734151b..d549644 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -227,4 +227,11 @@
          action bar tabs from becoming too wide on a wide screen when only
          a few are present. -->
     <dimen name="action_bar_stacked_tab_max_width">180dp</dimen>
+
+	<!-- Size of notification text (see TextAppearance.StatusBar.EventContent) -->
+    <dimen name="notification_text_size">14dp</dimen>
+	<!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
+    <dimen name="notification_title_text_size">18dp</dimen>
+	<!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
+    <dimen name="notification_subtext_size">12dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 48038dd..f1f67eb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -208,6 +208,9 @@
   <java-symbol type="id" name="inbox_text2" />
   <java-symbol type="id" name="inbox_text3" />
   <java-symbol type="id" name="inbox_text4" />
+  <java-symbol type="id" name="inbox_text5" />
+  <java-symbol type="id" name="inbox_text6" />
+  <java-symbol type="id" name="inbox_more" />
   <java-symbol type="id" name="status_bar_latest_event_content" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
@@ -294,6 +297,9 @@
   <java-symbol type="dimen" name="volume_panel_top" />
   <java-symbol type="dimen" name="action_bar_stacked_max_height" />
   <java-symbol type="dimen" name="action_bar_stacked_tab_max_width" />
+  <java-symbol type="dimen" name="notification_text_size" />
+  <java-symbol type="dimen" name="notification_title_text_size" />
+  <java-symbol type="dimen" name="notification_subtext_size" />
 
   <java-symbol type="string" name="addToDictionary" />
   <java-symbol type="string" name="action_bar_home_description" />
@@ -1380,7 +1386,6 @@
   <java-symbol type="bool" name="config_wifi_dual_band_support" />
   <java-symbol type="bool" name="config_wimaxEnabled" />
   <java-symbol type="bool" name="show_ongoing_ime_switcher" />
-  <java-symbol type="bool" name="activity_chooser_view_has_default_activity" />
   <java-symbol type="color" name="config_defaultNotificationColor" />
   <java-symbol type="drawable" name="ic_notification_ime_default" />
   <java-symbol type="drawable" name="stat_notify_car_mode" />
@@ -1490,6 +1495,8 @@
   <java-symbol type="string" name="low_internal_storage_view_title" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
+  <java-symbol type="string" name="select_keyboard_layout_notification_title" />
+  <java-symbol type="string" name="select_keyboard_layout_notification_message" />
   <java-symbol type="string" name="smv_application" />
   <java-symbol type="string" name="smv_process" />
   <java-symbol type="string" name="tethered_notification_message" />
@@ -1514,6 +1521,8 @@
   <java-symbol type="xml" name="storage_list" />
   <java-symbol type="bool" name="config_enableDreams" />
   <java-symbol type="string" name="config_defaultDreamComponent" />
+  <java-symbol type="string" name="enable_explore_by_touch_warning_title" />
+  <java-symbol type="string" name="enable_explore_by_touch_warning_message" />
 
   <java-symbol type="layout" name="resolver_grid" />
   <java-symbol type="id" name="resolver_grid" />
@@ -1581,6 +1590,7 @@
   <java-symbol type="drawable" name="expander_ic_minimized" />
   <java-symbol type="drawable" name="ic_menu_archive" />
   <java-symbol type="drawable" name="ic_menu_goto" />
+  <java-symbol type="drawable" name="ic_settings_language" />
   <java-symbol type="drawable" name="title_bar_medium" />
   <java-symbol type="id" name="body" />
   <java-symbol type="string" name="fast_scroll_alphabet" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 687a00b..5929439 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -846,6 +846,12 @@
         interface of an input method. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindAccessibilityService">bind to an accessibility service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindAccessibilityService">Allows the holder to bind to the top-level
+        interface of an accessibility service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bindTextService">bind to a text service</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindTextService">Allows the holder to bind to the top-level
@@ -2497,6 +2503,25 @@
     <!-- SearchView accessibility description for voice button [CHAR LIMIT=NONE] -->
     <string name="searchview_description_voice">Voice search</string>
 
+    <!-- Title for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (default). [CHAR LIMIT=35] -->
+    <string name="enable_explore_by_touch_warning_title">Enable Explore by Touch?</string>
+    <!-- Summary for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (tablet). [CHAR LIMIT=NONE] -->
+    <string name="enable_explore_by_touch_warning_message" product="tablet">
+            <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch.
+            When Explore by Touch is turned on, you can hear or see descriptions of what\'s under
+            your finger or perform gestures to interact with the tablet.</string>
+    <!-- Summary for a warning message about the interaction model changes after allowing an accessibility
+         service to put the device into explore by touch mode, displayed as a dialog message when
+         the user selects to enables the service. (default). [CHAR LIMIT=NONE] -->
+    <string name="enable_explore_by_touch_warning_message" product="default">
+            <xliff:g id="accessibility_service_name">%1$s</xliff:g> wants to enable Explore by Touch.
+            When Explore by Touch is turned on, you can hear or see descriptions of what\'s under
+            your finger or perform gestures to interact with the phone.</string>
+
     <!-- String used to display the date. This is the string to say something happened 1 month ago. -->
     <string name="oneMonthDurationPast">1 month ago</string>
     <!-- String used to display the date. This is the string to say something happened more than 1 month ago. -->
@@ -3086,6 +3111,11 @@
     <!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=10] -->
     <string name="hardware">Hardware</string>
 
+    <!-- Title of the notification to prompt the user to select a keyboard layout. -->
+    <string name="select_keyboard_layout_notification_title">Select keyboard layout</string>
+    <!-- Message of the notification to prompt the user to select a keyboard layout. -->
+    <string name="select_keyboard_layout_notification_message">Touch to select a keyboard layout.</string>
+
     <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
     <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
 
@@ -3566,6 +3596,6 @@
 
     <!-- Title for a button to choose the currently selected activity
          from the activity resolver to use just this once. [CHAR LIMIT=25] -->
-    <string name="activity_resolver_use_once">Just Once</string>
+    <string name="activity_resolver_use_once">Just once</string>
 
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2b34dab..a54cdf1 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -239,22 +239,25 @@
     </style>
     <!-- Notification content styles -->
     <style name="TextAppearance.StatusBar.EventContent">
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
-        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">#808080</item>
+        <item name="android:textSize">@dimen/notification_text_size</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Title">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">#ffffff</item>
+        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:textSize">@dimen/notification_title_text_size</item>
         <item name="android:textStyle">bold</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Line2">
-        <item name="android:textSize">13sp</item>
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Info">
-        <!-- inherit all -->
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
+        <item name="android:textColor">#666666</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Time">
-        <!-- inherit all -->
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
+        <item name="android:textColor">#666666</item>
     </style>
 
     <style name="TextAppearance.Small.CalendarViewWeekDayView">
@@ -631,6 +634,7 @@
 
     <style name="Widget.WebView">
         <item name="android:focusable">true</item>
+        <item name="android:focusableInTouchMode">true</item>
         <item name="android:scrollbars">horizontal|vertical</item>
     </style>
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index f7b0cd0..f01562c 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -244,8 +244,13 @@
         mContext = this;
         mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
 
-        initializeNetworkStates();
+        if (mWifiManager.isWifiApEnabled()) {
+            // if soft AP is enabled, disable it
+            mWifiManager.setWifiApEnabled(null, false);
+            log("Disable soft ap");
+        }
 
+        initializeNetworkStates();
         log("Clear Wifi before we start the test.");
         removeConfiguredNetworksAndDisableWifi();
         mWifiRegexs = mCM.getTetherableWifiRegexs();
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
index 7e136be..60595fb 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
@@ -103,7 +103,7 @@
             assertTrue(mAct.mWifiManager.setWifiApEnabled(config, true));
             // Wait for wifi ap state to be ENABLED
             assertTrue(mAct.waitForWifiAPState(WifiManager.WIFI_AP_STATE_ENABLED,
-                    ConnectivityManagerTestActivity.LONG_TIMEOUT));
+                    2 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
             // Wait for wifi tethering result
             assertEquals(ConnectivityManagerTestActivity.SUCCESS,
                     mAct.waitForTetherStateChange(2*ConnectivityManagerTestActivity.SHORT_TIMEOUT));
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index 649ec3e..39e2cf2 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -83,6 +83,7 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
+
         mAct = getActivity();
         mRunner = (ConnectivityManagerStressTestRunner) getInstrumentation();
         mReconnectIterations = mRunner.mReconnectIterations;
@@ -97,11 +98,6 @@
         mOutputWriter = new BufferedWriter(new FileWriter(new File(
                 Environment.getExternalStorageDirectory(), OUTPUT_FILE), true));
         mAct.turnScreenOn();
-        if (mAct.mWifiManager.isWifiApEnabled()) {
-            // if soft AP is enabled, disable it
-            assertTrue(mAct.mWifiManager.setWifiApEnabled(null, false));
-            Log.v(TAG, "disable soft ap");
-        }
         if (!mAct.mWifiManager.isWifiEnabled()) {
             log("Enable wi-fi before stress tests.");
             if (!mAct.enableWifi()) {
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index cadc895..dcd1bab 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1039,7 +1039,8 @@
             </intent-filter>
         </activity>
 
-        <service android:name="android.webkit.AccessibilityInjectorTest$MockAccessibilityService">
+        <service android:name="android.webkit.AccessibilityInjectorTest$MockAccessibilityService"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
             <intent-filter>
                 <action android:name="android.accessibilityservice.AccessibilityService" />
             </intent-filter>
diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java
index c573498..27b699d 100644
--- a/core/tests/coretests/src/android/net/SSLTest.java
+++ b/core/tests/coretests/src/android/net/SSLTest.java
@@ -59,6 +59,14 @@
                 new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' })));
     }
 
+    public void testStringsToNpnBytesEmptyArray() {
+        try {
+            SSLCertificateSocketFactory.toNpnProtocolsList();
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     public void testStringsToNpnBytesEmptyByteArray() {
         try {
             SSLCertificateSocketFactory.toNpnProtocolsList(new byte[0]);
@@ -67,11 +75,6 @@
         }
     }
 
-    public void testStringsToNpnBytesEmptyArray() {
-        byte[] expected = {};
-        assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList()));
-    }
-
     public void testStringsToNpnBytesOversizedInput() {
         try {
             SSLCertificateSocketFactory.toNpnProtocolsList(new byte[256]);
diff --git a/core/tests/utillib/src/android/test/BandwidthTestCase.java b/core/tests/utillib/src/android/test/BandwidthTestCase.java
index 4f95f77..c03d9b3 100644
--- a/core/tests/utillib/src/android/test/BandwidthTestCase.java
+++ b/core/tests/utillib/src/android/test/BandwidthTestCase.java
@@ -18,6 +18,7 @@
 import android.net.NetworkStats;
 import android.net.TrafficStats;
 import android.os.Bundle;
+import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -29,6 +30,7 @@
  * as an {@link InstrumentationTestCase}
  */
 public class BandwidthTestCase extends InstrumentationTestCase {
+    private static final String TAG = "BandwidthTestCase";
     private static final String REPORT_KEY_PACKETS_SENT = "txPackets";
     private static final String REPORT_KEY_PACKETS_RECEIVED = "rxPackets";
     private static final String REPORT_KEY_BYTES_SENT = "txBytes";
@@ -86,11 +88,26 @@
             }
         } else if (method.isAnnotationPresent(BandwidthTest.class) ||
                 testClass.isAnnotationPresent(BandwidthTest.class)) {
-            TrafficStats.startDataProfiling(null);
+            /**
+             * If bandwidth profiling fails for whatever reason the test
+             * should be allow to execute to its completion.
+             * Typically bandwidth profiling would fail when a lower level
+             * component is missing, such as the kernel module, for a newly
+             * introduced hardware.
+             */
+            try{
+                TrafficStats.startDataProfiling(null);
+            } catch(IllegalStateException isx){
+                Log.w(TAG, "Failed to start bandwidth profiling");
+            }
             runMethod(method, 1, false);
-            NetworkStats stats = TrafficStats.stopDataProfiling(null);
-            NetworkStats.Entry entry = stats.getTotal(null);
-            getInstrumentation().sendStatus(2, getBandwidthStats(entry));
+            try{
+                NetworkStats stats = TrafficStats.stopDataProfiling(null);
+                NetworkStats.Entry entry = stats.getTotal(null);
+                getInstrumentation().sendStatus(2, getBandwidthStats(entry));
+            } catch (IllegalStateException isx){
+                Log.w(TAG, "Failed to collect bandwidth stats");
+            }
         } else {
             runMethod(method, runCount, isRepetitive);
         }
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 544076f..782a701 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -252,6 +252,7 @@
     label:                              ' '
     base:                               ' '
     alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
 }
 
 key ENTER {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index e592013..d90b790 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -249,6 +249,7 @@
     label:                              ' '
     base:                               ' '
     alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
 }
 
 key ENTER {
diff --git a/docs/html/guide/practices/design/performance.jd b/docs/html/guide/practices/design/performance.jd
index c41f971..dd9b554 100644
--- a/docs/html/guide/practices/design/performance.jd
+++ b/docs/html/guide/practices/design/performance.jd
@@ -180,6 +180,9 @@
 trivial getter. This is true in Froyo, but will improve in the future when
 the JIT inlines getter methods.</p>
 
+<p>Note that if you're using ProGuard, you can have the best
+of both worlds because ProGuard can inline accessors for you.</p>
+
 <a name="use_final" id="use_final"></a>
 <h2>Use Static Final For Constants</h2>
 
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index b33a097..82b5e29 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -528,20 +528,28 @@
 which indicates the current device orientation.</p>
       </td>
     </tr>
-    <tr id="DockQualifier">
-      <td>Dock mode</td>
+    <tr id="UiModeQualifier">
+      <td>UI mode</td>
       <td>
         <code>car</code><br/>
-        <code>desk</code>
+        <code>desk</code><br/>
+        <code>television<br/>
+        <code>appliance</code>
       </td>
       <td>
         <ul class="nolist">
-          <li>{@code car}: Device is in a car dock</li>
-          <li>{@code desk}: Device is in a desk dock</li>
+          <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, 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.</em></p>
+        <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
-dock. You can enable or disable this mode using {@link
+dock. You can enable or disable some of these modes using {@link
 android.app.UiModeManager}. See <a href="runtime-changes.html">Handling Runtime Changes</a> for
 information about how this affects your application during runtime.</p>
       </td>
@@ -608,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>
@@ -692,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/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 5f74c01..785582c 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -386,6 +386,7 @@
 
     /**
      * Get the resolved layout direction of this Drawable.
+     * @hide
      */
     public int getResolvedLayoutDirectionSelf() {
         final Callback callback = getCallback();
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index d2a6a7a..546925e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -162,6 +162,13 @@
     }
     mBitmapResources.clear();
 
+    for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
+        SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(bitmap);
+        caches.resourceCache.destructor(bitmap);
+    }
+    mOwnedBitmapResources.clear();
+
     for (size_t i = 0; i < mFilterResources.size(); i++) {
         caches.resourceCache.decrementRefcount(mFilterResources.itemAt(i));
     }
@@ -217,44 +224,51 @@
 
     Caches& caches = Caches::getInstance();
 
-    const Vector<SkBitmap*> &bitmapResources = recorder.getBitmapResources();
+    const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
     for (size_t i = 0; i < bitmapResources.size(); i++) {
         SkBitmap* resource = bitmapResources.itemAt(i);
         mBitmapResources.add(resource);
         caches.resourceCache.incrementRefcount(resource);
     }
 
-    const Vector<SkiaColorFilter*> &filterResources = recorder.getFilterResources();
+    const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
+    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
+        SkBitmap* resource = ownedBitmapResources.itemAt(i);
+        mOwnedBitmapResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
+    }
+
+    const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
     for (size_t i = 0; i < filterResources.size(); i++) {
         SkiaColorFilter* resource = filterResources.itemAt(i);
         mFilterResources.add(resource);
         caches.resourceCache.incrementRefcount(resource);
     }
 
-    const Vector<SkiaShader*> &shaders = recorder.getShaders();
+    const Vector<SkiaShader*>& shaders = recorder.getShaders();
     for (size_t i = 0; i < shaders.size(); i++) {
         SkiaShader* resource = shaders.itemAt(i);
         mShaders.add(resource);
         caches.resourceCache.incrementRefcount(resource);
     }
 
-    const Vector<SkPaint*> &paints = recorder.getPaints();
+    const Vector<SkPaint*>& paints = recorder.getPaints();
     for (size_t i = 0; i < paints.size(); i++) {
         mPaints.add(paints.itemAt(i));
     }
 
-    const Vector<SkPath*> &paths = recorder.getPaths();
+    const Vector<SkPath*>& paths = recorder.getPaths();
     for (size_t i = 0; i < paths.size(); i++) {
         mPaths.add(paths.itemAt(i));
     }
 
-    const SortedVector<SkPath*> &sourcePaths = recorder.getSourcePaths();
+    const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
     for (size_t i = 0; i < sourcePaths.size(); i++) {
         mSourcePaths.add(sourcePaths.itemAt(i));
         caches.resourceCache.incrementRefcount(sourcePaths.itemAt(i));
     }
 
-    const Vector<SkMatrix*> &matrices = recorder.getMatrices();
+    const Vector<SkMatrix*>& matrices = recorder.getMatrices();
     for (size_t i = 0; i < matrices.size(); i++) {
         mMatrices.add(matrices.itemAt(i));
     }
@@ -282,7 +296,8 @@
         indent[i] = ' ';
     }
     indent[count] = '\0';
-    ALOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string());
+    ALOGD("%sStart display list (%p, %s, render=%d)", (char*) indent + 2, this,
+            mName.string(), isRenderable());
 
     ALOGD("%s%s %d", indent, "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     int saveCount = renderer.getSaveCount() - 1;
@@ -1036,10 +1051,7 @@
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
                         bitmap, x, y, paint);
-                if (bitmap) {
-                    renderer.drawBitmap(bitmap, x, y, paint);
-                    delete bitmap;
-                }
+                renderer.drawBitmap(bitmap, x, y, paint);
             }
             break;
             case DrawBitmapMesh: {
@@ -1295,6 +1307,12 @@
     }
     mBitmapResources.clear();
 
+    for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
+        SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(bitmap);
+    }
+    mOwnedBitmapResources.clear();
+
     for (size_t i = 0; i < mFilterResources.size(); i++) {
         caches.resourceCache.decrementRefcount(mFilterResources.itemAt(i));
     }
@@ -1336,6 +1354,10 @@
     return displayList;
 }
 
+bool DisplayListRenderer::isDeferred() {
+    return true;
+}
+
 void DisplayListRenderer::setViewport(int width, int height) {
     mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 2f74f5b..0ba4078 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -170,9 +170,10 @@
     }
 
     void setAlpha(float alpha) {
+        alpha = fminf(1.0f, fmaxf(0.0f, alpha));
         if (alpha != mAlpha) {
             mAlpha = alpha;
-            mMultipliedAlpha = (int)(255 * alpha);
+            mMultipliedAlpha = (int) (255 * alpha);
         }
     }
 
@@ -184,7 +185,7 @@
         if (translationX != mTranslationX) {
             mTranslationX = translationX;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mTranslationX, 0) && ALMOST_EQUAL(mTranslationY, 0)) {
+            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
                 mMatrixFlags &= ~TRANSLATION;
             } else {
                 mMatrixFlags |= TRANSLATION;
@@ -196,7 +197,7 @@
         if (translationY != mTranslationY) {
             mTranslationY = translationY;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mTranslationX, 0) && ALMOST_EQUAL(mTranslationY, 0)) {
+            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
                 mMatrixFlags &= ~TRANSLATION;
             } else {
                 mMatrixFlags |= TRANSLATION;
@@ -208,7 +209,7 @@
         if (rotation != mRotation) {
             mRotation = rotation;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mRotation, 0)) {
+            if (mRotation == 0.0f) {
                 mMatrixFlags &= ~ROTATION;
             } else {
                 mMatrixFlags |= ROTATION;
@@ -220,7 +221,7 @@
         if (rotationX != mRotationX) {
             mRotationX = rotationX;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mRotationX, 0) && ALMOST_EQUAL(mRotationY, 0)) {
+            if (mRotationX == 0.0f && mRotationY == 0.0f) {
                 mMatrixFlags &= ~ROTATION_3D;
             } else {
                 mMatrixFlags |= ROTATION_3D;
@@ -232,7 +233,7 @@
         if (rotationY != mRotationY) {
             mRotationY = rotationY;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mRotationX, 0) && ALMOST_EQUAL(mRotationY, 0)) {
+            if (mRotationX == 0.0f && mRotationY == 0.0f) {
                 mMatrixFlags &= ~ROTATION_3D;
             } else {
                 mMatrixFlags |= ROTATION_3D;
@@ -244,7 +245,7 @@
         if (scaleX != mScaleX) {
             mScaleX = scaleX;
             mMatrixDirty = true;
-            if (ALMOST_EQUAL(mScaleX, 1) && ALMOST_EQUAL(mScaleY, 1)) {
+            if (mScaleX == 1.0f && mScaleY == 1.0f) {
                 mMatrixFlags &= ~SCALE;
             } else {
                 mMatrixFlags |= SCALE;
@@ -267,7 +268,7 @@
     void setPivotX(float pivotX) {
         mPivotX = pivotX;
         mMatrixDirty = true;
-        if (ALMOST_EQUAL(mPivotX, 0) && ALMOST_EQUAL(mPivotY, 0)) {
+        if (mPivotX == 0.0f && mPivotY == 0.0f) {
             mMatrixFlags &= ~PIVOT;
         } else {
             mMatrixFlags |= PIVOT;
@@ -278,7 +279,7 @@
     void setPivotY(float pivotY) {
         mPivotY = pivotY;
         mMatrixDirty = true;
-        if (ALMOST_EQUAL(mPivotX, 0) && ALMOST_EQUAL(mPivotY, 0)) {
+        if (mPivotX == 0.0f && mPivotY == 0.0f) {
             mMatrixFlags &= ~PIVOT;
         } else {
             mMatrixFlags |= PIVOT;
@@ -398,7 +399,6 @@
 
 private:
     void init();
-
     void initProperties();
 
     void clearResources();
@@ -424,16 +424,7 @@
     }
 
     SkBitmap* getBitmapData() {
-        SkBitmap* bitmap = new SkBitmap;
-        bitmap->setConfig((SkBitmap::Config) getInt(), getInt(), getInt());
-        if (!bitmap->allocPixels()) {
-            delete bitmap;
-            return NULL;
-        }
-
-        bitmap->setPixels((void*) mReader.skip(bitmap->height() * bitmap->rowBytes()));
-
-        return bitmap;
+        return (SkBitmap*) getInt();
     }
 
     SkiaShader* getShader() {
@@ -497,6 +488,7 @@
     }
 
     Vector<SkBitmap*> mBitmapResources;
+    Vector<SkBitmap*> mOwnedBitmapResources;
     Vector<SkiaColorFilter*> mFilterResources;
 
     Vector<SkPaint*> mPaints;
@@ -552,6 +544,8 @@
 
     ANDROID_API DisplayList* getDisplayList(DisplayList* displayList);
 
+    virtual bool isDeferred();
+
     virtual void setViewport(int width, int height);
     virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque);
     virtual void finish();
@@ -634,6 +628,10 @@
         return mBitmapResources;
     }
 
+    const Vector<SkBitmap*>& getOwnedBitmapResources() const {
+        return mOwnedBitmapResources;
+    }
+
     const Vector<SkiaColorFilter*>& getFilterResources() const {
         return mFilterResources;
     }
@@ -719,17 +717,6 @@
         mWriter.write(values, count * sizeof(int32_t));
     }
 
-    void addBitmapData(SkBitmap* bitmap) {
-        mWriter.writeInt(bitmap->config());
-        mWriter.writeInt(bitmap->width());
-        mWriter.writeInt(bitmap->height());
-
-        SkAutoLockPixels alp(*bitmap);
-        void* src = bitmap->getPixels();
-
-        mWriter.write(src, bitmap->rowBytes() * bitmap->height());
-    }
-
     void addUInts(const uint32_t* values, int8_t count) {
         mWriter.writeInt(count);
         mWriter.write(values, count * sizeof(uint32_t));
@@ -825,6 +812,12 @@
         Caches::getInstance().resourceCache.incrementRefcount(bitmap);
     }
 
+    void addBitmapData(SkBitmap* bitmap) {
+        addInt((int) bitmap);
+        mOwnedBitmapResources.add(bitmap);
+        Caches::getInstance().resourceCache.incrementRefcount(bitmap);
+    }
+
     inline void addShader(SkiaShader* shader) {
         if (!shader) {
             addInt((int) NULL);
@@ -851,6 +844,7 @@
     }
 
     Vector<SkBitmap*> mBitmapResources;
+    Vector<SkBitmap*> mOwnedBitmapResources;
     Vector<SkiaColorFilter*> mFilterResources;
 
     Vector<SkPaint*> mPaints;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7f242c3..2a8b32c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -144,6 +144,10 @@
     return STENCIL_BUFFER_SIZE;
 }
 
+bool OpenGLRenderer::isDeferred() {
+    return false;
+}
+
 void OpenGLRenderer::setViewport(int width, int height) {
     mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
@@ -166,21 +170,32 @@
 
 void OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
     mCaches.clearGarbage();
-    mFunctors.clear();
 
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->fbo = getTargetFbo();
     mSaveCount = 1;
 
-    glViewport(0, 0, mWidth, mHeight);
-    mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
-
     mSnapshot->setClip(left, top, right, bottom);
-    mDirtyClip = false;
+    mDirtyClip = opaque;
+
+    syncState();
 
     if (!opaque) {
+        mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
         glClear(GL_COLOR_BUFFER_BIT);
+    } else {
+        mCaches.resetScissor();
+    }
+}
+
+void OpenGLRenderer::syncState() {
+    glViewport(0, 0, mWidth, mHeight);
+
+    if (mCaches.blend) {
+        glEnable(GL_BLEND);
+    } else {
+        glDisable(GL_BLEND);
     }
 }
 
@@ -287,11 +302,6 @@
         }
     }
 
-    // Restore state possibly changed by the functors in process mode
-    GLboolean value;
-    glGetBooleanv(GL_BLEND, &value);
-    mCaches.blend = value;
-
     mCaches.activeTexture(0);
 
     return result;
@@ -299,6 +309,8 @@
 
 status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
     interrupt();
+    detachFunctor(functor);
+
     if (mDirtyClip) {
         setScissorFromClip();
     }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0ea0db7..ad83b31 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -63,6 +63,8 @@
     ANDROID_API OpenGLRenderer();
     virtual ~OpenGLRenderer();
 
+    virtual bool isDeferred();
+
     virtual void setViewport(int width, int height);
 
     ANDROID_API void prepare(bool opaque);
@@ -88,7 +90,7 @@
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
             int alpha, int flags);
 
-    virtual void setAlpha(float alpha) {
+    void setAlpha(float alpha) {
         mSnapshot->alpha = alpha;
     }
 
@@ -213,6 +215,12 @@
 
 private:
     /**
+     * Ensures the state of the renderer is the same as the state of
+     * the GL context.
+     */
+    void syncState();
+
+    /**
      * Saves the current state of the renderer as a new snapshot.
      * The new snapshot is saved in mSnapshot and the previous snapshot
      * is linked from mSnapshot->previous.
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 9ffad88..cf5f822 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -59,11 +59,11 @@
 void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
     SkSafeRef(bitmapResource->pixelRef());
     SkSafeRef(bitmapResource->getColorTable());
-    incrementRefcount((void*)bitmapResource, kBitmap);
+    incrementRefcount((void*) bitmapResource, kBitmap);
 }
 
 void ResourceCache::incrementRefcount(SkPath* pathResource) {
-    incrementRefcount((void*)pathResource, kPath);
+    incrementRefcount((void*) pathResource, kPath);
 }
 
 void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
index f079a7b..fdd9acf 100644
--- a/libs/hwui/utils/Compare.h
+++ b/libs/hwui/utils/Compare.h
@@ -19,10 +19,6 @@
 
 #include <cmath>
 
-#define EPSILON 0.00001f
-
-#define ALMOST_EQUAL(u, v) (fabs((u) - (v)) < EPSILON)
-
 /**
  * Compare floats.
  */
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/AudioManager.java b/media/java/android/media/AudioManager.java
index ef5da5b..16cfa92 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2000,6 +2000,37 @@
     }
 
     /**
+     * @hide
+     * Used internally by telephony package to register an intent receiver for ACTION_MEDIA_BUTTON.
+     * @param eventReceiver the component that will receive the media button key events,
+     *          no-op if eventReceiver is null
+     */
+    public void registerMediaButtonEventReceiverForCalls(ComponentName eventReceiver) {
+        if (eventReceiver == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            // eventReceiver != null
+            service.registerMediaButtonEventReceiverForCalls(eventReceiver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in registerMediaButtonEventReceiverForCalls", e);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void unregisterMediaButtonEventReceiverForCalls() {
+        IAudioService service = getService();
+        try {
+            service.unregisterMediaButtonEventReceiverForCalls();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiverForCalls", e);
+        }
+    }
+
+    /**
      * Unregister the receiver of MEDIA_BUTTON intents.
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that was registered with {@link #registerMediaButtonEventReceiver(ComponentName)}.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 8da7d0f..da01c44 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -59,6 +59,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
+import android.speech.RecognizerIntent;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -3625,12 +3626,14 @@
             Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
             return;
         }
-        // event filtering based on audio mode
+        // event filtering for telephony
         synchronized(mRingingLock) {
-            if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
-                    (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
-                    (getMode() == AudioSystem.MODE_RINGTONE) ) {
-                return;
+            synchronized(mRCStack) {
+                if ((mMediaReceiverForCalls != null) &&
+                        (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
+                    dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
+                    return;
+                }
             }
         }
         // event filtering based on voice-based interactions
@@ -3642,6 +3645,25 @@
     }
 
     /**
+     * Handles the dispatching of the media button events to the telephony package.
+     * Precondition: mMediaReceiverForCalls != null
+     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
+     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+     *     is dispatched.
+     */
+    private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
+        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
+        if (needWakeLock) {
+            mMediaEventWakeLock.acquire();
+            keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+        }
+        mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
+                mAudioHandler, Activity.RESULT_OK, null, null);
+    }
+
+    /**
      * Handles the dispatching of the media button events to one of the registered listeners,
      * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
@@ -3678,38 +3700,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
@@ -3719,67 +3718,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)
 
@@ -3855,24 +3819,32 @@
      * Tell the system to start voice-based interactions / voice commands
      */
     private void startVoiceBasedInteractions(boolean needWakeLock) {
-        Intent voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        Intent voiceIntent = null;
+        // select which type of search to launch:
+        // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+        // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
+        //    with EXTRA_SECURE set to true if the device is securely locked
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+        if (!isLocked && pm.isScreenOn()) {
+            voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+        } else {
+            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+                    isLocked && mKeyguardManager.isKeyguardSecure());
+        }
+        // start the search activity
         if (needWakeLock) {
             mMediaEventWakeLock.acquire();
         }
-        voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         try {
-            if (mKeyguardManager != null) {
-                // it's ok to start voice-based interactions when:
-                // - the device is locked but doesn't require a password to be unlocked
-                // - the device is not locked
-                if ((mKeyguardManager.isKeyguardLocked() && !mKeyguardManager.isKeyguardSecure())
-                        || !mKeyguardManager.isKeyguardLocked()) {
-                    mContext.startActivity(voiceIntent);
-                }
+            if (voiceIntent != null) {
+                voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                mContext.startActivity(voiceIntent);
             }
         } catch (ActivityNotFoundException e) {
-            Log.e(TAG, "Error launching activity for ACTION_WEB_SEARCH: " + e);
+            Log.w(TAG, "No activity for search: " + e);
         } finally {
             if (needWakeLock) {
                 mMediaEventWakeLock.release();
@@ -3880,20 +3852,6 @@
         }
     }
 
-    /**
-     * Verify whether it is safe to start voice-based interactions given the state of the system
-     * @return false is the Keyguard is locked and secure, true otherwise
-     */
-    private boolean safeToStartVoiceBasedInteractions() {
-        KeyguardManager keyguard =
-                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        if (keyguard == null) {
-            return false;
-        }
-        
-        return true;
-    }
-
     private PowerManager.WakeLock mMediaEventWakeLock;
 
     private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
@@ -4028,6 +3986,12 @@
     private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
 
     /**
+     * The component the telephony package can register so telephony calls have priority to
+     * handle media button events
+     */
+    private ComponentName mMediaReceiverForCalls = null;
+
+    /**
      * Helper function:
      * Display in the log the current entries in the remote control focus stack
      */
@@ -4381,6 +4345,35 @@
     }
 
     /**
+     * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
+     * precondition: c != null
+     */
+    public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
+        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.e(TAG, "Invalid permissions to register media button receiver for calls");
+            return;
+        }
+        synchronized(mRCStack) {
+            mMediaReceiverForCalls = c;
+        }
+    }
+
+    /**
+     * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
+     */
+    public void unregisterMediaButtonEventReceiverForCalls() {
+        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
+            return;
+        }
+        synchronized(mRCStack) {
+            mMediaReceiverForCalls = null;
+        }
+    }
+
+    /**
      * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
      * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
      *     without modifying the RC stack, but while still causing the display to refresh (will
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 48f091c..6753ad3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -109,6 +109,9 @@
     oneway void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
     oneway void unregisterMediaButtonIntent(in PendingIntent pi,  in ComponentName c);
 
+    oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
+    oneway void unregisterMediaButtonEventReceiverForCalls();
+
     oneway void registerRemoteControlClient(in PendingIntent mediaIntent,
            in IRemoteControlClient rcClient, in String callingPackageName);
     oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
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/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 821a251b..c6c1ccb 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1444,7 +1444,9 @@
         String where;
         String[] selectionArgs;
         if (mCaseInsensitivePaths) {
-            where = Files.FileColumns.DATA + " LIKE ?";
+            // the 'like' makes it use the index, the 'lower()' makes it correct
+            // when the path contains sqlite wildcard characters
+            where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
             selectionArgs = new String[] { path };
         } else {
             where = Files.FileColumns.DATA + "=?";
@@ -1623,7 +1625,7 @@
                     if (line.startsWith("File")) {
                         int equals = line.indexOf('=');
                         if (equals > 0) {
-                            cachePlaylistEntry(line, playListDirectory);
+                            cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
                         }
                     }
                     line = reader.readLine();
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 18aa4b3..c365e4c 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -932,8 +932,11 @@
             if (format == MtpConstants.FORMAT_ASSOCIATION) {
                 // recursive case - delete all children first
                 Uri uri = Files.getMtpObjectsUri(mVolumeName);
-                int count = mMediaProvider.delete(uri, "_data LIKE ?",
-                        new String[] { path + "/%"});
+                int count = mMediaProvider.delete(uri,
+                        // the 'like' makes it use the index, the 'lower()' makes it correct
+                        // when the path contains sqlite wildcard characters
+                        "_data LIKE ? AND lower(substr(_data,?))=lower(?)",
+                        new String[] { path + "/%", "" + path.length() + 1, path + "/"});
             }
 
             Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index d1cf8b5..040d2ab 100755
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -47,20 +47,23 @@
     $(TOP)/frameworks/native/include/media/openmax
 
 LOCAL_SHARED_LIBRARIES := \
+    libandroid_runtime \
+    libaudioflinger \
     libaudioutils \
+    libbinder \
     libcutils \
     libdl \
-    libutils \
-    libandroid_runtime \
-    libnativehelper \
+    libgui \
     libmedia \
-    libaudioflinger \
-    libbinder \
+    libnativehelper \
     libstagefright \
     libstagefright_foundation \
     libstagefright_omx \
-    libgui \
-    libvideoeditorplayer
+    libutils \
+    libvideoeditor_core \
+    libvideoeditor_osal \
+    libvideoeditor_videofilters \
+    libvideoeditorplayer \
 
 
 LOCAL_CFLAGS += \
@@ -72,15 +75,6 @@
     -DUSE_STAGEFRIGHT_READERS \
     -DUSE_STAGEFRIGHT_3GPP_READER
 
-LOCAL_STATIC_LIBRARIES := \
-    libvideoeditor_core \
-    libstagefright_color_conversion \
-    libvideoeditor_3gpwriter \
-    libvideoeditor_mcs \
-    libvideoeditor_videofilters \
-    libvideoeditor_stagefrightshells \
-    libvideoeditor_osal
-
 LOCAL_MODULE:= libvideoeditor_jni
 
 LOCAL_MODULE_TAGS := optional
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
index 3657d8a..d8aa40f 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
@@ -376,8 +376,6 @@
 
     @Override
     public void process(FilterContext context) {
-        if (mLogVerbose) Log.v(TAG, "Starting frame processing");
-
         GLEnvironment glEnv = context.getGLEnvironment();
         // Get input frame
         Frame input = pullInput("videoframe");
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
index b023e42..674a2bd 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -121,6 +121,7 @@
     }
 
     public void updateRenderMode() {
+        if (mLogVerbose) Log.v(TAG, "updateRenderMode. Thread: " + Thread.currentThread());
         if (mRenderModeString != null) {
             if (mRenderModeString.equals("stretch")) {
                 mRenderMode = RENDERMODE_STRETCH;
@@ -139,6 +140,7 @@
 
     @Override
     public void prepare(FilterContext context) {
+        if (mLogVerbose) Log.v(TAG, "Prepare. Thread: " + Thread.currentThread());
         // Create identity shader to render, and make sure to render upside-down, as textures
         // are stored internally bottom-to-top.
         mProgram = ShaderProgram.createIdentity(context);
@@ -214,8 +216,10 @@
         float currentAspectRatio =
           (float)input.getFormat().getWidth() / input.getFormat().getHeight();
         if (currentAspectRatio != mAspectRatio) {
-            if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +
-                ", previously: " + mAspectRatio);
+            if (mLogVerbose) {
+                Log.v(TAG, "Process. New aspect ratio: " + currentAspectRatio +
+                    ", previously: " + mAspectRatio + ". Thread: " + Thread.currentThread());
+            }
             mAspectRatio = currentAspectRatio;
             updateTargetRect();
         }
@@ -249,6 +253,7 @@
 
     @Override
     public void fieldPortValueUpdated(String name, FilterContext context) {
+        if (mLogVerbose) Log.v(TAG, "FPVU. Thread: " + Thread.currentThread());
         updateRenderMode();
     }
 
@@ -260,16 +265,22 @@
     }
 
     private void updateTargetRect() {
+        if (mLogVerbose) Log.v(TAG, "updateTargetRect. Thread: " + Thread.currentThread());
         if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
             float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
             float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+            if (mLogVerbose) {
+                Log.v(TAG, "UTR. screen w = " + (float)mScreenWidth + " x screen h = " +
+                    (float)mScreenHeight + " Screen AR: " + screenAspectRatio +
+                    ", frame AR: "  + mAspectRatio + ", relative AR: " + relativeAspectRatio);
+            }
 
             if (relativeAspectRatio == 1.0f && mRenderMode != RENDERMODE_CUSTOMIZE) {
+                mProgram.setTargetRect(0, 0, 1, 1);
                 mProgram.setClearsOutput(false);
             } else {
                 switch (mRenderMode) {
                     case RENDERMODE_STRETCH:
-                        mProgram.setTargetRect(0, 0, 1, 1);
                         mTargetQuad.p0.set(0f, 0.0f);
                         mTargetQuad.p1.set(1f, 0.0f);
                         mTargetQuad.p2.set(0f, 1.0f);
@@ -313,6 +324,7 @@
                         ((ShaderProgram) mProgram).setSourceRegion(mSourceQuad);
                         break;
                 }
+                if (mLogVerbose) Log.v(TAG,  "UTR. quad: " + mTargetQuad);
                 ((ShaderProgram) mProgram).setTargetRegion(mTargetQuad);
             }
         }
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/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 50aaa9a..0165977 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,6 +34,7 @@
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.CharArrayReader;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.EOFException;
@@ -45,7 +46,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.zip.CRC32;
 
@@ -55,7 +60,7 @@
  */
 public class SettingsBackupAgent extends BackupAgentHelper {
     private static final boolean DEBUG = false;
-    private static final boolean DEBUG_BACKUP = DEBUG || true;
+    private static final boolean DEBUG_BACKUP = DEBUG || false;
 
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
@@ -111,6 +116,130 @@
     private WifiManager mWfm;
     private static String mWifiConfigFile;
 
+    // Class for capturing a network definition from the wifi supplicant config file
+    static class Network {
+        String ssid = "";  // equals() and hashCode() need these to be non-null
+        String key_mgmt = "";
+        final ArrayList<String> rawLines = new ArrayList<String>();
+
+        public static Network readFromStream(BufferedReader in) {
+            final Network n = new Network();
+            String line;
+            try {
+                while (in.ready()) {
+                    line = in.readLine();
+                    if (line == null || line.startsWith("}")) {
+                        break;
+                    }
+                    n.rememberLine(line);
+                }
+            } catch (IOException e) {
+                return null;
+            }
+            return n;
+        }
+
+        void rememberLine(String line) {
+            // can't rely on particular whitespace patterns so strip leading/trailing
+            line = line.trim();
+            if (line.isEmpty()) return; // only whitespace; drop the line
+            rawLines.add(line);
+
+            // remember the ssid and key_mgmt lines for duplicate culling
+            if (line.startsWith("ssid")) {
+                ssid = line;
+            } else if (line.startsWith("key_mgmt")) {
+                key_mgmt = line;
+            }
+        }
+
+        public void write(Writer w) throws IOException {
+            w.write("\nnetwork={\n");
+            for (String line : rawLines) {
+                w.write("\t" + line + "\n");
+            }
+            w.write("}\n");
+        }
+
+        public void dump() {
+            Log.v(TAG, "network={");
+            for (String line : rawLines) {
+                Log.v(TAG, "   " + line);
+            }
+            Log.v(TAG, "}");
+        }
+
+        // Same approach as Pair.equals() and Pair.hashCode()
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) return true;
+            if (!(o instanceof Network)) return false;
+            final Network other;
+            try {
+                other = (Network) o;
+            } catch (ClassCastException e) {
+                return false;
+            }
+            return ssid.equals(other.ssid) && key_mgmt.equals(other.key_mgmt);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 17;
+            result = 31 * result + ssid.hashCode();
+            result = 31 * result + key_mgmt.hashCode();
+            return result;
+        }
+    }
+
+    // Ingest multiple wifi config file fragments, looking for network={} blocks
+    // and eliminating duplicates
+    class WifiNetworkSettings {
+        // One for fast lookup, one for maintaining ordering
+        final HashSet<Network> mKnownNetworks = new HashSet<Network>();
+        final ArrayList<Network> mNetworks = new ArrayList<Network>(8);
+
+        public void readNetworks(BufferedReader in) {
+            try {
+                String line;
+                while (in.ready()) {
+                    line = in.readLine();
+                    if (line != null) {
+                        // Parse out 'network=' decls so we can ignore duplicates
+                        if (line.startsWith("network")) {
+                            Network net = Network.readFromStream(in);
+                            if (! mKnownNetworks.contains(net)) {
+                                if (DEBUG_BACKUP) {
+                                    Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
+                                }
+                                mKnownNetworks.add(net);
+                                mNetworks.add(net);
+                            } else {
+                                if (DEBUG_BACKUP) {
+                                    Log.v(TAG, "Dupe; skipped " + net.ssid + " / " + net.key_mgmt);
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                // whatever happened, we're done now
+            }
+        }
+
+        public void write(Writer w) throws IOException {
+            for (Network net : mNetworks) {
+                net.write(w);
+            }
+        }
+
+        public void dump() {
+            for (Network net : mNetworks) {
+                net.dump();
+            }
+        }
+    }
+
     @Override
     public void onCreate() {
         if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
@@ -622,29 +751,51 @@
 
     private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
         try {
-            File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
-            if (supplicantFile.exists()) supplicantFile.delete();
-            copyWifiSupplicantTemplate();
+            WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
 
-            OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
-            os.write("\n".getBytes());
-            os.write(bytes, 0, size);
-            os.close();
+            File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
+            if (supplicantFile.exists()) {
+                // Retain the existing APs; we'll append the restored ones to them
+                BufferedReader in = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT));
+                supplicantImage.readNetworks(in);
+                in.close();
+
+                supplicantFile.delete();
+            }
+
+            // Incorporate the restore AP information
+            if (size > 0) {
+                char[] restoredAsBytes = new char[size];
+                for (int i = 0; i < size; i++) restoredAsBytes[i] = (char) bytes[i];
+                BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsBytes));
+                supplicantImage.readNetworks(in);
+
+                if (DEBUG_BACKUP) {
+                    Log.v(TAG, "Final AP list:");
+                    supplicantImage.dump();
+                }
+            }
+
+            // Install the correct default template
+            BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
+            copyWifiSupplicantTemplate(bw);
+
+            // Write the restored supplicant config and we're done
+            supplicantImage.write(bw);
+            bw.close();
         } catch (IOException ioe) {
             Log.w(TAG, "Couldn't restore " + filename);
         }
     }
 
-    private void copyWifiSupplicantTemplate() {
+    private void copyWifiSupplicantTemplate(BufferedWriter bw) {
         try {
             BufferedReader br = new BufferedReader(new FileReader(FILE_WIFI_SUPPLICANT_TEMPLATE));
-            BufferedWriter bw = new BufferedWriter(new FileWriter(FILE_WIFI_SUPPLICANT));
             char[] temp = new char[1024];
             int size;
             while ((size = br.read(temp)) > 0) {
                 bw.write(temp, 0, size);
             }
-            bw.close();
             br.close();
         } catch (IOException ioe) {
             Log.w(TAG, "Couldn't copy wpa_supplicant file");
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0038d13..637c403 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" />
@@ -54,7 +55,7 @@
         android:allowBackup="false"
         android:hardwareAccelerated="true"
         android:label="@string/app_label"
-        android:icon="@drawable/ic_launcher_settings">
+        android:icon="@*android:drawable/platlogo">
 
         <!-- Broadcast receiver that gets the broadcast at boot time and starts
              up everything else.
diff --git a/packages/SystemUI/res/anim/search_launch_enter.xml b/packages/SystemUI/res/anim/search_launch_enter.xml
new file mode 100644
index 0000000..055ea5d
--- /dev/null
+++ b/packages/SystemUI/res/anim/search_launch_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shareInterpolator="false" android:zAdjustment="top">
+
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_quad"
+            android:duration="300"/>
+
+    <translate android:fromYDelta="200" android:toYDelta="0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_cubic"
+            android:duration="300" />
+</set>
diff --git a/packages/SystemUI/res/anim/search_launch_exit.xml b/packages/SystemUI/res/anim/search_launch_exit.xml
new file mode 100644
index 0000000..b4ed278
--- /dev/null
+++ b/packages/SystemUI/res/anim/search_launch_exit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:anim/accelerate_interpolator"
+       android:fromXDelta="0" android:toXDelta="0"
+       android:duration="300" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
index daf18c7..d9ec745 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..117cf19
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
index 5292998..2cebe85 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..7c60bea
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_signal_null.png
index 3733a38..709b181 100644
--- a/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..4fd3a08
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw720dp-hdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_signal_null.png
index ab718ad..67d5cbf 100644
--- a/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..1943e8c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw720dp-mdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_signal_null.png
index 023bbe6..fa8735d 100644
--- a/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..de573b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw720dp-xhdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_null.png
index 3e7fefd..90b8c84 100644
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_signal_null.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_signal_null.png
new file mode 100644
index 0000000..5881402
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_wifi_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/navbar_search_outerring.xml b/packages/SystemUI/res/drawable/navbar_search_outerring.xml
index 37b6c1c..0dd081d 100644
--- a/packages/SystemUI/res/drawable/navbar_search_outerring.xml
+++ b/packages/SystemUI/res/drawable/navbar_search_outerring.xml
@@ -19,5 +19,5 @@
     <size android:height="@dimen/navbar_search_outerring_diameter"
         android:width="@dimen/navbar_search_outerring_diameter" />
     <solid android:color="#00000000" />
-    <stroke android:color="#1affffff" android:width="2dp" />
+    <stroke android:color="#40ffffff" android:width="2dp" />
 </shape>
\ No newline at end of file
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-sw600dp/super_status_bar.xml b/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
index b9af3a9..f93dd33 100644
--- a/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
@@ -26,15 +26,16 @@
     android:fitsSystemWindows="true"
     >
 
+    <include layout="@layout/status_bar"
+        android:layout_width="match_parent"
+        android:layout_height="@*android:dimen/status_bar_height"
+        />
+
     <include layout="@layout/status_bar_expanded"
         android:layout_width="@dimen/notification_panel_width"
         android:layout_height="match_parent"
         android:layout_gravity="center_horizontal|top"
-        />
-
-    <include layout="@layout/status_bar"
-        android:layout_width="match_parent"
-        android:layout_height="@*android:dimen/status_bar_height"
+        android:visibility="invisible"
         />
 
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
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/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index b905db3..d41040d 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -69,7 +69,7 @@
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
                 systemui:keyCode="3"
-                systemui:keyRepeat="true"
+                systemui:keyRepeat="false"
                 android:layout_weight="0"
                 systemui:glowBackground="@drawable/ic_sysbar_highlight"
                 android:contentDescription="@string/accessibility_home"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4cff67b..406ed25 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -23,34 +23,39 @@
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
+    android:layout_height="match_parent"
     android:background="@drawable/notification_panel_bg"
     android:paddingTop="@dimen/notification_panel_padding_top"
     android:layout_marginLeft="@dimen/notification_panel_margin_left"
     >
 
-    <include layout="@layout/status_bar_expanded_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        />
- 
-    <ScrollView
-        android:id="@+id/scroll"
+    <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:fadingEdge="none"
-        android:overScrollMode="ifContentScrolls"
-        android:layout_marginTop="@dimen/notification_panel_header_height"
         android:layout_marginBottom="@dimen/close_handle_underlap"
         >
-        <com.android.systemui.statusbar.policy.NotificationRowLayout
-            android:id="@+id/latestItems"
+
+        <include layout="@layout/status_bar_expanded_header"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            systemui:rowHeight="@dimen/notification_row_min_height"
+            android:layout_height="48dp"
             />
-    </ScrollView>
+     
+        <ScrollView
+            android:id="@+id/scroll"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fadingEdge="none"
+            android:overScrollMode="ifContentScrolls"
+            android:layout_marginTop="@dimen/notification_panel_header_height"
+            >
+            <com.android.systemui.statusbar.policy.NotificationRowLayout
+                android:id="@+id/latestItems"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                systemui:rowHeight="@dimen/notification_row_min_height"
+                />
+        </ScrollView>
+    </FrameLayout>
 
     <com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close"
         android:layout_width="match_parent"
@@ -67,5 +72,4 @@
             />
 
     </com.android.systemui.statusbar.phone.CloseDragHandle>
-
 </FrameLayout><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 6c31ff4..b85686f 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,14 +26,15 @@
     android:fitsSystemWindows="true"
     >
 
-    <include layout="@layout/status_bar_expanded"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        />
-
     <include layout="@layout/status_bar"
         android:layout_width="match_parent"
         android:layout_height="@*android:dimen/status_bar_height"
         />
 
+    <include layout="@layout/status_bar_expanded"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"
+        />
+
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index b4e0d8a..06a9395 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -144,10 +144,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Información de la aplicación"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Notificaciones desactivadas"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Toca aquí para volver a activar las notificaciones."</string>
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla rotará automáticamente."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 46adfd7..aae070a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -26,7 +26,7 @@
     <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूची से निकालें"</string>
     <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"एप्‍लिकेशन जानकारी"</string>
     <string name="status_bar_no_recent_apps" msgid="6576392951053994640">"कोई हाल ही के एप्लिकेशन नहीं"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"हाल ही के एप्लिकेशन ख़ारिज करें"</string>
+    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"हाल ही के एप्लिकेशन खारिज करें"</string>
   <plurals name="status_bar_accessibility_recent_apps">
     <item quantity="one" msgid="5854176083865845541">"1 हाल ही का एप्लिकेशन"</item>
     <item quantity="other" msgid="1040784359794890744">"%d हाल ही के एप्लिकेशन"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a117252..a69fc23 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -141,8 +141,7 @@
     <string name="dreams_dock_launcher" msgid="3541196417659166245">"Aktifkan tirai layar"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Info aplikasi"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Pemberitahuan mati"</string>
-    <!-- no translation found for notifications_off_text (2529001315769385273) -->
-    <skip />
+    <string name="notifications_off_text" msgid="2529001315769385273">"Ketuk di sini untuk menyalakan pemberitahuan lagi."</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Layar akan diputar secara otomatis."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a242640..018b5e5 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -142,10 +142,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Programos informacija"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Pranešimai išjungti"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Jei norite vėl įjungti pranešimus, palieskite čia."</string>
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekranas bus sukamas automatiškai."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Užrakintas ekranas yra horizontalios orientacijos."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Užrakintas ekranas yra vertikalios orientacijos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 3c2e193..e708804 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -142,10 +142,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informācija par lietotni"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Paziņojumi ir izslēgti"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Pieskarieties šeit, lai atkal ieslēgtu paziņojumus."</string>
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekrāns tiks pagriezts automātiski."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 77ed068..d08c529 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -142,10 +142,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Informações da aplicação"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Notificações desativadas"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Toque aqui para voltar a ativar as notificações."</string>
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"O ecrã será rodado automaticamente."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"O ecrã está bloqueado na orientação horizontal."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"O ecrã está bloqueado na orientação vertical."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3277fb6..7a7d08f 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -38,7 +38,7 @@
     <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея разряжена."</string>
     <string name="battery_low_percent_format" msgid="1077244949318261761">"Осталось <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"Зарядка через порт USB не поддерживается."\n"Используйте только зарядное устройство из комплекта поставки."</string>
-    <string name="battery_low_why" msgid="7279169609518386372">"Расход заряда батареи"</string>
+    <string name="battery_low_why" msgid="7279169609518386372">"Подробнее"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Настройки"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим полета"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 5026d6d..1fa3b21 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -141,7 +141,7 @@
     <string name="dreams_dock_launcher" msgid="3541196417659166245">"Vklop ohranjevalnika zaslona"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Podatki o aplikaciji"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Obvestila so izklopljena"</string>
-    <string name="notifications_off_text" msgid="2529001315769385273">"Dotaknite se tukaj, da spet vklopite obvestila."</string>
+    <string name="notifications_off_text" msgid="2529001315769385273">"Dotaknite se tukaj, da ponovno vklopite obvestila."</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon se bo samodejno zasukal."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5f523a2..fd41e02 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -140,10 +140,8 @@
     <string name="accessibility_clear_all" msgid="5235938559247164925">"Обриши сва обавештења."</string>
     <string name="dreams_dock_launcher" msgid="3541196417659166245">"Активирање чувара екрана"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Информације о апликацији"</string>
-    <!-- no translation found for notifications_off_title (8936620513608443224) -->
-    <skip />
-    <!-- no translation found for notifications_off_text (2529001315769385273) -->
-    <skip />
+    <string name="notifications_off_title" msgid="8936620513608443224">"Обавештења су искључена"</string>
+    <string name="notifications_off_text" msgid="2529001315769385273">"Додирните овде да бисте поново укључили обавештења."</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран ће се аутоматски ротирати."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран је закључан у хоризонталном положају."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран је закључан у вертикалном положају."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 07d55f1..1bf59b0 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -28,13 +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>
-
-    <!-- Extra space above the clock in the panel; on this device, zero -->
-    <dimen name="notification_panel_header_padding_top">0dp</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-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index dbb8d91..70f0b9b 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -140,14 +140,9 @@
     <string name="accessibility_clear_all" msgid="5235938559247164925">"ล้างการแจ้งเตือนทั้งหมด"</string>
     <string name="dreams_dock_launcher" msgid="3541196417659166245">"เปิดโปรแกรมรักษาหน้าจอ"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"ข้อมูลแอป"</string>
-    <!-- no translation found for notifications_off_title (8936620513608443224) -->
-    <skip />
-    <!-- no translation found for notifications_off_text (2529001315769385273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="notifications_off_title" msgid="8936620513608443224">"การแจ้งเตือนปิดอยู่"</string>
+    <string name="notifications_off_text" msgid="2529001315769385273">"แตะที่นี่เพื่อเปิดการแจ้งเตือนอีกครั้ง"</string>
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"หน้าจอจะหมุนโดยอัตโนมัติ"</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d0f08fc..9bf8f76 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -140,14 +140,9 @@
     <string name="accessibility_clear_all" msgid="5235938559247164925">"Tüm bildirimleri temizle"</string>
     <string name="dreams_dock_launcher" msgid="3541196417659166245">"Ekran koruyucuyu etkinleştir"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Uygulama bilgileri"</string>
-    <!-- no translation found for notifications_off_title (8936620513608443224) -->
-    <skip />
-    <!-- no translation found for notifications_off_text (2529001315769385273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="notifications_off_title" msgid="8936620513608443224">"Bildirimler kapalı"</string>
+    <string name="notifications_off_text" msgid="2529001315769385273">"Bildirimleri tekrar açmak için buraya hafifçe vurun."</string>
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran otomatik olarak dönecektir."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 0019c7c..02d0138 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -142,10 +142,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Thông tin về ứng dụng"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Tắt thông báo"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Chạm vào đây để bật lại thông báo."</string>
-    <!-- no translation found for accessibility_rotation_lock_off (4062780228931590069) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_landscape (6731197337665366273) -->
-    <skip />
-    <!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
-    <skip />
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Màn hinh sẽ xoay tự động."</string>
+    <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
+    <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8293d99..4f3e787 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -142,7 +142,7 @@
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"Ulwazi lohlelo lokusebenza"</string>
     <string name="notifications_off_title" msgid="8936620513608443224">"Izaziso zivaliwe"</string>
     <string name="notifications_off_text" msgid="2529001315769385273">"Thepha lapha ukuvula futhi izaziso."</string>
-    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Isikrini sizophenduka ngokuzanzakalela."</string>
+    <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Isikrini sizophenduka ngokuzenzakalela."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
 </resources>
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/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9042045..b1611d1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -115,10 +115,10 @@
     <dimen name="navbar_search_hit_radius">60dip</dimen>
 
     <!-- Diameter of outer shape drawable shown in navbar search-->
-    <dimen name="navbar_search_outerring_diameter">270dp</dimen>
+    <dimen name="navbar_search_outerring_diameter">340dp</dimen>
 
     <!-- Threshold for swipe-up gesture to activate search dialog -->
-    <dimen name="navbar_search_up_threshhold">20dip</dimen>
+    <dimen name="navbar_search_up_threshhold">40dip</dimen>
 
     <!-- Height of search panel including navigation bar height -->
     <dimen name="navbar_search_panel_height">230dip</dimen>
@@ -132,10 +132,10 @@
     <!-- Height of the notification panel header bar -->
     <dimen name="notification_panel_header_height">48dp</dimen>
 
-    <!-- Height of the notification panel header bar -->
-    <dimen name="notification_panel_padding_top">@*android:dimen/status_bar_height</dimen>
+    <!-- Extra space above the panel -->
+    <dimen name="notification_panel_padding_top">4dp</dimen>
 
-    <!-- Extra space above the clock in the panel; half of (notification_panel_header_height - 32) -->
+    <!-- Extra space above the clock in the panel -->
     <dimen name="notification_panel_header_padding_top">0dp</dimen>
 
     <!-- Layout parameters for the notification panel -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 144760e..a23fe12 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -16,12 +16,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="TextAppearance.StatusBar.Title" parent="@*android:style/TextAppearance.StatusBar">
-        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
-        <item name="android:textStyle">bold</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
-    </style>
-
     <style name="TextAppearance.StatusBar.IntruderAlert"
         parent="@*android:style/TextAppearance.StatusBar">
     </style>
@@ -48,7 +42,7 @@
     </style>
 
     <style name="TextAppearance.StatusBar.Date" parent="@*android:style/TextAppearance.StatusBar.Icon">
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">16dp</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">@android:color/holo_blue_light</item>
     </style>
@@ -57,6 +51,7 @@
 
     <style name="TextAppearance.StatusBar.Expanded.Clock">
         <item name="android:textSize">32dp</item>
+        <item name="android:fontFamily">sans-serif-light</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">#ffffff</item>
     </style>
@@ -64,7 +59,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">12dp</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#666666</item>
+        <item name="android:textColor">#cccccc</item>
         <item name="android:textAllCaps">true</item>
     </style>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
index e4f00d6..616d72f 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBag.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -81,6 +81,10 @@
             return (float) Math.sqrt(x*x+y*y);
         }
 
+        static float clamp(float x, float a, float b) {
+            return ((x<a)?a:((x>b)?b:x));
+        }
+
         static float dot(float x1, float y1, float x2, float y2) {
             return x1*x2+y1+y2;
         }
@@ -149,6 +153,7 @@
             public boolean grabbed;
             public float grabx, graby;
             public long grabtime;
+            private float grabx_offset, graby_offset;
 
             public Bean(Context context, AttributeSet as) {
                 super(context, as);
@@ -236,17 +241,20 @@
                 switch (e.getAction()) {
                     case MotionEvent.ACTION_DOWN:
                         grabbed = true;
+                        grabx_offset = e.getRawX() - x;
+                        graby_offset = e.getRawY() - y;
                         va = 0;
                         // fall
                     case MotionEvent.ACTION_MOVE:
-                        grabx = e.getRawX();
-                        graby = e.getRawY();
+                        grabx = e.getRawX() - grabx_offset;
+                        graby = e.getRawY() - graby_offset;
                         grabtime = e.getEventTime();
                         break;
                     case MotionEvent.ACTION_CANCEL:
                     case MotionEvent.ACTION_UP:
                         grabbed = false;
-                        va = randfrange(-5,5);
+                        float a = randsign() * clamp(mag(vx, vy) * 0.33f, 0, 1080f);
+                        va = randfrange(a*0.5f, a);
                         break;
                 }
                 return true;
@@ -308,47 +316,6 @@
                             if (!(v2 instanceof Bean)) continue;
                             Bean nv2 = (Bean) v2;
                             final float overlap = nv.overlap(nv2);
-                            if (false && overlap < 0) {
-                                // angle pointing from nv2 to nv
-                                final float dx = nv.x - nv2.x;
-                                final float dy = nv.y - nv2.y;
-                                final float ang = (float) Math.atan2(dx, dy);
-
-                                if (false) {
-                                nv.vx -= Math.cos(ang) * overlap * 0.5f;
-                                nv.vy -= Math.sin(ang) * overlap * 0.5f;
-                                nv2.vx += Math.cos(ang) * overlap * 0.5f;
-                                nv2.vy += Math.sin(ang) * overlap * 0.5f;
-                                }
-
-
-                                // first, move them apart
-                                nv.x -= Math.cos(ang) * overlap/2;
-                                nv.y -= Math.sin(ang) * overlap/2;
-                                nv2.x += Math.cos(ang) * overlap/2;
-                                nv2.y += Math.sin(ang) * overlap/2;
-
-                                // next, figure out velocities
-                                final float sap = 0f; // randfrange(0,0.25f);
-
-                                final float mag1 = mag(nv.vx, nv.vy) * (1f-sap);
-                                final float mag2 = mag(nv2.vx, nv2.vy) * (1f-sap);
-
-
-                                // hacky way to transfer "momentum"
-                                nv.vx = mag2 * (float)Math.cos(ang);
-                                nv.vy = mag2 * (float)Math.sin(ang);
-                                nv2.vx = -mag1 * (float)Math.cos(ang);
-                                nv2.vy = -mag1 * (float)Math.sin(ang);
-
-                                final float totalva = nv.va + nv2.va;
-                                final float frac = randfrange(0.25f,0.75f);
-                                nv.va = totalva * frac;
-                                nv2.va = totalva * (1f-frac);
-//                                nv.va += randfrange(-20,20);
-//                                nv2.va += randfrange(-20,20);
-
-                            }
                         }
 
                         nv.setRotation(nv.a);
@@ -375,17 +342,28 @@
             boardWidth = w;
             boardHeight = h;
 //            android.util.Log.d("Nyandroid", "resized: " + w + "x" + h);
-            post(new Runnable() { public void run() {
-                reset();
-                mAnim.start();
-            } });
         }
 
+        public void startAnimation() {
+            stopAnimation();
+            if (mAnim == null) {
+                post(new Runnable() { public void run() {
+                    reset();
+                    startAnimation();
+                } });
+            } else {
+                mAnim.start();
+            }
+        }
+
+        public void stopAnimation() {
+            if (mAnim != null) mAnim.cancel();
+        }
 
         @Override
         protected void onDetachedFromWindow() {
             super.onDetachedFromWindow();
-            mAnim.cancel();
+            stopAnimation();
         }
 
         @Override
@@ -428,12 +406,19 @@
                   WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
                 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                 );
+        mBoard = new Board(this, null);
+        setContentView(mBoard);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mBoard.stopAnimation();
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        mBoard = new Board(this, null);
-        setContentView(mBoard);
+        mBoard.startAnimation();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 7a7afa7..ba3336b 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -31,7 +31,7 @@
 
 public class ExpandHelper implements Gefingerpoken, OnClickListener {
     public interface Callback {
-        View getChildAtPosition(MotionEvent ev);
+        View getChildAtRawPosition(float x, float y);
         View getChildAtPosition(float x, float y);
         boolean canChildBeExpanded(View v);
         boolean setUserExpandedChild(View v, boolean userxpanded);
@@ -62,6 +62,7 @@
     private Context mContext;
 
     private boolean mStretching;
+    private View mEventSource;
     private View mCurrView;
     private View mCurrViewTopGlow;
     private View mCurrViewBottomGlow;
@@ -141,7 +142,19 @@
             @Override
             public boolean onScaleBegin(ScaleGestureDetector detector) {
                 if (DEBUG) Log.v(TAG, "onscalebegin()");
-                View v = mCallback.getChildAtPosition(detector.getFocusX(), detector.getFocusY());
+                float x = detector.getFocusX();
+                float y = detector.getFocusY();
+
+                View v = null;
+                if (mEventSource != null) {
+                    int[] location = new int[2];
+                    mEventSource.getLocationOnScreen(location);
+                    x += (float) location[0];
+                    y += (float) location[1];
+                    v = mCallback.getChildAtRawPosition(x, y);
+                } else {
+                    v = mCallback.getChildAtPosition(x, y);
+                }
 
                 // your fingers have to be somewhat close to the bounds of the view in question
                 mInitialTouchFocusY = detector.getFocusY();
@@ -189,6 +202,11 @@
             }
         });
     }
+
+    public void setEventSource(View eventSource) {
+        mEventSource = eventSource;
+    }
+
     public void setGlow(float glow) {
         if (!mGlowAnimationSet.isRunning() || glow == 0f) {
             if (mGlowAnimationSet.isRunning()) {
@@ -211,7 +229,6 @@
             }
         }
     }
-
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DEBUG) Log.d(TAG, "interceptTouch: act=" + (ev.getAction()) +
                          " stretching=" + mStretching);
@@ -223,11 +240,13 @@
         final int action = ev.getAction();
         if (DEBUG) Log.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching);
         if (mStretching) {
+            if (DEBUG) Log.d(TAG, "detector ontouch");
             mDetector.onTouchEvent(ev);
         }
         switch (action) {
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+                if (DEBUG) Log.d(TAG, "cancel");
                 mStretching = false;
                 clearView();
                 break;
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/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 57f15a8..28283ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -18,22 +18,27 @@
 
 import android.animation.Animator;
 import android.animation.LayoutTransition;
+import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.speech.RecognizerIntent;
+import android.content.pm.PackageManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.widget.FrameLayout;
 
 import com.android.internal.widget.multiwaveview.MultiWaveView;
 import com.android.internal.widget.multiwaveview.MultiWaveView.OnTriggerListener;
+import com.android.server.am.ActivityManagerService;
 import com.android.systemui.R;
 import com.android.systemui.recent.StatusBarTouchProxy;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -68,31 +73,55 @@
 
     private SearchManager mSearchManager;
 
+    // This code should be the same as that used in LockScreen.java
     public boolean isAssistantAvailable() {
-        return mSearchManager != null && mSearchManager.getGlobalSearchActivity() != null;
+        Intent intent = getAssistIntent();
+        return intent == null ? false
+                : mContext.getPackageManager().queryIntentActivities(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
     }
 
-    private void startAssistActivity() {
-        if (mSearchManager != null) {
-            ComponentName globalSearchActivity = mSearchManager.getGlobalSearchActivity();
+    private Intent getAssistIntent() {
+        Intent intent = null;
+        SearchManager searchManager = getSearchManager();
+        if (searchManager != null) {
+            ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
             if (globalSearchActivity != null) {
-                Intent intent = new Intent(Intent.ACTION_ASSIST);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                intent = new Intent(Intent.ACTION_ASSIST);
                 intent.setPackage(globalSearchActivity.getPackageName());
-                try {
-                    mContext.startActivity(intent);
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, "Activity not found for " + intent.getAction());
-                }
             } else {
                 Slog.w(TAG, "No global search activity");
             }
+        } else {
+            Slog.w(TAG, "No SearchManager");
+        }
+        return intent;
+    }
+
+    private SearchManager getSearchManager() {
+        if (mSearchManager == null) {
+            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+        }
+        return mSearchManager;
+    }
+
+    private void startAssistActivity() {
+        Intent intent = getAssistIntent();
+        try {
+            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+                    R.anim.search_launch_enter, R.anim.search_launch_exit);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent, opts.toBundle());
+        } catch (ActivityNotFoundException e) {
+            Slog.w(TAG, "Activity not found for " + intent.getAction());
         }
     }
 
     final MultiWaveView.OnTriggerListener mMultiWaveViewListener
             = new MultiWaveView.OnTriggerListener() {
 
+        private int mTarget = -1;
+
         public void onGrabbed(View v, int handle) {
         }
 
@@ -106,11 +135,18 @@
         }
 
         public void onTrigger(View v, int target) {
-            final int resId = mMultiWaveView.getResourceIdForTarget(target);
-            switch (resId) {
-                case com.android.internal.R.drawable.ic_lockscreen_search:
-                    startAssistActivity();
-                break;
+            mTarget = target;
+        }
+
+        public void onFinishFinalAnimation() {
+            if (mTarget != -1) {
+                final int resId = mMultiWaveView.getResourceIdForTarget(mTarget);
+                mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger
+                switch (resId) {
+                    case com.android.internal.R.drawable.ic_lockscreen_search:
+                        startAssistActivity();
+                    break;
+                }
             }
             mBar.hideSearchPanel();
         }
@@ -146,6 +182,14 @@
         }
     }
 
+    private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+        public boolean onPreDraw() {
+            getViewTreeObserver().removeOnPreDrawListener(this);
+            mMultiWaveView.resumeAnimations();
+            return false;
+        }
+    };
+
     public void show(final boolean show, boolean animate) {
         if (animate) {
             if (mShowing != show) {
@@ -156,16 +200,20 @@
             mShowing = show;
             onAnimationEnd(null);
         }
-        postDelayed(new Runnable() {
-            public void run() {
-                setVisibility(show ? View.VISIBLE : View.INVISIBLE);
-                if (show) {
-                    setFocusable(true);
-                    setFocusableInTouchMode(true);
-                    requestFocus();
-                }
+        if (show) {
+            if (getVisibility() != View.VISIBLE) {
+                setVisibility(View.VISIBLE);
+                // Don't start the animation until we've created the layer, which is done
+                // right before we are drawn
+                mMultiWaveView.suspendAnimations();
+                getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
             }
-        }, show ? 0 : 100);
+            setFocusable(true);
+            setFocusableInTouchMode(true);
+            requestFocus();
+        } else {
+            setVisibility(View.INVISIBLE);
+        }
     }
 
     public void hide(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 6584c7d..2d65dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -186,6 +186,7 @@
     public void removeLongPressCallback() {
         if (mWatchLongPress != null) {
             mHandler.removeCallbacks(mWatchLongPress);
+            mWatchLongPress = null;
         }
     }
 
@@ -245,6 +246,7 @@
                 mCurrView = null;
                 mCurrAnimView = null;
                 mLongPressSent = false;
+                removeLongPressCallback();
                 break;
         }
         return mDragging;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 9e273d4..3502b62 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -41,7 +41,7 @@
  */
 public class RingtonePlayer extends SystemUI {
     private static final String TAG = "RingtonePlayer";
-    private static final boolean LOGD = true;
+    private static final boolean LOGD = false;
 
     // TODO: support Uri switching under same IBinder
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 4b223dd..a352748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -531,30 +531,27 @@
             }
         }
         catch (RuntimeException e) {
-            exception = e;
-        }
-        if (expandedOneU == null && expandedLarge == null) {
             final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
-            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+            Slog.e(TAG, "couldn't inflate view for notification " + ident, e);
             return false;
-        } else {
-            if (expandedOneU != null) {
-                SizeAdaptiveLayout.LayoutParams params =
-                        new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
-                params.minHeight = minHeight;
-                params.maxHeight = minHeight;
-                adaptive.addView(expandedOneU, params);
-            }
-            if (expandedLarge != null) {
-                SizeAdaptiveLayout.LayoutParams params =
-                        new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
-                params.minHeight = minHeight+1;
-                params.maxHeight = maxHeight;
-                adaptive.addView(expandedLarge, params);
-            }
-            row.setDrawingCacheEnabled(true);
         }
 
+        if (expandedOneU != null) {
+            SizeAdaptiveLayout.LayoutParams params =
+                    new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
+            params.minHeight = rowHeight;
+            params.maxHeight = rowHeight;
+            adaptive.addView(expandedOneU, params);
+        }
+        if (expandedLarge != null) {
+            SizeAdaptiveLayout.LayoutParams params =
+                    new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
+            params.minHeight = rowHeight+1;
+            params.maxHeight = maxHeight;
+            adaptive.addView(expandedLarge, params);
+        }
+        row.setDrawingCacheEnabled(true);
+
         applyLegacyRowBackground(sbn, content);
 
         row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 0e6dfd5..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) {
@@ -63,7 +59,8 @@
                 break;
         }
         if (mDelegateView != null) {
-            if (mDelegateView.getVisibility() != View.VISIBLE && event.getAction() != MotionEvent.ACTION_CANCEL) {
+            if (mDelegateView.getVisibility() != View.VISIBLE
+                    && event.getAction() != MotionEvent.ACTION_CANCEL) {
                 final boolean isVertical = (mOrientation == Surface.ROTATION_90
                         || mOrientation == Surface.ROTATION_270);
                 final int historySize = event.getHistorySize();
@@ -91,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/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 2f02d23..1321ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -21,9 +21,9 @@
 import android.util.Slog;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.systemui.statusbar.policy.NetworkController;
 
@@ -31,12 +31,12 @@
 
 // Intimately tied to the design of res/layout/signal_cluster_view.xml
 public class SignalClusterView
-        extends LinearLayout 
+        extends LinearLayout
         implements NetworkController.SignalCluster {
 
     static final boolean DEBUG = false;
     static final String TAG = "SignalClusterView";
-    
+
     NetworkController mNC;
 
     private boolean mWifiVisible = false;
@@ -132,6 +132,17 @@
         apply();
     }
 
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        // Standard group layout onPopulateAccessibilityEvent() implementations
+        // ignore content description, so populate manually
+        if (mWifiVisible && mWifiGroup.getContentDescription() != null)
+            event.getText().add(mWifiGroup.getContentDescription());
+        if (mMobileVisible && mMobileGroup.getContentDescription() != null)
+            event.getText().add(mMobileGroup.getContentDescription());
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+
     // Run after each indicator change.
     private void apply() {
         if (mWifiGroup == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 287c2922..d38611d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -289,7 +289,7 @@
                         animateCollapse();
                     }
                 }
-                return true;
+                return mStatusBarWindow.onTouchEvent(event);
             }});
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
@@ -301,6 +301,9 @@
                 return true;
             }
         });
+        mNotificationPanel.setSystemUiVisibility(
+                  View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
+                | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
 
         if (!ActivityManager.isHighEndGfx(mDisplay)) {
             mStatusBarWindow.setBackground(null);
@@ -336,7 +339,6 @@
         mPixelFormat = PixelFormat.OPAQUE;
         mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
         mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
-        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
         mNotificationIcons.setOverflowIndicator(mMoreIcon);
         mIcons = (LinearLayout)mStatusBarView.findViewById(R.id.icons);
         mTickerView = mStatusBarView.findViewById(R.id.ticker);
@@ -513,14 +515,8 @@
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
-                    Slog.d(TAG, "showing search panel");
                     showSearchPanel();
                 break;
-
-                case MotionEvent.ACTION_UP:
-                    Slog.d(TAG, "hiding search panel");
-                    hideSearchPanel();
-                break;
             }
             return false;
         }
@@ -531,8 +527,8 @@
 
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
+        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
         updateSearchPanel();
-//        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
     }
 
     // For small-screen devices (read: phones) that lack hardware navigation buttons
@@ -884,6 +880,15 @@
         flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
         flagdbg.append(">");
         Slog.d(TAG, flagdbg.toString());
+        
+        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+            mIcons.animate().cancel();
+            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+                mIcons.animate().alpha(0f).setStartDelay(100).setDuration(200).start();
+            } else {
+                mIcons.animate().alpha(1f).setStartDelay(0).setDuration(300).start();
+            }
+        }
 
         if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
             boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
@@ -985,19 +990,19 @@
         }
         
         mExpandedVisible = true;
+        mNotificationPanel.setVisibility(View.VISIBLE);
 
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
 
         // Expand the window to encompass the full screen in anticipation of the drag.
         // This is only possible to do atomically because the status bar is at the top of the screen!
         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
-        lp.flags &= (~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
         final WindowManager wm = WindowManagerImpl.getDefault();
         wm.updateViewLayout(mStatusBarWindow, lp);
 
-        mStatusBarWindow.requestFocus(View.FOCUS_FORWARD);
-
         visibilityChanged(true);
     }
 
@@ -1079,12 +1084,13 @@
         }
         mExpandedVisible = false;
         visibilityChanged(false);
-        //mNotificationPanel.setVisibility(View.GONE);
+        mNotificationPanel.setVisibility(View.INVISIBLE);
 
         // Shrink the window to the size of the status bar only
         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
         lp.height = getStatusBarHeight();
-        lp.flags |= (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         final WindowManager wm = WindowManagerImpl.getDefault();
         wm.updateViewLayout(mStatusBarWindow, lp);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 0fc5b4d..ed1b2f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -18,17 +18,39 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.widget.FrameLayout;
 import android.widget.TextSwitcher;
 
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
+
 
 public class StatusBarWindowView extends FrameLayout
 {
+    private static final String TAG = "StatusBarWindowView";
+
+    private ExpandHelper mExpandHelper;
+    private NotificationRowLayout latestItems;
+
     PhoneStatusBar mService;
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setMotionEventSplittingEnabled(false);
+    }
+
+    @Override
+    protected void onAttachedToWindow () {
+        super.onAttachedToWindow();
+        latestItems = (NotificationRowLayout) findViewById(R.id.latestItems);
+        int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
+        int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
+        mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
+        mExpandHelper.setEventSource(this);
     }
 
     @Override
@@ -43,5 +65,25 @@
         }
         return super.dispatchKeyEvent(event);
     }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        MotionEvent cancellation = MotionEvent.obtain(ev);
+        cancellation.setAction(MotionEvent.ACTION_CANCEL);
+
+        boolean intercept = mExpandHelper.onInterceptTouchEvent(ev) ||
+                super.onInterceptTouchEvent(ev);
+        if (intercept) {
+            latestItems.onInterceptTouchEvent(cancellation);
+        }
+        return intercept;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = mExpandHelper.onTouchEvent(ev) ||
+                super.onTouchEvent(ev);
+        return handled;
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
index a31e2a4..3d63781 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
@@ -18,32 +18,40 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.os.AsyncTask;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
-import android.util.Slog;
 import android.view.IWindowManager;
 import android.widget.CompoundButton;
 
-/**
- * TODO: Listen for changes to the setting.
- */
 public class AutoRotateController implements CompoundButton.OnCheckedChangeListener {
     private static final String TAG = "StatusBar.AutoRotateController";
 
-    private Context mContext;
-    private CompoundButton mCheckBox;
+    private final Context mContext;
+    private final CompoundButton mCheckbox;
 
     private boolean mAutoRotation;
 
+    private ContentObserver mAccelerometerRotationObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateCheckbox();
+        }
+    };
+
     public AutoRotateController(Context context, CompoundButton checkbox) {
         mContext = context;
-        mAutoRotation = getAutoRotation();
-        mCheckBox = checkbox;
-        checkbox.setChecked(mAutoRotation);
-        checkbox.setOnCheckedChangeListener(this);
+        mCheckbox = checkbox;
+        updateCheckbox();
+        mCheckbox.setOnCheckedChangeListener(this);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), true,
+                mAccelerometerRotationObserver);
     }
 
     public void onCheckedChanged(CompoundButton view, boolean checked) {
@@ -52,6 +60,15 @@
         }
     }
 
+    public void release() {
+        mContext.getContentResolver().unregisterContentObserver(mAccelerometerRotationObserver);
+    }
+
+    private void updateCheckbox() {
+        mAutoRotation = getAutoRotation();
+        mCheckbox.setChecked(mAutoRotation);
+    }
+
     private boolean getAutoRotation() {
         ContentResolver cr = mContext.getContentResolver();
         return 0 != Settings.System.getInt(cr, Settings.System.ACCELEROMETER_ROTATION, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index a00fab3..bb0ce16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,6 +52,7 @@
     int mCode;
     int mTouchSlop;
     Drawable mGlowBG;
+    int mGlowWidth, mGlowHeight;
     float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
     boolean mSupportsLongpress = true;
     RectF mRect = new RectF(0f,0f,0f,0f);
@@ -89,6 +90,8 @@
         mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
         if (mGlowBG != null) {
             setDrawingAlpha(BUTTON_QUIESCENT_ALPHA);
+            mGlowWidth = mGlowBG.getIntrinsicWidth();
+            mGlowHeight = mGlowBG.getIntrinsicHeight();
         }
         
         a.recycle();
@@ -103,8 +106,12 @@
             canvas.save();
             final int w = getWidth();
             final int h = getHeight();
+            final float aspect = (float)mGlowWidth / mGlowHeight;
+            final int drawW = (int)(h*aspect);
+            final int drawH = h;
+            final int margin = (drawW-w)/2;
             canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
-            mGlowBG.setBounds(0, 0, w, h);
+            mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
             mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
             mGlowBG.draw(canvas);
             canvas.restore();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index a05fcc1..c65f581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -282,7 +282,8 @@
 
     public void refreshSignalCluster(SignalCluster cluster) {
         cluster.setWifiIndicators(
-                mWifiConnected, // only show wifi in the cluster if connected
+                // only show wifi in the cluster if connected or if wifi-only
+                mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature),
                 mWifiIconId,
                 mWifiActivityIconId,
                 mContentDescriptionWifi);
@@ -786,7 +787,7 @@
             if (mDataAndWifiStacked) {
                 mWifiIconId = 0;
             } else {
-                mWifiIconId = mWifiEnabled ? WifiIcons.WIFI_SIGNAL_STRENGTH[0][0] : 0;
+                mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0;
             }
             mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index f41d99c..0284644 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -36,7 +36,6 @@
 import android.widget.LinearLayout;
 
 import com.android.systemui.ExpandHelper;
-import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.statusbar.NotificationData;
@@ -62,9 +61,6 @@
     HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
 
     private SwipeHelper mSwipeHelper;
-    private ExpandHelper mExpandHelper;
-
-    private Gefingerpoken mCurrentHelper;
 
     // Flag set during notification removal animation to avoid causing too much work until
     // animation is done
@@ -81,8 +77,6 @@
         
         setOrientation(LinearLayout.VERTICAL);
 
-        setMotionEventSplittingEnabled(false);
-
         if (DEBUG) {
             setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
                 @Override
@@ -101,9 +95,6 @@
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
-        int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
-        int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
-        mExpandHelper = new ExpandHelper(mContext, this, minHeight, maxHeight);
     }
 
     public void setLongPressListener(View.OnLongClickListener listener) {
@@ -135,39 +126,17 @@
         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
         if (DEBUG) logLayoutTransition();
 
-        MotionEvent cancellation = MotionEvent.obtain(ev);
-        cancellation.setAction(MotionEvent.ACTION_CANCEL);
-
-        if (mSwipeHelper.onInterceptTouchEvent(ev)) {
-            if (DEBUG) Log.v(TAG, "will swipe");
-            mCurrentHelper = mSwipeHelper;
-            mExpandHelper.onInterceptTouchEvent(cancellation);
-            return true;
-        } else if (mExpandHelper.onInterceptTouchEvent(ev)) {
-            if (DEBUG) Log.v(TAG, "will stretch");
-            mCurrentHelper = mExpandHelper;
-            mSwipeHelper.onInterceptTouchEvent(cancellation);
-            return true;
-        } else {
-            mCurrentHelper = null;
-            if (super.onInterceptTouchEvent(ev)) {
-                if (DEBUG) Log.v(TAG, "intercepting ourselves");
-                mSwipeHelper.onInterceptTouchEvent(cancellation);
-                mExpandHelper.onInterceptTouchEvent(cancellation);
-                return true;
-            }
-        }
-        return false;
+        return mSwipeHelper.onInterceptTouchEvent(ev) ||
+                super.onInterceptTouchEvent(ev);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (DEBUG) Log.v(TAG, "onTouchEvent()");
         if (DEBUG) logLayoutTransition();
-        if (mCurrentHelper != null) {
-            return mCurrentHelper.onTouchEvent(ev);
-        }
-        return super.onTouchEvent(ev);
+
+        return mSwipeHelper.onTouchEvent(ev) ||
+                super.onTouchEvent(ev);
     }
 
     public boolean canChildBeDismissed(View v) {
@@ -202,6 +171,13 @@
     public View getChildAtPosition(MotionEvent ev) {
         return getChildAtPosition(ev.getX(), ev.getY());
     }
+
+    public View getChildAtRawPosition(float touchX, float touchY) {
+        int[] location = new int[2];
+        getLocationOnScreen(location);
+        return getChildAtPosition((float) (touchX - location[0]), (float) (touchY - location[1]));
+    }
+
     public View getChildAtPosition(float touchX, float touchY) {
         // find the view under the pointer, accounting for GONE views
         final int count = getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index 9ca83e6..46ea940 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -75,6 +75,7 @@
         super.onDetachedFromWindow();
         mAirplane.release();
         mDoNotDisturb.release();
+        mRotate.release();
     }
 
     public void onClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index dba1606..10c5dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -188,6 +188,17 @@
 
     public Context getContext() { return mContext; }
 
+    private View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
+        public boolean onTouch(View v, MotionEvent event) {
+            switch(event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    showSearchPanel();
+                break;
+            }
+            return false;
+        }
+    };
+
     @Override
     protected void createAndAddWindows() {
         addStatusBarWindow();
@@ -290,6 +301,7 @@
 
         // Search Panel
         mStatusBarView.setBar(this);
+        mHomeButton.setOnTouchListener(mHomeSearchActionListener);
         updateSearchPanel();
 
         // Input methods Panel
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/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/FaceUnlock.java
index c46b94a..737ea47 100644
--- a/policy/src/com/android/internal/policy/impl/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/FaceUnlock.java
@@ -300,7 +300,18 @@
      * onServiceConnected() callback is received.
      */
     void handleServiceConnected() {
-        if (DEBUG) Log.d(TAG, "handleServiceConnected()");
+        Log.d(TAG, "handleServiceConnected()");
+
+        // It is possible that an unbind has occurred in the time between the bind and when this
+        // function is reached.  If an unbind has already occurred, proceeding on to call startUi()
+        // can result in a fatal error.  Note that the onServiceConnected() callback is
+        // asynchronous, so this possibility would still exist if we executed this directly in
+        // onServiceConnected() rather than using a handler.
+        if (!mBoundToService) {
+            Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
+            return;
+        }
+
         try {
             mService.registerCallback(mFaceUnlockCallback);
         } catch (RemoteException e) {
@@ -452,25 +463,12 @@
      * Tells the Face Unlock service to start displaying its UI and start processing.
      */
     private void startUi(IBinder windowToken, int x, int y, int w, int h) {
-        Log.d(TAG, "startUi()");
+        if (DEBUG) Log.d(TAG, "startUi()");
         synchronized (mServiceRunningLock) {
             if (!mServiceRunning) {
-                if (DEBUG) Log.d(TAG, "Starting Face Unlock");
+                Log.d(TAG, "Starting Face Unlock");
                 try {
-                    // TODO: these checks and logs are for tracking down bug 6409767 and can be
-                    // removed when that bug is fixed.
-                    if (mService == null) {
-                        Log.d(TAG, "mService is null");
-                    }
-                    if (windowToken == null) {
-                        Log.d(TAG, "windowToken is null");
-                    }
-                    if (mLockPatternUtils == null) {
-                        Log.d(TAG, "mLockPatternUtils is null");
-                    }
-                    Log.d(TAG, "x,y,w,h,live: " + x + "," + y + "," + w + "," + h + ", no");
                     mService.startUi(windowToken, x, y, w, h, false);
-                    Log.d(TAG, "mService.startUi() called");
                 } catch (RemoteException e) {
                     Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
                     return;
@@ -492,7 +490,7 @@
         // screen is turned off.  That's why we check.
         synchronized (mServiceRunningLock) {
             if (mServiceRunning) {
-                if (DEBUG) Log.d(TAG, "Stopping Face Unlock");
+                Log.d(TAG, "Stopping Face Unlock");
                 try {
                     mService.stopUi();
                 } catch (RemoteException e) {
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/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 8ea334e..30cb530 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -32,6 +32,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Vibrator;
@@ -45,7 +46,6 @@
 import android.media.AudioManager;
 import android.os.RemoteException;
 import android.provider.MediaStore;
-import android.provider.Settings;
 
 import java.io.File;
 
@@ -252,13 +252,19 @@
         }
     }
 
+    // This code should be the same as that in SearchPanelView
+    public boolean isAssistantAvailable() {
+        Intent intent = getAssistIntent();
+        return intent == null ? false
+                : mContext.getPackageManager().queryIntentActivities(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+    }
+
     private Intent getAssistIntent() {
         Intent intent = null;
-        if (mSearchManager == null) {
-            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
-        }
-        if (mSearchManager != null) {
-            ComponentName globalSearchActivity = mSearchManager.getGlobalSearchActivity();
+        SearchManager searchManager = getSearchManager();
+        if (searchManager != null) {
+            ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
             if (globalSearchActivity != null) {
                 intent = new Intent(Intent.ACTION_ASSIST);
                 intent.setPackage(globalSearchActivity.getPackageName());
@@ -271,6 +277,13 @@
         return intent;
     }
 
+    private SearchManager getSearchManager() {
+        if (mSearchManager == null) {
+            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+        }
+        return mSearchManager;
+    }
+
     class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener,
             UnlockWidgetCommonMethods {
         private final MultiWaveView mMultiWaveView;
@@ -388,6 +401,10 @@
         public void cleanUp() {
             mMultiWaveView.setOnTriggerListener(null);
         }
+
+        public void onFinishFinalAnimation() {
+
+        }
     }
 
     private void requestUnlockScreen() {
@@ -517,14 +534,12 @@
                         .isTargetPresent(com.android.internal.R.drawable.ic_lockscreen_search)
                         : false;
 
-        // TODO: test to see if search is available
-        boolean searchActionAvailable = true;
-
         if (disabledByAdmin) {
             Log.v(TAG, "Camera disabled by Device Policy");
         } else if (disabledBySimState) {
             Log.v(TAG, "Camera disabled by Sim State");
         }
+        boolean searchActionAvailable = isAssistantAvailable();
         mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent;
         mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
         mUnlockWidgetMethods.updateResources();
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index 06cd69e..203f9db 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -113,10 +113,7 @@
         mPasswordEntry.setOnEditorActionListener(this);
 
         mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false);
-        mKeyboardHelper.setEnableHaptics(
-                Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, 0)
-                        != 0);
+        mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
         boolean imeOrDeleteButtonVisible = false;
         if (mIsAlpha) {
             // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 17e671d..9a6d2cc 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -41,7 +41,7 @@
 class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
         implements KeyguardScreen {
 
-    private static final boolean DEBUG = true; /* TODO: revert before JB release */
+    private static final boolean DEBUG = false;
     private static final String TAG = "UnlockScreen";
 
     // how long before we clear the wrong pattern
@@ -321,7 +321,6 @@
             implements LockPatternView.OnPatternListener {
 
         public void onPatternStart() {
-            if (DEBUG) Log.d(TAG, "Got pattern start");
             mLockPatternView.removeCallbacks(mCancelPatternRunnable);
         }
 
@@ -337,7 +336,6 @@
                 // Give just a little extra time if they hit one of the first few dots
                 mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
             }
-            if (DEBUG) Log.d(TAG, "Got pattern cell");
         }
 
         public void onPatternDetected(List<LockPatternView.Cell> pattern) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index cc7050a..47703c6 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -79,6 +79,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewManager;
+import android.view.ViewParent;
 import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
@@ -582,7 +583,10 @@
             st.decorView.setWindowBackground(getContext().getResources().getDrawable(
                     backgroundResId));
 
-
+            ViewParent shownPanelParent = st.shownPanelView.getParent();
+            if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {
+                ((ViewGroup) shownPanelParent).removeView(st.shownPanelView);
+            }
             st.decorView.addView(st.shownPanelView, lp);
 
             /*
@@ -1626,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/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 3147ba7..29de5c1 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -158,7 +158,7 @@
     static final boolean DEBUG = false;
     static final boolean localLOGV = false;
     static final boolean DEBUG_LAYOUT = false;
-    static final boolean DEBUG_FALLBACK = false;
+    static final boolean DEBUG_INPUT = false;
     static final boolean SHOW_STARTING_ANIMATIONS = true;
     static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
 
@@ -179,7 +179,6 @@
     static final int LONG_PRESS_HOME_NOTHING = 0;
     static final int LONG_PRESS_HOME_RECENT_DIALOG = 1;
     static final int LONG_PRESS_HOME_RECENT_SYSTEM_UI = 2;
-    static final int LONG_PRESS_HOME_VOICE_SEARCH = 3;
 
     // wallpaper is at the bottom, though the window manager may move it.
     static final int WALLPAPER_LAYER = 2;
@@ -322,10 +321,12 @@
 
     static final int RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS = 0;
     static final int RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW = 1;
-    static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 2;
+    static final int RECENT_APPS_BEHAVIOR_DISMISS = 2;
+    static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 3;
 
     RecentApplicationsDialog mRecentAppsDialog;
     int mRecentAppsDialogHeldModifiers;
+    boolean mLanguageSwitchKeyPressed;
 
     int mLidState = LID_ABSENT;
     boolean mHaveBuiltInKeyboard;
@@ -410,6 +411,10 @@
     int mSystemLeft, mSystemTop, mSystemRight, mSystemBottom;
     // For applications requesting stable content insets, these are them.
     int mStableLeft, mStableTop, mStableRight, mStableBottom;
+    // For applications requesting stable content insets but have also set the
+    // fullscreen window flag, these are the stable dimensions without the status bar.
+    int mStableFullscreenLeft, mStableFullscreenTop;
+    int mStableFullscreenRight, mStableFullscreenBottom;
     // During layout, the current screen borders with all outer decoration
     // (status bar, input method dock) accounted for.
     int mCurLeft, mCurTop, mCurRight, mCurBottom;
@@ -500,6 +505,7 @@
 
     ShortcutManager mShortcutManager;
     PowerManager.WakeLock mBroadcastWakeLock;
+    boolean mHavePendingMediaKeyRepeatWithWakeLock;
 
     // Fallback actions by key code.
     private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
@@ -507,6 +513,8 @@
 
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
+    private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
+    private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -518,6 +526,12 @@
                 case MSG_DISABLE_POINTER_LOCATION:
                     disablePointerLocation();
                     break;
+                case MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK:
+                    dispatchMediaKeyWithWakeLock((KeyEvent)msg.obj);
+                    break;
+                case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
+                    dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
+                    break;
             }
         }
     }
@@ -758,9 +772,6 @@
                     mLongPressOnHomeBehavior > LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
                 mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
             }
-            if (hasNavigationBar()) {
-                mLongPressOnHomeBehavior = LONG_PRESS_HOME_VOICE_SEARCH;
-            }
         }
 
         if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
@@ -780,18 +791,6 @@
             } catch (RemoteException e) {
                 Slog.e(TAG, "RemoteException when showing recent apps", e);
             }
-        } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_VOICE_SEARCH) {
-            Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
-            try {
-                intent.setFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                mContext.startActivity(intent);
-            } catch (ActivityNotFoundException e) {
-                Log.e(TAG, "Unable to launch. tag=" + TAG + " intent=" + intent, e);
-            } catch (SecurityException e) {
-                Log.e(TAG, "PhoneWindowManager does not have the permission to launch " +
-                      "tag=" + TAG + " intent=" + intent, e);
-            }
         }
     }
 
@@ -809,6 +808,7 @@
                 if (mRecentAppsDialog.isShowing()) {
                     switch (behavior) {
                         case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:
+                        case RECENT_APPS_BEHAVIOR_DISMISS:
                             mRecentAppsDialog.dismiss();
                             break;
                         case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
@@ -830,6 +830,7 @@
                             }
                             mRecentAppsDialog.show();
                             break;
+                        case RECENT_APPS_BEHAVIOR_DISMISS:
                         case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
                         default:
                             break;
@@ -1701,7 +1702,7 @@
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
 
-        if (false) {
+        if (DEBUG_INPUT) {
             Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
                     + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed
                     + " canceled=" + canceled);
@@ -1849,7 +1850,7 @@
             }
             return 0;
         } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
-            if (down && repeatCount == 0) {
+            if (down && repeatCount == 0 && !keyguardOn) {
                 showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS);
             }
             return -1;
@@ -1886,27 +1887,29 @@
         }
 
         // Invoke shortcuts using Meta.
-        if (down && repeatCount == 0
+        if (down && repeatCount == 0 && !keyguardOn
                 && (metaState & KeyEvent.META_META_ON) != 0) {
             final KeyCharacterMap kcm = event.getKeyCharacterMap();
-            Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
-                    metaState & ~(KeyEvent.META_META_ON
-                            | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
-            if (shortcutIntent != null) {
-                shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                try {
-                    mContext.startActivity(shortcutIntent);
-                } catch (ActivityNotFoundException ex) {
-                    Slog.w(TAG, "Dropping shortcut key combination because "
-                            + "the activity to which it is registered was not found: "
-                            + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
+            if (kcm.isPrintingKey(keyCode)) {
+                Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
+                        metaState & ~(KeyEvent.META_META_ON
+                                | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
+                if (shortcutIntent != null) {
+                    shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        mContext.startActivity(shortcutIntent);
+                    } catch (ActivityNotFoundException ex) {
+                        Slog.w(TAG, "Dropping shortcut key combination because "
+                                + "the activity to which it is registered was not found: "
+                                + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
+                    }
+                    return -1;
                 }
-                return -1;
             }
         }
 
         // Handle application launch keys.
-        if (down && repeatCount == 0) {
+        if (down && repeatCount == 0 && !keyguardOn) {
             String category = sApplicationLaunchKeyCategories.get(keyCode);
             if (category != null) {
                 Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
@@ -1924,7 +1927,7 @@
 
         // Display task switcher for ALT-TAB or Meta-TAB.
         if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
-            if (mRecentAppsDialogHeldModifiers == 0) {
+            if (mRecentAppsDialogHeldModifiers == 0 && !keyguardOn) {
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
                 if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
                         || KeyEvent.metaStateHasModifiers(
@@ -1937,7 +1940,24 @@
         } else if (!down && mRecentAppsDialogHeldModifiers != 0
                 && (metaState & mRecentAppsDialogHeldModifiers) == 0) {
             mRecentAppsDialogHeldModifiers = 0;
-            showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH);
+            showOrHideRecentAppsDialog(keyguardOn ? RECENT_APPS_BEHAVIOR_DISMISS :
+                    RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH);
+        }
+
+        // Handle keyboard language switching.
+        if (down && repeatCount == 0
+                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
+                        || (keyCode == KeyEvent.KEYCODE_SPACE
+                                && (metaState & KeyEvent.META_CTRL_MASK) != 0))) {
+            int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+            mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+            return -1;
+        }
+        if (mLanguageSwitchKeyPressed && !down
+                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
+                        || keyCode == KeyEvent.KEYCODE_SPACE)) {
+            mLanguageSwitchKeyPressed = false;
+            return -1;
         }
 
         // Let the application handle the key.
@@ -1948,7 +1968,7 @@
     @Override
     public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
         // Note: This method is only called if the initial down was unhandled.
-        if (DEBUG_FALLBACK) {
+        if (DEBUG_INPUT) {
             Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
                     + ", flags=" + event.getFlags()
                     + ", keyCode=" + event.getKeyCode()
@@ -1975,7 +1995,7 @@
             }
 
             if (fallbackAction != null) {
-                if (DEBUG_FALLBACK) {
+                if (DEBUG_INPUT) {
                     Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
                             + " metaState=" + Integer.toHexString(fallbackAction.metaState));
                 }
@@ -2002,7 +2022,7 @@
             }
         }
 
-        if (DEBUG_FALLBACK) {
+        if (DEBUG_INPUT) {
             if (fallbackEvent == null) {
                 Slog.d(TAG, "No fallback.");
             } else {
@@ -2153,22 +2173,31 @@
 
     public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
         final int fl = attrs.flags;
+        final int systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility);
 
-        if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
+        if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
                 == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
             int availRight, availBottom;
             if (mCanHideNavigationBar &&
-                    (attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+                    (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
                 availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
                 availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
             } else {
                 availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
                 availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
             }
-            if ((attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-                contentInset.set(mStableLeft, mStableTop,
-                        availRight - mStableRight, availBottom - mStableBottom);
-            } else if ((attrs.systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
+            if ((systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+                if ((fl & FLAG_FULLSCREEN) != 0) {
+                    contentInset.set(mStableFullscreenLeft, mStableFullscreenTop,
+                            availRight - mStableFullscreenRight,
+                            availBottom - mStableFullscreenBottom);
+                } else {
+                    contentInset.set(mStableLeft, mStableTop,
+                            availRight - mStableRight, availBottom - mStableBottom);
+                }
+            } else if ((fl & FLAG_FULLSCREEN) != 0) {
+                contentInset.setEmpty();
+            } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
                 contentInset.set(mCurLeft, mCurTop,
                         availRight - mCurRight, availBottom - mCurBottom);
@@ -2189,10 +2218,14 @@
         mRestrictedScreenLeft = mRestrictedScreenTop = 0;
         mRestrictedScreenWidth = displayWidth;
         mRestrictedScreenHeight = displayHeight;
-        mDockLeft = mContentLeft = mStableLeft = mSystemLeft = mCurLeft = 0;
-        mDockTop = mContentTop = mStableTop = mSystemTop = mCurTop = 0;
-        mDockRight = mContentRight = mStableRight = mSystemRight = mCurRight = displayWidth;
-        mDockBottom = mContentBottom = mStableBottom = mSystemBottom = mCurBottom = displayHeight;
+        mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft
+                = mSystemLeft = mCurLeft = 0;
+        mDockTop = mContentTop = mStableTop = mStableFullscreenTop
+                = mSystemTop = mCurTop = 0;
+        mDockRight = mContentRight = mStableRight = mStableFullscreenRight
+                = mSystemRight = mCurRight = displayWidth;
+        mDockBottom = mContentBottom = mStableBottom = mStableFullscreenBottom
+                = mSystemBottom = mCurBottom = displayHeight;
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
@@ -2245,7 +2278,7 @@
                     }
                 }
                 mTmpNavigationFrame.set(0, top, displayWidth, displayHeight);
-                mStableBottom = mTmpNavigationFrame.top;
+                mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
                 if (navVisible) {
                     mNavigationBar.showLw(true);
                     mDockBottom = mTmpNavigationFrame.top;
@@ -2269,7 +2302,7 @@
                     }
                 }
                 mTmpNavigationFrame.set(left, 0, displayWidth, displayHeight);
-                mStableRight = mTmpNavigationFrame.left;
+                mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
                 if (navVisible) {
                     mNavigationBar.showLw(true);
                     mDockRight = mTmpNavigationFrame.left;
@@ -2407,7 +2440,25 @@
         pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0
                 ? attached.getFrameLw() : df);
     }
-    
+
+    private void applyStableConstraints(int sysui, int fl, Rect r) {
+        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+            // If app is requesting a stable layout, don't let the
+            // content insets go below the stable values.
+            if ((fl & FLAG_FULLSCREEN) != 0) {
+                if (r.left < mStableFullscreenLeft) r.left = mStableFullscreenLeft;
+                if (r.top < mStableFullscreenTop) r.top = mStableFullscreenTop;
+                if (r.right > mStableFullscreenRight) r.right = mStableFullscreenRight;
+                if (r.bottom > mStableFullscreenBottom) r.bottom = mStableFullscreenBottom;
+            } else {
+                if (r.left < mStableLeft) r.left = mStableLeft;
+                if (r.top < mStableTop) r.top = mStableTop;
+                if (r.right > mStableRight) r.right = mStableRight;
+                if (r.bottom > mStableBottom) r.bottom = mStableBottom;
+            }
+        }
+    }
+
     /** {@inheritDoc} */
     public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs,
             WindowState attached) {
@@ -2514,14 +2565,7 @@
                         cf.right = mContentRight;
                         cf.bottom = mContentBottom;
                     }
-                    if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-                        // If app is requesting a stable layout, don't let the
-                        // content insets go below the stable values.
-                        if (cf.left < mStableLeft) cf.left = mStableLeft;
-                        if (cf.top < mStableTop) cf.top = mStableTop;
-                        if (cf.right > mStableRight) cf.right = mStableRight;
-                        if (cf.bottom > mStableBottom) cf.bottom = mStableBottom;
-                    }
+                    applyStableConstraints(sysUiFl, fl, cf);
                     if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                         vf.left = mCurLeft;
                         vf.top = mCurTop;
@@ -2603,14 +2647,7 @@
                     pf.bottom = df.bottom = cf.bottom
                             = mRestrictedScreenTop+mRestrictedScreenHeight;
                 }
-                if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-                    // If app is requesting a stable layout, don't let the
-                    // content insets go below the stable values.
-                    if (cf.left < mStableLeft) cf.left = mStableLeft;
-                    if (cf.top < mStableTop) cf.top = mStableTop;
-                    if (cf.right > mStableRight) cf.right = mStableRight;
-                    if (cf.bottom > mStableBottom) cf.bottom = mStableBottom;
-                }
+                applyStableConstraints(sysUiFl, fl, cf);
                 if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
                     vf.left = mCurLeft;
                     vf.top = mCurTop;
@@ -3078,7 +3115,7 @@
             return 0;
         }
 
-        if (false) {
+        if (DEBUG_INPUT) {
             Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                   + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
         }
@@ -3300,8 +3337,13 @@
                     // Only do this if we would otherwise not pass it to the user. In that
                     // case, the PhoneWindow class will do the same thing, except it will
                     // only do it if the showing app doesn't process the key on its own.
+                    // Note that we need to make a copy of the key event here because the
+                    // original key event will be recycled when we return.
                     mBroadcastWakeLock.acquire();
-                    mHandler.post(new PassHeadsetKey(new KeyEvent(event)));
+                    Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,
+                            new KeyEvent(event));
+                    msg.setAsynchronous(true);
+                    msg.sendToTarget();
                 }
                 break;
             }
@@ -3350,24 +3392,58 @@
         return result;
     }
 
-    class PassHeadsetKey implements Runnable {
-        KeyEvent mKeyEvent;
-
-        PassHeadsetKey(KeyEvent keyEvent) {
-            mKeyEvent = keyEvent;
+    void dispatchMediaKeyWithWakeLock(KeyEvent event) {
+        if (DEBUG_INPUT) {
+            Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
         }
 
-        public void run() {
-            if (ActivityManagerNative.isSystemReady()) {
-                IAudioService audioService = getAudioService();
-                if (audioService != null) {
-                    try {
-                        audioService.dispatchMediaKeyEventUnderWakelock(mKeyEvent);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
-                    }
+        if (mHavePendingMediaKeyRepeatWithWakeLock) {
+            if (DEBUG_INPUT) {
+                Slog.d(TAG, "dispatchMediaKeyWithWakeLock: canceled repeat");
+            }
+
+            mHandler.removeMessages(MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK);
+            mHavePendingMediaKeyRepeatWithWakeLock = false;
+            mBroadcastWakeLock.release(); // pending repeat was holding onto the wake lock
+        }
+
+        dispatchMediaKeyWithWakeLockToAudioService(event);
+
+        if (event.getAction() == KeyEvent.ACTION_DOWN
+                && event.getRepeatCount() == 0) {
+            mHavePendingMediaKeyRepeatWithWakeLock = true;
+
+            Message msg = mHandler.obtainMessage(
+                    MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK, event);
+            msg.setAsynchronous(true);
+            mHandler.sendMessageDelayed(msg, ViewConfiguration.getKeyRepeatTimeout());
+        } else {
+            mBroadcastWakeLock.release();
+        }
+    }
+
+    void dispatchMediaKeyRepeatWithWakeLock(KeyEvent event) {
+        mHavePendingMediaKeyRepeatWithWakeLock = false;
+
+        KeyEvent repeatEvent = KeyEvent.changeTimeRepeat(event,
+                SystemClock.uptimeMillis(), 1, event.getFlags() | KeyEvent.FLAG_LONG_PRESS);
+        if (DEBUG_INPUT) {
+            Slog.d(TAG, "dispatchMediaKeyRepeatWithWakeLock: " + repeatEvent);
+        }
+
+        dispatchMediaKeyWithWakeLockToAudioService(repeatEvent);
+        mBroadcastWakeLock.release();
+    }
+
+    void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
+        if (ActivityManagerNative.isSystemReady()) {
+            IAudioService audioService = getAudioService();
+            if (audioService != null) {
+                try {
+                    audioService.dispatchMediaKeyEventUnderWakelock(event);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
                 }
-                mBroadcastWakeLock.release();
             }
         }
     }
@@ -4258,6 +4334,10 @@
                 pw.print(","); pw.print(mRestrictedScreenTop);
                 pw.print(") "); pw.print(mRestrictedScreenWidth);
                 pw.print("x"); pw.println(mRestrictedScreenHeight);
+        pw.print(prefix); pw.print("mStableFullscreen=("); pw.print(mStableFullscreenLeft);
+                pw.print(","); pw.print(mStableFullscreenTop);
+                pw.print(")-("); pw.print(mStableFullscreenRight);
+                pw.print(","); pw.print(mStableFullscreenBottom); pw.println(")");
         pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft);
                 pw.print(","); pw.print(mStableTop);
                 pw.print(")-("); pw.print(mStableRight);
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 50bfee6..f80ac18 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "EventHub"
 
-#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 
 #include "EventHub.h"
 
@@ -767,7 +767,11 @@
                     size_t count = size_t(readSize) / sizeof(struct input_event);
                     for (size_t i = 0; i < count; i++) {
                         const struct input_event& iev = readBuffer[i];
-                        nsecs_t delta = 0; 
+                        ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+                                device->path.string(),
+                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
+                                iev.type, iev.code, iev.value);
+
 #ifdef HAVE_POSIX_CLOCKS
                         // Use the time specified in the event instead of the current time
                         // so that downstream code can get more accurate estimates of
@@ -782,23 +786,10 @@
                         // system call that also queries ktime_get_ts().
                         event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                 + nsecs_t(iev.time.tv_usec) * 1000LL;
-                        delta = now - event->when;
-
-                        // Only log verbose if events are older that 1ms
-                        if (delta > 1 * 1000000LL) {
-                            ALOGV("event time %lld, now %lld, delta %lldus", event->when, now, delta / 1000LL);
-                        }
+                        ALOGV("event time %lld, now %lld", event->when, now);
 #else
                         event->when = now;
 #endif
-                        if (delta > 1 * 1000000LL) {
-                            ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
-                                  device->path.string(),
-                                  (int) iev.time.tv_sec, (int) iev.time.tv_usec,
-                                  iev.type, iev.code, iev.value);
-                        }
-
-
                         event->deviceId = deviceId;
                         event->type = iev.type;
                         event->code = iev.code;
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 23f2fdd..df2e1aa 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.util.Xml;
+import android.view.WindowManager;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetHost;
@@ -171,6 +172,7 @@
     boolean mSafeMode;
     int mUserId;
     boolean mStateLoaded;
+    int mMaxWidgetBitmapMemory;
 
     // These are for debugging only -- widgets are going missing in some rare instances
     ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
@@ -181,6 +183,14 @@
         mPm = AppGlobals.getPackageManager();
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mUserId = userId;
+        computeMaximumWidgetBitmapMemory();
+    }
+
+    void computeMaximumWidgetBitmapMemory() {
+        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        int height = wm.getDefaultDisplay().getRawHeight();
+        int width = wm.getDefaultDisplay().getRawWidth();
+        mMaxWidgetBitmapMemory = 4 * width * height;
     }
 
     public void systemReady(boolean safeMode) {
@@ -806,6 +816,15 @@
         if (appWidgetIds == null) {
             return;
         }
+
+        int bitmapMemoryUsage = views.estimateMemoryUsage();
+        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
+            throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
+                    " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
+                    mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
+                    " fill the device's screen once.");
+        }
+
         if (appWidgetIds.length == 0) {
             return;
         }
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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a49ccf7..48219a4 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -139,6 +139,7 @@
 
     static final int MSG_UNBIND_METHOD = 3000;
     static final int MSG_BIND_METHOD = 3010;
+    static final int MSG_SET_ACTIVE = 3020;
 
     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
@@ -195,6 +196,7 @@
     private PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
     private boolean mNotificationShown;
+    private final boolean mImeSelectedOnBoot;
 
     class SessionState {
         final ClientState client;
@@ -412,13 +414,9 @@
             }
 
             // Inform the current client of the change in active status
-            try {
-                if (mCurClient != null && mCurClient.client != null) {
-                    mCurClient.client.setActive(mScreenOn);
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid "
-                        + mCurClient.pid + " uid " + mCurClient.uid);
+            if (mCurClient != null && mCurClient.client != null) {
+                executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+                        MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient));
             }
         }
     }
@@ -590,7 +588,6 @@
         mImeSwitcherNotification.vibrate = null;
         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-        mLastSystemLocale = mRes.getConfiguration().locale;
 
         mShowOngoingImeSwitcherForPhones = false;
 
@@ -612,11 +609,17 @@
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
                 mRes, context.getContentResolver(), mMethodMap, mMethodList);
+
+        // Just checking if defaultImiId is empty or not
+        final String defaultImiId = Settings.Secure.getString(
+                mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+        mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
+
         buildInputMethodListLocked(mMethodList, mMethodMap);
         mSettings.enableAllIMEsIfThereIsNoEnabledIME();
 
-        if (TextUtils.isEmpty(Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD))) {
+        if (!mImeSelectedOnBoot) {
+            Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
             resetDefaultImeLocked(context);
         }
 
@@ -639,6 +642,10 @@
     }
 
     private void checkCurrentLocaleChangedLocked() {
+        if (!mSystemReady) {
+            // not system ready
+            return;
+        }
         final Locale newLocale = mRes.getConfiguration().locale;
         if (newLocale != null && !newLocale.equals(mLastSystemLocale)) {
             if (DEBUG) {
@@ -647,6 +654,7 @@
             buildInputMethodListLocked(mMethodList, mMethodMap);
             // Reset the current ime to the proper one
             resetDefaultImeLocked(mContext);
+            updateFromSettingsLocked();
             mLastSystemLocale = newLocale;
         }
     }
@@ -675,7 +683,10 @@
         }
     }
 
-    private static boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+    private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+        if (!mSystemReady) {
+            return false;
+        }
         if (!isSystemIme(imi)) {
             return false;
         }
@@ -738,7 +749,6 @@
                         mContext.getSystemService(Context.KEYGUARD_SERVICE);
                 mNotificationManager = (NotificationManager)
                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-                mLastSystemLocale = mContext.getResources().getConfiguration().locale;
                 mStatusBar = statusBar;
                 statusBar.setIconVisibility("ime", false);
                 updateImeWindowStatusLocked();
@@ -748,6 +758,12 @@
                     mWindowManagerService.setOnHardKeyboardStatusChangeListener(
                             mHardKeyboardListener);
                 }
+                buildInputMethodListLocked(mMethodList, mMethodMap);
+                if (!mImeSelectedOnBoot) {
+                    Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
+                    checkCurrentLocaleChangedLocked();
+                }
+                mLastSystemLocale = mRes.getConfiguration().locale;
                 try {
                     startInputInnerLocked();
                 } catch (RuntimeException e) {
@@ -863,17 +879,12 @@
                             MSG_UNBIND_INPUT, mCurMethod));
                 }
             }
+
+            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+                    MSG_SET_ACTIVE, 0, mCurClient));
             executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
                     MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
             mCurClient.sessionRequested = false;
-
-            // Call setActive(false) on the old client
-            try {
-                mCurClient.client.setActive(false);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
-                        + mCurClient.pid + " uid " + mCurClient.uid);
-            }
             mCurClient = null;
 
             hideInputMethodMenuLocked();
@@ -968,12 +979,8 @@
 
             // If the screen is on, inform the new client it is active
             if (mScreenOn) {
-                try {
-                    cs.client.setActive(mScreenOn);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Got RemoteException sending setActive notification to pid "
-                            + cs.pid + " uid " + cs.uid);
-                }
+                executeOrSendMessage(cs.client, mCaller.obtainMessageIO(
+                        MSG_SET_ACTIVE, mScreenOn ? 1 : 0, cs));
             }
         }
 
@@ -1421,34 +1428,41 @@
             throw new IllegalArgumentException("Unknown id: " + id);
         }
 
+        // See if we need to notify a subtype change within the same IME.
         if (id.equals(mCurMethodId)) {
-            InputMethodSubtype subtype = null;
-            if (subtypeId >= 0 && subtypeId < info.getSubtypeCount()) {
-                subtype = info.getSubtypeAt(subtypeId);
+            final int subtypeCount = info.getSubtypeCount();
+            if (subtypeCount <= 0) {
+                return;
             }
-            if (subtype != mCurrentSubtype) {
-                synchronized (mMethodMap) {
-                    if (subtype != null) {
-                        setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
-                    }
-                    if (mCurMethod != null) {
-                        try {
-                            refreshImeWindowVisibilityLocked();
-                            // If subtype is null, try to find the most applicable one from
-                            // getCurrentInputMethodSubtype.
-                            if (subtype == null) {
-                                subtype = getCurrentInputMethodSubtype();
-                            }
-                            mCurMethod.changeInputMethodSubtype(subtype);
-                        } catch (RemoteException e) {
-                            return;
-                        }
+            final InputMethodSubtype oldSubtype = mCurrentSubtype;
+            final InputMethodSubtype newSubtype;
+            if (subtypeId >= 0 && subtypeId < subtypeCount) {
+                newSubtype = info.getSubtypeAt(subtypeId);
+            } else {
+                // If subtype is null, try to find the most applicable one from
+                // getCurrentInputMethodSubtype.
+                newSubtype = getCurrentInputMethodSubtype();
+            }
+            if (newSubtype == null || oldSubtype == null) {
+                Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
+                        + ", new subtype = " + newSubtype);
+                return;
+            }
+            if (newSubtype != oldSubtype) {
+                setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
+                if (mCurMethod != null) {
+                    try {
+                        refreshImeWindowVisibilityLocked();
+                        mCurMethod.changeInputMethodSubtype(newSubtype);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to call changeInputMethodSubtype");
                     }
                 }
             }
             return;
         }
 
+        // Changing to a different IME.
         final long ident = Binder.clearCallingIdentity();
         try {
             // Set a subtype to this input method.
@@ -1641,7 +1655,8 @@
                 }
 
                 if (mCurFocusedWindow == windowToken) {
-                    Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client);
+                    Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
+                            + " attribute=" + attribute);
                     if (attribute != null) {
                         return startInputUncheckedLocked(cs, inputContext, attribute,
                                 controlFlags);
@@ -2113,6 +2128,15 @@
                     Slog.w(TAG, "Client died receiving input method " + args.arg2);
                 }
                 return true;
+            case MSG_SET_ACTIVE:
+                try {
+                    ((ClientState)msg.obj).client.setActive(msg.arg1 != 0);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
+                            + ((ClientState)msg.obj).pid + " uid "
+                            + ((ClientState)msg.obj).uid);
+                }
+                return true;
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
@@ -2137,7 +2161,6 @@
         return subtypes;
     }
 
-
     private static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
             InputMethodInfo imi, String mode) {
         ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
@@ -2155,15 +2178,19 @@
         List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
         if (enabled != null && enabled.size() > 0) {
             // We'd prefer to fall back on a system IME, since that is safer.
-            int i=enabled.size();
+            int i = enabled.size();
+            int firstFoundSystemIme = -1;
             while (i > 0) {
                 i--;
                 final InputMethodInfo imi = enabled.get(i);
-                if (isSystemIme(imi) && !imi.isAuxiliaryIme()) {
-                    break;
+                if (isSystemImeThatHasEnglishSubtype(imi) && !imi.isAuxiliaryIme()) {
+                    return imi;
+                }
+                if (firstFoundSystemIme < 0 && isSystemIme(imi) && !imi.isAuxiliaryIme()) {
+                    firstFoundSystemIme = i;
                 }
             }
-            return enabled.get(i);
+            return enabled.get(Math.max(firstFoundSystemIme, 0));
         }
         return null;
     }
@@ -2238,11 +2265,17 @@
             }
         }
 
-        String defaultIme = Settings.Secure.getString(mContext
+        final String defaultImiId = Settings.Secure.getString(mContext
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
-        if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) {
-            if (chooseNewDefaultIMELocked()) {
-                updateFromSettingsLocked();
+        if (!TextUtils.isEmpty(defaultImiId)) {
+            if (!map.containsKey(defaultImiId)) {
+                Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
+                if (chooseNewDefaultIMELocked()) {
+                    updateFromSettingsLocked();
+                }
+            } else {
+                // Double check that the default IME is certainly enabled.
+                setInputMethodEnabledLocked(defaultImiId, true);
             }
         }
     }
@@ -2626,7 +2659,8 @@
                 mCurrentSubtype = subtype;
             } else {
                 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-                mCurrentSubtype = null;
+                // If the subtype is not specified, choose the most applicable one
+                mCurrentSubtype = getCurrentInputMethodSubtype();
             }
         }
 
@@ -3007,8 +3041,8 @@
             mContext = context;
             mPm = context.getPackageManager();
             mImms = imms;
-            mSystemLocaleStr =
-                    imms.mLastSystemLocale != null ? imms.mLastSystemLocale.toString() : "";
+            final Locale locale = context.getResources().getConfiguration().locale;
+            mSystemLocaleStr = locale != null ? locale.toString() : "";
         }
 
         private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
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/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 6a6c585..a15d3bbb 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -148,6 +148,7 @@
                                 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                         event.getCode(), event.getRawEvent()));
                             } else {
+                                log("POST<- {" + rawEvent + "}");
                                 mResponseQueue.add(event.getCmdNumber(), event);
                             }
                         } catch (IllegalArgumentException e) {
@@ -327,6 +328,7 @@
                 loge("timed-out waiting for response to " + logCmd);
                 throw new NativeDaemonFailureException(logCmd, event);
             }
+            log("RMV <- {" + event + "}");
             events.add(event);
         } while (event.isClassContinue());
 
@@ -337,6 +339,7 @@
             throw new NativeDaemonFailureException(logCmd, event);
         }
 
+        log("RTN <- {" + logCmd + "}");
         return events.toArray(new NativeDaemonEvent[events.size()]);
     }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 4536a6d..e2852b5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -166,7 +166,7 @@
         }
 
         mConnector = new NativeDaemonConnector(
-                new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 50);
+                new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 80);
         mThread = new Thread(mConnector, NETD_TAG);
 
         // Add ourself to the Watchdog monitors.
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 8429086..78c0c12 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -489,7 +489,8 @@
         synchronized (mNotifications) {
             final StatusBarNotification n = mNotifications.remove(key);
             if (n == null) {
-                throw new IllegalArgumentException("removeNotification key not found: " + key);
+                Slog.e(TAG, "removeNotification key not found: " + key);
+                return;
             }
             if (mBar != null) {
                 try {
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 bb38cd9..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()}
@@ -562,6 +578,8 @@
      */
     public synchronized boolean setWifiEnabled(boolean enable) {
         enforceChangePermission();
+        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
         if (DBG) {
             Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
         }
@@ -576,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/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 889fbe4..3e35b20d 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,10 +16,10 @@
 
 package com.android.server.accessibility;
 
-import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.input.InputFilter;
 
 import android.content.Context;
+import android.os.PowerManager;
 import android.util.Slog;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -37,7 +37,9 @@
 
     private final Context mContext;
 
-    private final GestureListener mGestureListener;
+    private final PowerManager mPm;
+
+    private final AccessibilityManagerService mAms;
 
     /**
      * This is an interface for explorers that take a {@link MotionEvent}
@@ -70,10 +72,11 @@
 
     private int mTouchscreenSourceDeviceId;
 
-    public AccessibilityInputFilter(Context context, GestureListener gestureListener) {
+    public AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
         super(context.getMainLooper());
         mContext = context;
-        mGestureListener = gestureListener;
+        mAms = service;
+        mPm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
     }
 
     @Override
@@ -81,7 +84,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Accessibility input filter installed.");
         }
-        mTouchExplorer = new TouchExplorer(this, mContext, mGestureListener);
+        mTouchExplorer = new TouchExplorer(this, mContext, mAms);
         super.onInstalled();
     }
 
@@ -108,6 +111,7 @@
                 mTouchExplorer.clear(motionEvent, policyFlags);
             }
             if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
+                mPm.userActivity(event.getEventTime(), false);
                 mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
             } else {
                 mTouchExplorer.clear(motionEvent, policyFlags);
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 28ce1df..5e2f494 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -24,16 +24,21 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.app.AlertDialog;
 import android.app.PendingIntent;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
@@ -56,7 +61,9 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -64,8 +71,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
-import com.android.server.accessibility.TouchExplorer.GestureListener;
 import com.android.server.wm.WindowManagerService;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -88,8 +95,7 @@
  *
  * @hide
  */
-public class AccessibilityManagerService extends IAccessibilityManager.Stub
-        implements GestureListener {
+public class AccessibilityManagerService extends IAccessibilityManager.Stub {
 
     private static final boolean DEBUG = false;
 
@@ -100,6 +106,8 @@
 
     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
 
+    private static final int MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG = 1;
+
     private static int sIdCounter = 0;
 
     private static int sNextWindowId;
@@ -126,6 +134,8 @@
 
     private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
 
+    private final Rect mTempRect = new Rect();
+
     private PackageManager mPackageManager;
 
     private int mHandledFeedbackTypes = 0;
@@ -144,23 +154,15 @@
 
     private final SecurityPolicy mSecurityPolicy;
 
+    private final MainHanler mMainHandler;
+
     private Service mUiAutomationService;
 
-    /**
-     * Handler for delayed event dispatch.
-     */
-    private Handler mHandler = new Handler() {
+    private Service mQueryBridge;
 
-        @Override
-        public void handleMessage(Message message) {
-            Service service = (Service) message.obj;
-            int eventType = message.arg1;
+    private boolean mTouchExplorationGestureEnded;
 
-            synchronized (mLock) {
-                notifyAccessibilityEventLocked(service, eventType);
-            }
-        }
-    };
+    private boolean mTouchExplorationGestureStarted;
 
     /**
      * Creates a new instance.
@@ -173,7 +175,7 @@
         mWindowManagerService = (WindowManagerService) ServiceManager.getService(
                 Context.WINDOW_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
-
+        mMainHandler = new MainHanler();
         registerPackageChangeAndBootCompletedBroadcastReceiver();
         registerSettingsContentObservers();
     }
@@ -347,15 +349,37 @@
     }
 
     public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+        // The event for gesture start should be strictly before the
+        // first hover enter event for the gesture.
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+                && mTouchExplorationGestureStarted) {
+            mTouchExplorationGestureStarted = false;
+            AccessibilityEvent gestureStartEvent = AccessibilityEvent.obtain(
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+            sendAccessibilityEvent(gestureStartEvent);
+        }
+
         synchronized (mLock) {
             if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
-                mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
+                mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event);
                 notifyAccessibilityServicesDelayedLocked(event, false);
                 notifyAccessibilityServicesDelayedLocked(event, true);
             }
         }
+
+        // The event for gesture end should be strictly after the
+        // last hover exit event for the gesture.
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+                && mTouchExplorationGestureEnded) {
+            mTouchExplorationGestureEnded = false;
+            AccessibilityEvent gestureEndEvent = AccessibilityEvent.obtain(
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
+            sendAccessibilityEvent(gestureEndEvent);
+        }
+
         event.recycle();
         mHandledFeedbackTypes = 0;
+
         return (OWN_PROCESS_ID != Binder.getCallingPid());
     }
 
@@ -470,8 +494,7 @@
         }
     }
 
-    @Override
-    public boolean onGesture(int gestureId) {
+    boolean onGesture(int gestureId) {
         synchronized (mLock) {
             boolean handled = notifyGestureLocked(gestureId, false);
             if (!handled) {
@@ -481,6 +504,65 @@
         }
     }
 
+    /**
+     * Gets the bounds of the accessibility focus if the provided,
+     * point coordinates are within the currently active window
+     * and accessibility focus is found within the latter.
+     *
+     * @param x X coordinate.
+     * @param y Y coordinate
+     * @param outBounds The output to which to write the focus bounds.
+     * @return The accessibility focus rectangle if the point is in the
+     *     window and the window has accessibility focus.
+     */
+    boolean getAccessibilityFocusBounds(int x, int y, Rect outBounds) {
+        // Instead of keeping track of accessibility focus events per
+        // window to be able to find the focus in the active window,
+        // we take a stateless approach and look it up. This is fine
+        // since we do this only when the user clicks/long presses.
+        Service service = getQueryBridge();
+        final int connectionId = service.mId;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        client.addConnection(connectionId, service);
+        try {
+            AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
+                    .getRootInActiveWindow(connectionId);
+            if (root == null) {
+                return false;
+            }
+            Rect bounds = mTempRect;
+            root.getBoundsInScreen(bounds);
+            if (!bounds.contains(x, y)) {
+                return false;
+            }
+            AccessibilityNodeInfo focus = root.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
+            if (focus == null) {
+                return false;
+            }
+            focus.getBoundsInScreen(outBounds);
+            return true;
+        } finally {
+            client.removeConnection(connectionId);
+        }
+    }
+
+    private Service getQueryBridge() {
+        if (mQueryBridge == null) {
+            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+            mQueryBridge = new Service(null, info, true);
+        }
+        return mQueryBridge;
+    }
+
+    public void touchExplorationGestureEnded() {
+        mTouchExplorationGestureEnded = true;
+    }
+
+    public void touchExplorationGestureStarted() {
+        mTouchExplorationGestureStarted = true;
+    }
+
     private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
         // TODO: Now we are giving the gestures to the last enabled
         //       service that can handle them which is the last one
@@ -494,12 +576,7 @@
         for (int i = mServices.size() - 1; i >= 0; i--) {
             Service service = mServices.get(i);
             if (service.mReqeustTouchExplorationMode && service.mIsDefault == isDefault) {
-                try {
-                    service.mServiceInterface.onGesture(gestureId);
-                } catch (RemoteException re) {
-                    Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
-                            + " to " + service.mService, re);
-                }
+                service.notifyGesture(gestureId);
                 return true;
             }
         }
@@ -531,6 +608,18 @@
 
         for (int i = 0, count = installedServices.size(); i < count; i++) {
             ResolveInfo resolveInfo = installedServices.get(i);
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            // For now we are enforcing this if the target version is JellyBean or
+            // higher and in a later release we will enforce this for everyone.
+            if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN
+                    && !android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
+                    serviceInfo.permission)) {
+                Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
+                        serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+                        + ": it does not require the permission "
+                        + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
+                continue;
+            }
             AccessibilityServiceInfo accessibilityServiceInfo;
             try {
                 accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
@@ -559,7 +648,7 @@
                 if (service.mIsDefault == isDefault) {
                     if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
                         mHandledFeedbackTypes |= service.mFeedbackType;
-                        notifyAccessibilityServiceDelayedLocked(service, event);
+                        service.notifyAccessibilityEvent(event);
                     }
                 }
             }
@@ -572,90 +661,6 @@
     }
 
     /**
-     * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
-     * and denotes the period after the last event before notifying the service.
-     *
-     * @param service The service.
-     * @param event The event.
-     */
-    private void notifyAccessibilityServiceDelayedLocked(Service service,
-            AccessibilityEvent event) {
-        synchronized (mLock) {
-            final int eventType = event.getEventType();
-            // Make a copy since during dispatch it is possible the event to
-            // be modified to remove its source if the receiving service does
-            // not have permission to access the window content.
-            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
-            AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
-            service.mPendingEvents.put(eventType, newEvent);
-
-            final int what = eventType | (service.mId << 16);
-            if (oldEvent != null) {
-                mHandler.removeMessages(what);
-                oldEvent.recycle();
-            }
-
-            Message message = mHandler.obtainMessage(what, service);
-            message.arg1 = eventType;
-            mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
-        }
-    }
-
-    /**
-     * Notifies an accessibility service client for a scheduled event given the event type.
-     *
-     * @param service The service client.
-     * @param eventType The type of the event to dispatch.
-     */
-    private void notifyAccessibilityEventLocked(Service service, int eventType) {
-        IAccessibilityServiceClient listener = service.mServiceInterface;
-
-        // If the service died/was disabled while the message for dispatching
-        // the accessibility event was propagating the listener may be null.
-        if (listener == null) {
-            return;
-        }
-
-        AccessibilityEvent event = service.mPendingEvents.get(eventType);
-
-        // Check for null here because there is a concurrent scenario in which this
-        // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
-        // which posts a message for dispatching an event. 2) The message is pulled
-        // from the queue by the handler on the service thread and the latter is
-        // just about to acquire the lock and call this method. 3) Now another binder
-        // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
-        // so the service thread waits for the lock; 4) The binder thread replaces
-        // the event with a more recent one (assume the same event type) and posts a
-        // dispatch request releasing the lock. 5) Now the main thread is unblocked and
-        // dispatches the event which is removed from the pending ones. 6) And ... now
-        // the service thread handles the last message posted by the last binder call
-        // but the event is already dispatched and hence looking it up in the pending
-        // ones yields null. This check is much simpler that keeping count for each
-        // event type of each service to catch such a scenario since only one message
-        // is processed at a time.
-        if (event == null) {
-            return;
-        }
-
-        service.mPendingEvents.remove(eventType);
-        try {
-            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
-                event.setConnectionId(service.mId);
-            } else {
-                event.setSource(null);
-            }
-            event.setSealed(true);
-            listener.onAccessibilityEvent(event);
-            event.recycle();
-            if (DEBUG) {
-                Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
-            }
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
-        }
-    }
-
-    /**
      * Adds a service.
      *
      * @param service The service to add.
@@ -669,6 +674,7 @@
             mServices.add(service);
             mComponentNameToServiceMap.put(service.mComponentName, service);
             updateInputFilterLocked();
+            tryEnableTouchExploration(service);
         } catch (RemoteException e) {
             /* do nothing */
         }
@@ -686,10 +692,10 @@
             return false;
         }
         mComponentNameToServiceMap.remove(service.mComponentName);
-        mHandler.removeMessages(service.mId);
         service.unlinkToOwnDeath();
         service.dispose();
         updateInputFilterLocked();
+        tryDisableTouchExploration(service);
         return removed;
     }
 
@@ -918,6 +924,29 @@
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
     }
 
+    private void tryEnableTouchExploration(final Service service) {
+        if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
+            mMainHandler.obtainMessage(MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG,
+                    service).sendToTarget();
+        }
+    }
+
+    private void tryDisableTouchExploration(Service service) {
+        if (mIsTouchExplorationEnabled && service.mReqeustTouchExplorationMode) {
+            synchronized (mLock) {
+                final int serviceCount = mServices.size();
+                for (int i = 0; i < serviceCount; i++) {
+                    Service other = mServices.get(i);
+                    if (other != service && other.mRequestTouchExplorationMode) {
+                        return;
+                    }
+                }
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0);
+            }
+        }
+    }
+
     private class AccessibilityConnectionWrapper implements DeathRecipient {
         private final int mWindowId;
         private final IAccessibilityInteractionConnection mConnection;
@@ -945,6 +974,42 @@
         }
     }
 
+    private class MainHanler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            final int type = msg.what;
+            switch (type) {
+                case MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG: {
+                    Service service = (Service) msg.obj;
+                    String label = service.mResolveInfo.loadLabel(
+                            mContext.getPackageManager()).toString();
+                    final AlertDialog dialog = new AlertDialog.Builder(mContext)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setPositiveButton(android.R.string.ok, new OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                Settings.Secure.putInt(mContext.getContentResolver(),
+                                        Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1);
+                            }
+                        })
+                        .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                dialog.dismiss();
+                            }
+                        })
+                        .setTitle(R.string.enable_explore_by_touch_warning_title)
+                        .setMessage(mContext.getString(
+                            R.string.enable_explore_by_touch_warning_message, label))
+                        .create();
+                    dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+                    dialog.setCanceledOnTouchOutside(true);
+                    dialog.show();
+                }
+            }
+        }
+    }
+
     /**
      * This class represents an accessibility service. It stores all per service
      * data required for the service management, provides API for starting/stopping the
@@ -955,6 +1020,11 @@
      */
     class Service extends IAccessibilityServiceConnection.Stub
             implements ServiceConnection, DeathRecipient {
+
+        // We pick the MSB to avoid collision since accessibility event types are
+        // used as message types allowing us to remove messages per event type. 
+        private static final int MSG_ON_GESTURE = 0x80000000;
+
         int mId = 0;
 
         AccessibilityServiceInfo mAccessibilityServiceInfo;
@@ -971,6 +1041,8 @@
 
         boolean mIsDefault;
 
+        boolean mRequestTouchExplorationMode;
+
         boolean mIncludeNotImportantViews;
 
         long mNotificationTimeout;
@@ -987,12 +1059,35 @@
 
         final Rect mTempBounds = new Rect();
 
+        final ResolveInfo mResolveInfo;
+
         // the events pending events to be dispatched to this service
         final SparseArray<AccessibilityEvent> mPendingEvents =
             new SparseArray<AccessibilityEvent>();
 
+        /**
+         * Handler for delayed event dispatch.
+         */
+        public Handler mHandler = new Handler(mMainHandler.getLooper()) {
+            @Override
+            public void handleMessage(Message message) {
+                final int type = message.what;
+                switch (type) {
+                    case MSG_ON_GESTURE: {
+                        final int gestureId = message.arg1;
+                        notifyGestureInternal(gestureId);
+                    } break;
+                    default: {
+                        final int eventType = type;
+                        notifyAccessibilityEventInternal(eventType);
+                    } break;
+                }
+            }
+        };
+
         public Service(ComponentName componentName,
                 AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
+            mResolveInfo = accessibilityServiceInfo.getResolveInfo();
             mId = sIdCounter++;
             mComponentName = componentName;
             mAccessibilityServiceInfo = accessibilityServiceInfo;
@@ -1029,6 +1124,9 @@
                     (info.flags & FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
             }
 
+            mRequestTouchExplorationMode = (info.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+
             synchronized (mLock) {
                 tryAddServiceLocked(this);
             }
@@ -1314,11 +1412,11 @@
                     }
                 }
             }
+            final int flags = (mIncludeNotImportantViews) ?
+                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+            final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                final int flags = (mIncludeNotImportantViews) ?
-                        AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-                final int interrogatingPid = Binder.getCallingPid();
                 connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
                         interactionId, callback, flags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
@@ -1343,7 +1441,7 @@
                     sendDownAndUpKeyEvents(KeyEvent.KEYCODE_APP_SWITCH);
                 } return true;
                 case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
-                    // TODO: Implement when 6346026 is fixed.
+                    expandStatusBar();
                 } return true;
             }
             return false;
@@ -1389,6 +1487,108 @@
             }
         }
 
+        /**
+         * Performs a notification for an {@link AccessibilityEvent}.
+         *
+         * @param event The event.
+         */
+        public void notifyAccessibilityEvent(AccessibilityEvent event) {
+            synchronized (mLock) {
+                final int eventType = event.getEventType();
+                // Make a copy since during dispatch it is possible the event to
+                // be modified to remove its source if the receiving service does
+                // not have permission to access the window content.
+                AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
+                AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
+                mPendingEvents.put(eventType, newEvent);
+
+                final int what = eventType;
+                if (oldEvent != null) {
+                    mHandler.removeMessages(what);
+                    oldEvent.recycle();
+                }
+
+                Message message = mHandler.obtainMessage(what);
+                mHandler.sendMessageDelayed(message, mNotificationTimeout);
+            }
+        }
+
+        /**
+         * Notifies an accessibility service client for a scheduled event given the event type.
+         *
+         * @param eventType The type of the event to dispatch.
+         */
+        private void notifyAccessibilityEventInternal(int eventType) {
+            IAccessibilityServiceClient listener;
+            AccessibilityEvent event;
+
+            synchronized (mLock) {
+                listener = mServiceInterface;
+
+                // If the service died/was disabled while the message for dispatching
+                // the accessibility event was propagating the listener may be null.
+                if (listener == null) {
+                    return;
+                }
+
+                event = mPendingEvents.get(eventType);
+
+                // Check for null here because there is a concurrent scenario in which this
+                // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
+                // which posts a message for dispatching an event. 2) The message is pulled
+                // from the queue by the handler on the service thread and the latter is
+                // just about to acquire the lock and call this method. 3) Now another binder
+                // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
+                // so the service thread waits for the lock; 4) The binder thread replaces
+                // the event with a more recent one (assume the same event type) and posts a
+                // dispatch request releasing the lock. 5) Now the main thread is unblocked and
+                // dispatches the event which is removed from the pending ones. 6) And ... now
+                // the service thread handles the last message posted by the last binder call
+                // but the event is already dispatched and hence looking it up in the pending
+                // ones yields null. This check is much simpler that keeping count for each
+                // event type of each service to catch such a scenario since only one message
+                // is processed at a time.
+                if (event == null) {
+                    return;
+                }
+
+                mPendingEvents.remove(eventType);
+                if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+                    event.setConnectionId(mId);
+                } else {
+                    event.setSource(null);
+                }
+                event.setSealed(true);
+            }
+
+            try {
+                listener.onAccessibilityEvent(event);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
+                }
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
+            } finally {
+                event.recycle();
+            }
+        }
+
+        public void notifyGesture(int gestureId) {
+            mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget();
+        }
+
+        private void notifyGestureInternal(int gestureId) {
+            IAccessibilityServiceClient listener = mServiceInterface;
+            if (listener != null) {
+                try {
+                    listener.onGesture(gestureId);
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
+                            + " to " + mService, re);
+                }
+            }
+        }
+
         private void sendDownAndUpKeyEvents(int keyCode) {
             final long token = Binder.clearCallingIdentity();
 
@@ -1413,6 +1613,16 @@
             Binder.restoreCallingIdentity(token);
         }
 
+        private void expandStatusBar() {
+            final long token = Binder.clearCallingIdentity();
+
+            StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
+                    android.app.Service.STATUS_BAR_SERVICE);
+            statusBarManager.expand();
+
+            Binder.restoreCallingIdentity(token);
+        }
+
         private IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
@@ -1430,7 +1640,7 @@
 
         private int resolveAccessibilityWindowId(int accessibilityWindowId) {
             if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
-                return mSecurityPolicy.mRetrievalAlowingWindowId;
+                return mSecurityPolicy.mActiveWindowId;
             }
             return accessibilityWindowId;
         }
@@ -1473,24 +1683,35 @@
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
 
-        private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
-            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
-            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
-
-        private int mRetrievalAlowingWindowId;
+        private int mActiveWindowId;
 
         private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
             // Send window changed event only for the retrieval allowing window.
             return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-                    || event.getWindowId() == mRetrievalAlowingWindowId);
+                    || event.getWindowId() == mActiveWindowId);
         }
 
-        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
+        public void updateActiveWindowAndEventSourceLocked(AccessibilityEvent event) {
+            // The active window is either the window that has input focus or
+            // the window that the user is currently touching. If the user is
+            // touching a window that does not have input focus as soon as the
+            // the user stops touching that window the focused window becomes
+            // the active one.
             final int windowId = event.getWindowId();
             final int eventType = event.getEventType();
-            if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) {
-                mRetrievalAlowingWindowId = windowId;
+            switch (eventType) {
+                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+                    if (getFocusedWindowId() == windowId) {
+                        mActiveWindowId = windowId;
+                    }
+                } break;
+                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
+                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
+                    mActiveWindowId = windowId;
+                } break;
+                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
+                    mActiveWindowId = getFocusedWindowId();
+                } break;
             }
             if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
                 event.setSource(null);
@@ -1498,7 +1719,7 @@
         }
 
         public int getRetrievalAllowingWindowLocked() {
-            return mRetrievalAlowingWindowId;
+            return mActiveWindowId;
         }
 
         public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
@@ -1526,7 +1747,7 @@
         }
 
         private boolean isRetrievalAllowingWindow(int windowId) {
-            return (mRetrievalAlowingWindowId == windowId);
+            return (mActiveWindowId == windowId);
         }
 
         private boolean isActionPermitted(int action) {
@@ -1543,5 +1764,22 @@
                         + " required to call " + function);
             }
         }
+
+        private int getFocusedWindowId() {
+            // We call this only on window focus change or after touch
+            // exploration gesture end and the shown windows are not that
+            // many, so the linear look up is just fine.
+            IBinder token = mWindowManagerService.getFocusedWindowClientToken();
+            if (token != null) {
+                SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
+                final int windowCount = windows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    if (windows.valueAt(i) == token) {
+                        return windows.keyAt(i);
+                    }
+                }
+            }
+            return -1;
+        }
     }
 }
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 39012e6..b0b2b8d 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -16,9 +16,6 @@
 
 package com.android.server.accessibility;
 
-import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
-import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
-
 import android.content.Context;
 import android.gesture.Gesture;
 import android.gesture.GestureLibraries;
@@ -26,17 +23,19 @@
 import android.gesture.GesturePoint;
 import android.gesture.GestureStroke;
 import android.gesture.Prediction;
+import android.graphics.Rect;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.server.input.InputFilter;
 import com.android.internal.R;
+import com.android.server.input.InputFilter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -47,17 +46,18 @@
  * and consuming certain events. The interaction model is:
  *
  * <ol>
- *   <li>1. One finger moving around performs touch exploration.</li>
- *   <li>2. Two close fingers moving in the same direction perform a drag.</li>
- *   <li>3. Multi-finger gestures are delivered to view hierarchy.</li>
- *   <li>4. Pointers that have not moved more than a specified distance after they
+ *   <li>1. One finger moving slow around performs touch exploration.</li>
+ *   <li>2. One finger moving fast around performs gestures.</li>
+ *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
+ *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
+ *   <li>5. Pointers that have not moved more than a specified distance after they
  *          went down are considered inactive.</li>
- *   <li>5. Two fingers moving too far from each other or in different directions
- *          are considered a multi-finger gesture.</li>
- *   <li>6. Tapping on the last touch explored location within given time and
- *          distance slop performs a click.</li>
- *   <li>7. Tapping and holding for a while on the last touch explored location within
- *          given time and distance slop performs a long press.</li>
+ *   <li>6. Two fingers moving in different directions are considered a multi-finger gesture.</li>
+ *   <li>7. Double tapping clicks on the on the last touch explored location of it was in
+ *          a window that does not take focus, otherwise the click is within the accessibility
+ *          focused rectangle.</li>
+ *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
+ *          as the click above.</li>
  * <ol>
  *
  * @hide
@@ -75,85 +75,116 @@
     private static final int STATE_DELEGATING = 0x00000004;
     private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
-    // The time slop in milliseconds for activating an item after it has
-    // been touch explored. Tapping on an item within this slop will perform
-    // a click and tapping and holding down a long press.
-    private static final long ACTIVATION_TIME_SLOP = 2000;
-
     // The minimum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
 
-    // The delay for sending a hover enter event.
-    private static final long DELAY_SEND_HOVER_ENTER = 200;
-
     // Constant referring to the ids bits of all pointers.
     private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
 
     // This constant captures the current implementation detail that
     // pointer IDs are between 0 and 31 inclusive (subject to change).
     // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
-    public static final int MAX_POINTER_COUNT = 32;
+    private static final int MAX_POINTER_COUNT = 32;
 
     // Invalid pointer ID.
-    public static final int INVALID_POINTER_ID = -1;
+    private static final int INVALID_POINTER_ID = -1;
+
+    // The velocity above which we detect gestures.
+    private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000;
+
+    // The minimal distance before we take the middle of the distance between
+    // the two dragging pointers as opposed to use the location of the primary one.
+    private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
 
     // Temporary array for storing pointer IDs.
     private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
 
-    // The distance from the last touch explored location tapping within
-    // which would perform a click and tapping and holding a long press.
-    private final int mTouchExplorationTapSlop;
+    // Timeout within which we try to detect a tap.
+    private final int mTapTimeout;
+
+    // Timeout within which we try to detect a double tap.
+    private final int mDoubleTapTimeout;
+
+    // Slop between the down and up tap to be a tap.
+    private final int mTouchSlop;
+
+    // Slop between the first and second tap to be a double tap.
+    private final int mDoubleTapSlop;
 
     // The InputFilter this tracker is associated with i.e. the filter
     // which delegates event processing to this touch explorer.
     private final InputFilter mInputFilter;
 
-    // Handle to the accessibility manager for firing accessibility events
-    // announcing touch exploration gesture start and end.
-    private final AccessibilityManager mAccessibilityManager;
-
-    // The last event that was received while performing touch exploration.
-    private MotionEvent mLastTouchExploreEvent;
-
     // The current state of the touch explorer.
     private int mCurrentState = STATE_TOUCH_EXPLORING;
 
-    // Flag whether a touch exploration gesture is in progress.
-    private boolean mTouchExploreGestureInProgress;
-
     // The ID of the pointer used for dragging.
     private int mDraggingPointerId;
 
     // Handler for performing asynchronous operations.
     private final Handler mHandler;
 
-    // Command for delayed sending of a hover event.
-    private final SendHoverDelayed mSendHoverDelayed;
+    // Command for delayed sending of a hover enter event.
+    private final SendHoverDelayed mSendHoverEnterDelayed;
+
+    // Command for delayed sending of a hover exit event.
+    private final SendHoverDelayed mSendHoverExitDelayed;
 
     // Command for delayed sending of a long press.
     private final PerformLongPressDelayed mPerformLongPressDelayed;
 
+    // Helper to detect and react to double tap in touch explore mode.
+    private final DoubleTapDetector mDoubleTapDetector;
+
+    // The scaled minimal distance before we take the middle of the distance between
+    // the two dragging pointers as opposed to use the location of the primary one.
+    private final int mScaledMinPointerDistanceToUseMiddleLocation;
+
+    // The scaled velocity above which we detect gestures.
+    private final int mScaledGestureDetectionVelocity;
+
+    // Helper to track gesture velocity.
     private VelocityTracker mVelocityTracker;
 
+    // Helper class to track received pointers.
     private final ReceivedPointerTracker mReceivedPointerTracker;
 
+    // Helper class to track injected pointers.
     private final InjectedPointerTracker mInjectedPointerTracker;
 
-    private final GestureListener mGestureListener;
+    // Handle to the accessibility manager service.
+    private final AccessibilityManagerService mAms;
 
-    /**
-     * Callback for gesture detection.
-     */
-    public interface GestureListener {
+    // Temporary rectangle to avoid instantiation.
+    private final Rect mTempRect = new Rect();
 
-        /**
-         * Called when a given gesture was performed.
-         *
-         * @param gestureId The gesture id.
-         */
-        public boolean onGesture(int gestureId);
-    }
+    // The X of the previous event.
+    private float mPreviousX;
+
+    // The Y of the previous event.
+    private float mPreviousY;
+
+    // Buffer for storing points for gesture detection.
+    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+    // The minimal delta between moves to add a gesture point.
+    private static final int TOUCH_TOLERANCE = 3;
+
+    // The minimal score for accepting a predicted gesture.
+    private static final float MIN_PREDICTION_SCORE = 2.0f;
+
+    // The library for gesture detection.
+    private GestureLibrary mGestureLibrary;
+
+    // The long pressing pointer id if coordinate remapping is needed.
+    private int mLongPressingPointerId;
+
+    // The long pressing pointer X if coordinate remapping is needed.
+    private int mLongPressingPointerDeltaX;
+
+    // The long pressing pointer Y if coordinate remapping is needed.
+    private int mLongPressingPointerDeltaY;
 
     /**
      * Creates a new instance.
@@ -162,25 +193,73 @@
      * @param context A context handle for accessing resources.
      */
     public TouchExplorer(InputFilter inputFilter, Context context,
-            GestureListener gestureListener) {
-        mGestureListener = gestureListener;
+            AccessibilityManagerService service) {
+        mAms = service;
         mReceivedPointerTracker = new ReceivedPointerTracker(context);
         mInjectedPointerTracker = new InjectedPointerTracker();
         mInputFilter = inputFilter;
-        mTouchExplorationTapSlop =
-            ViewConfiguration.get(context).getScaledTouchExploreTapSlop();
+        mTapTimeout = ViewConfiguration.getTapTimeout();
+        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
-        mSendHoverDelayed = new SendHoverDelayed();
         mPerformLongPressDelayed = new PerformLongPressDelayed();
-        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
         mGestureLibrary.setOrientationStyle(4);
         mGestureLibrary.load();
+        mSendHoverEnterDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_ENTER, true);
+        mSendHoverExitDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_EXIT, false);
+        mDoubleTapDetector = new DoubleTapDetector();
+        final float density = context.getResources().getDisplayMetrics().density;
+        mScaledMinPointerDistanceToUseMiddleLocation =
+            (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
+        mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density);
+    }
+
+    public void clear() {
+        // If we have not received an event then we are in initial
+        // state. Therefore, there is not need to clean anything.
+        MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
+        if (event != null) {
+            clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
+        }
     }
 
     public void clear(MotionEvent event, int policyFlags) {
-        sendUpForInjectedDownPointers(event, policyFlags);
-        clear();
+        switch (mCurrentState) {
+            case STATE_TOUCH_EXPLORING: {
+                // If a touch exploration gesture is in progress send events for its end.
+                sendExitEventsIfNeeded(policyFlags);
+            } break;
+            case STATE_DRAGGING: {
+                mDraggingPointerId = INVALID_POINTER_ID;
+                // Send exit to all pointers that we have delivered.
+                sendUpForInjectedDownPointers(event, policyFlags);
+            } break;
+            case STATE_DELEGATING: {
+                // Send exit to all pointers that we have delivered.
+                sendUpForInjectedDownPointers(event, policyFlags);
+            } break;
+            case STATE_GESTURE_DETECTING: {
+                // Clear the current stroke.
+                mStrokeBuffer.clear();
+            } break;
+        }
+        // Remove all pending callbacks.
+        mSendHoverEnterDelayed.remove();
+        mSendHoverExitDelayed.remove();
+        mPerformLongPressDelayed.remove();
+        // Reset the pointer trackers.
+        mReceivedPointerTracker.clear();
+        mInjectedPointerTracker.clear();
+        // Clear the double tap detector
+        mDoubleTapDetector.clear();
+        // Go to initial state.
+        // Clear the long pressing pointer remap data.
+        mLongPressingPointerId = -1;
+        mLongPressingPointerDeltaX = 0;
+        mLongPressingPointerDeltaY = 0;
+        mCurrentState = STATE_TOUCH_EXPLORING;
     }
 
     public void onMotionEvent(MotionEvent event, int policyFlags) {
@@ -218,7 +297,6 @@
      */
     private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
         ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
-        InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
         final int activePointerCount = receivedTracker.getActivePointerCount();
 
         if (mVelocityTracker == null) {
@@ -226,8 +304,16 @@
         }
         mVelocityTracker.addMovement(event);
 
+        mDoubleTapDetector.onMotionEvent(event, policyFlags);
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
+                // Pre-feed the motion events to the gesture detector since we
+                // have a distance slop before getting into gesture detection
+                // mode and not using the points within this slop significantly
+                // decreases the quality of gesture recognition.
+                handleMotionEventGestureDetecting(event, policyFlags);
+                //$FALL-THROUGH$
             case MotionEvent.ACTION_POINTER_DOWN: {
                 switch (activePointerCount) {
                     case 0: {
@@ -235,44 +321,31 @@
                                 + "touch exploring state!");
                     }
                     case 1: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // Send a hover for every finger down so the user gets feedback.
+                        // If we still have not notified the user for the last
+                        // touch, we figure out what to do. If were waiting
+                        // we resent the delayed callback and wait again.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        }
+
+                        // If we have the first tap schedule a long press and break
+                        // since we do not want to schedule hover enter because
+                        // the delayed callback will kick in before the long click.
+                        // This would lead to a state transition resulting in long
+                        // pressing the item below the double taped area which is
+                        // not necessary where accessibility focus is.
+                        if (mDoubleTapDetector.firstTapDetected()) {
+                            // We got a tap now post a long press action.
+                            mPerformLongPressDelayed.post(event, policyFlags);
+                            break;
+                        }
+                        // Deliver hover enter with a delay to have a chance
+                        // to detect what the user is trying to do.
                         final int pointerId = receivedTracker.getPrimaryActivePointerId();
                         final int pointerIdBits = (1 << pointerId);
-                        final int lastAction = injectedTracker.getLastInjectedHoverAction();
-
-                        // Deliver hover enter with a delay to have a change to detect
-                        // whether the user actually starts a scrolling gesture.
-                        if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
-                            mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER,
-                                    pointerIdBits, policyFlags, DELAY_SEND_HOVER_ENTER);
-                        } else {
-                            sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                    policyFlags);
-                        }
-
-                        if (mLastTouchExploreEvent == null) {
-                            break;
-                        }
-
-                        // If more pointers down on the screen since the last touch
-                        // exploration we discard the last cached touch explore event.
-                        if (event.getPointerCount() != mLastTouchExploreEvent.getPointerCount()) {
-                            mLastTouchExploreEvent = null;
-                            break;
-                        }
-
-                        // If the down is in the time slop => schedule a long press.
-                        final long pointerDownTime =
-                            receivedTracker.getReceivedPointerDownTime(pointerId);
-                        final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
-                        final long deltaTimeExplore = pointerDownTime - lastExploreTime;
-                        if (deltaTimeExplore <= ACTIVATION_TIME_SLOP) {
-                            mPerformLongPressDelayed.post(event, policyFlags,
-                                    ViewConfiguration.getLongPressTimeout());
-                            break;
-                        }
+                        mSendHoverEnterDelayed.post(event, pointerIdBits, policyFlags);
                     } break;
                     default: {
                         /* do nothing - let the code for ACTION_MOVE decide what to do */
@@ -288,119 +361,130 @@
                         /* do nothing - no active pointers so we swallow the event */
                     } break;
                     case 1: {
-                        // Detect touch exploration gesture start by having one active pointer
-                        // that moved more than a given distance.
-                        if (!mTouchExploreGestureInProgress) {
+                        // We have not started sending events since we try to
+                        // figure out what the user is doing.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // Pre-feed the motion events to the gesture detector since we
+                            // have a distance slop before getting into gesture detection
+                            // mode and not using the points within this slop significantly
+                            // decreases the quality of gesture recognition.
+                            handleMotionEventGestureDetecting(event, policyFlags);
+
                             final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
                                 - event.getX(pointerIndex);
                             final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
                                 - event.getY(pointerIndex);
                             final double moveDelta = Math.hypot(deltaX, deltaY);
-
-                            if (moveDelta > mTouchExplorationTapSlop) {
-
+                            // The user has moved enough for us to decide.
+                            if (moveDelta > mDoubleTapSlop) {
+                                // Check whether the user is performing a gesture. We
+                                // detect gestures if the pointer is moving above a
+                                // given velocity.
                                 mVelocityTracker.computeCurrentVelocity(1000);
                                 final float maxAbsVelocity = Math.max(
                                         Math.abs(mVelocityTracker.getXVelocity(pointerId)),
                                         Math.abs(mVelocityTracker.getYVelocity(pointerId)));
-                                // TODO: Tune the velocity cut off and add a constant.
-                                if (maxAbsVelocity > 1000) {
-                                    clear(event, policyFlags);
+                                if (maxAbsVelocity > mScaledGestureDetectionVelocity) {
+                                    // We have to perform gesture detection, so
+                                    // clear the current state and try to detect.
                                     mCurrentState = STATE_GESTURE_DETECTING;
-                                    event.setAction(MotionEvent.ACTION_DOWN);
-                                    handleMotionEventGestureDetecting(event, policyFlags);
-                                    return;
-                                }
-
-                                mTouchExploreGestureInProgress = true;
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
-                                // Make sure the scheduled down/move event is sent.
-                                mSendHoverDelayed.forceSendAndRemove();
-                                mPerformLongPressDelayed.remove();
-                                // If we have transitioned to exploring state from another one
-                                // we need to send a hover enter event here.
-                                final int lastAction = injectedTracker.getLastInjectedHoverAction();
-                                if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
-                                    sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER,
+                                    mSendHoverEnterDelayed.remove();
+                                    mSendHoverExitDelayed.remove();
+                                    mPerformLongPressDelayed.remove();
+                                } else {
+                                    // We have just decided that the user is touch,
+                                    // exploring so start sending events.
+                                    mSendHoverEnterDelayed.forceSendAndRemove();
+                                    mSendHoverExitDelayed.remove();
+                                    sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
                                             pointerIdBits, policyFlags);
                                 }
-                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                        policyFlags);
+                                break;
                             }
                         } else {
-                            // Touch exploration gesture in progress so send a hover event.
+                            // The user is wither double tapping or performing long
+                            // press so do not send move events yet.
+                            if (mDoubleTapDetector.firstTapDetected()) {
+                                break;
+                            }
+                            sendEnterEventsIfNeeded(policyFlags);
                             sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
                                     policyFlags);
                         }
-
-                        // If the exploring pointer moved enough => cancel the long press.
-                        if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null
-                                && mPerformLongPressDelayed.isPenidng()) {
-
-                            // If the pointer moved more than the tap slop => cancel long press.
-                            final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
-                                    - event.getX(pointerIndex);
-                            final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
-                                    - event.getY(pointerIndex);
-                            final float moveDelta = (float) Math.hypot(deltaX, deltaY);
-                            if (moveDelta > mTouchExplorationTapSlop) {
-                                mLastTouchExploreEvent = null;
-                                mPerformLongPressDelayed.remove();
-                                break;
-                            }
-                        }
                     } break;
                     case 2: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // We want to no longer hover over the location so subsequent
-                        // touch at the same spot will generate a hover enter.
-                        ensureHoverExitSent(event, pointerIdBits, policyFlags);
+                        // More than one pointer so the user is not touch exploring
+                        // and now we have to decide whether to delegate or drag.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // We have not started sending events so cancel
+                            // scheduled sending events.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        } else {
+                            // If the user is touch exploring the second pointer may be
+                            // performing a double tap to activate an item without need
+                            // for the user to lift his exploring finger.
+                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
+                                    - event.getX(pointerIndex);
+                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
+                                    - event.getY(pointerIndex);
+                            final double moveDelta = Math.hypot(deltaX, deltaY);
+                            if (moveDelta < mDoubleTapSlop) {
+                                break;
+                            }
+                            // We are sending events so send exit and gesture
+                            // end since we transition to another state.
+                            sendExitEventsIfNeeded(policyFlags);
+                        }
+
+                        // We know that a new state transition is to happen and the
+                        // new state will not be gesture recognition, so clear the
+                        // stashed gesture strokes.
+                        mStrokeBuffer.clear();
 
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
                             // a given distance perform a drag.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
                             mCurrentState = STATE_DRAGGING;
-                            if (mTouchExploreGestureInProgress) {
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                                mTouchExploreGestureInProgress = false;
-                            }
-                            mLastTouchExploreEvent = null;
                             mDraggingPointerId = pointerId;
                             sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
                                     policyFlags);
                         } else {
                             // Two pointers moving arbitrary are delegated to the view hierarchy.
                             mCurrentState = STATE_DELEGATING;
-                            mSendHoverDelayed.remove();
-                            if (mTouchExploreGestureInProgress) {
-                                sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                                mTouchExploreGestureInProgress = false;
-                            }
-                            mLastTouchExploreEvent = null;
                             sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                         }
                     } break;
                     default: {
-                        mSendHoverDelayed.remove();
-                        mPerformLongPressDelayed.remove();
-                        // We want to no longer hover over the location so subsequent
-                        // touch at the same spot will generate a hover enter.
-                        ensureHoverExitSent(event, pointerIdBits, policyFlags);
+                        // More than one pointer so the user is not touch exploring
+                        // and now we have to decide whether to delegate or drag.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            // We have not started sending events so cancel
+                            // scheduled sending events.
+                            mSendHoverEnterDelayed.remove();
+                            mSendHoverExitDelayed.remove();
+                            mPerformLongPressDelayed.remove();
+                        } else {
+                            // We are sending events so send exit and gesture
+                            // end since we transition to another state.
+                            sendExitEventsIfNeeded(policyFlags);
+                        }
 
                         // More than two pointers are delegated to the view hierarchy.
                         mCurrentState = STATE_DELEGATING;
-                        mSendHoverDelayed.remove();
-                        if (mTouchExploreGestureInProgress) {
-                            sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                            mTouchExploreGestureInProgress = false;
-                        }
-                        mLastTouchExploreEvent = null;
                         sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                     }
                 }
             } break;
             case MotionEvent.ACTION_UP:
+                // We know that we do not need the pre-fed gesture points are not
+                // needed anymore since the last pointer just went up.
+                mStrokeBuffer.clear();
+                //$FALL-THROUGH$
             case MotionEvent.ACTION_POINTER_UP: {
                 final int pointerId = receivedTracker.getLastReceivedUpPointerId();
                 final int pointerIdBits = (1 << pointerId);
@@ -413,59 +497,12 @@
 
                         mPerformLongPressDelayed.remove();
 
-                        // If touch exploring announce the end of the gesture.
-                        // Also do not click on the last explored location.
-                        if (mTouchExploreGestureInProgress) {
-                            mTouchExploreGestureInProgress = false;
-                            mSendHoverDelayed.forceSendAndRemove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                            mLastTouchExploreEvent = MotionEvent.obtain(event);
-                            sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
-                            break;
-                        }
-
-                        // Detect whether to activate i.e. click on the last explored location.
-                        if (mLastTouchExploreEvent != null) {
-                            // If the down was not in the time slop => nothing else to do.
-                            final long eventTime =
-                                receivedTracker.getLastReceivedUpPointerDownTime();
-                            final long exploreTime = mLastTouchExploreEvent.getEventTime();
-                            final long deltaTime = eventTime - exploreTime;
-                            if (deltaTime > ACTIVATION_TIME_SLOP) {
-                                mSendHoverDelayed.forceSendAndRemove();
-                                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                                mLastTouchExploreEvent = MotionEvent.obtain(event);
-                                break;
-                            }
-
-                            // If a tap is farther than the tap slop => nothing to do.
-                            final int pointerIndex = event.findPointerIndex(pointerId);
-                            final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
-                                    - event.getX(pointerIndex);
-                            final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
-                                    - event.getY(pointerIndex);
-                            final float deltaMove = (float) Math.hypot(deltaX, deltaY);
-                            if (deltaMove > mTouchExplorationTapSlop) {
-                                mSendHoverDelayed.forceSendAndRemove();
-                                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                                mLastTouchExploreEvent = MotionEvent.obtain(event);
-                                break;
-                            }
-
-                            // This is a tap so do not send hover events since
-                            // this events will result in firing the corresponding
-                            // accessibility events confusing the user about what
-                            // is actually clicked.
-                            mSendHoverDelayed.remove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-
-                            // All preconditions are met, so click the last explored location.
-                            sendActionDownAndUp(mLastTouchExploreEvent, policyFlags);
-                            mLastTouchExploreEvent = null;
+                        // If we have not delivered the enter schedule exit.
+                        if (mSendHoverEnterDelayed.isPending()) {
+                            mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
                         } else {
-                            mSendHoverDelayed.forceSendAndRemove();
-                            ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                            mLastTouchExploreEvent = MotionEvent.obtain(event);
+                            // The user is touch exploring so we send events for end.
+                            sendExitEventsIfNeeded(policyFlags);
                         }
                     } break;
                 }
@@ -475,16 +512,7 @@
                 }
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                mSendHoverDelayed.remove();
-                mPerformLongPressDelayed.remove();
-                final int pointerId = receivedTracker.getPrimaryActivePointerId();
-                final int pointerIdBits = (1 << pointerId);                
-                ensureHoverExitSent(event, pointerIdBits, policyFlags);
-                clear();
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.clear();
-                    mVelocityTracker = null;
-                }
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -517,6 +545,28 @@
                     } break;
                     case 2: {
                         if (isDraggingGesture(event)) {
+                            // If the dragging pointer are closer that a given distance we
+                            // use the location of the primary one. Otherwise, we take the
+                            // middle between the pointers.
+                            int[] pointerIds = mTempPointerIds;
+                            mReceivedPointerTracker.populateActivePointerIds(pointerIds);
+
+                            final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
+                            final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
+
+                            final float firstPtrX = event.getX(firstPtrIndex);
+                            final float firstPtrY = event.getY(firstPtrIndex);
+                            final float secondPtrX = event.getX(secondPtrIndex);
+                            final float secondPtrY = event.getY(secondPtrIndex);
+
+                            final float deltaX = firstPtrX - secondPtrX;
+                            final float deltaY = firstPtrY - secondPtrY;
+                            final double distance = Math.hypot(deltaX, deltaY);
+
+                            if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
+                                event.setLocation(deltaX / 2, deltaY / 2);
+                            }
+
                             // If still dragging send a drag event.
                             sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
                                     policyFlags);
@@ -557,7 +607,7 @@
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                clear();
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -574,9 +624,6 @@
                 throw new IllegalStateException("Delegating state can only be reached if "
                         + "there is at least one pointer down!");
             }
-            case MotionEvent.ACTION_UP: {
-                mCurrentState = STATE_TOUCH_EXPLORING;
-            } break;
             case MotionEvent.ACTION_MOVE: {
                 // Check  whether some other pointer became active because they have moved
                 // a given distance and if such exist send them to the view hierarchy
@@ -587,30 +634,24 @@
                     sendDownForAllActiveNotInjectedPointers(prototype, policyFlags);
                 }
             } break;
+            case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP: {
+                mLongPressingPointerId = -1;
+                mLongPressingPointerDeltaX = 0;
+                mLongPressingPointerDeltaY = 0;
                 // No active pointers => go to initial state.
                 if (mReceivedPointerTracker.getActivePointerCount() == 0) {
                     mCurrentState = STATE_TOUCH_EXPLORING;
                 }
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                clear();
+                clear(event, policyFlags);
             } break;
         }
         // Deliver the event striping out inactive pointers.
         sendMotionEventStripInactivePointers(event, policyFlags);
     }
 
-    private float mPreviousX;
-    private float mPreviousY;
-
-    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
-
-    private static final int TOUCH_TOLERANCE = 3;
-    private static final float MIN_PREDICTION_SCORE = 2.0f;
-
-    private GestureLibrary mGestureLibrary;
-
     private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
@@ -631,8 +672,7 @@
                     mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
                 }
             } break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_POINTER_UP: {
+            case MotionEvent.ACTION_UP: {
                 float x = event.getX();
                 float y = event.getY();
                 mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
@@ -650,7 +690,7 @@
                         }
                         try {
                             final int gestureId = Integer.parseInt(bestPrediction.name);
-                            mGestureListener.onGesture(gestureId);
+                            mAms.onGesture(gestureId);
                         } catch (NumberFormatException nfe) {
                             Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
                         }
@@ -661,8 +701,7 @@
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
-                mStrokeBuffer.clear();
-                mCurrentState = STATE_TOUCH_EXPLORING;
+                clear(event, policyFlags);
             } break;
         }
     }
@@ -706,17 +745,32 @@
     }
 
     /**
-     * Ensures that hover exit has been sent.
+     * Sends the exit events if needed. Such events are hover exit and touch explore
+     * gesture end.
      *
-     * @param prototype The prototype from which to create the injected events.
-     * @param pointerIdBits The bits of the pointers to send.
      * @param policyFlags The policy flags associated with the event.
      */
-    private void ensureHoverExitSent(MotionEvent prototype, int pointerIdBits, int policyFlags) {
-        final int lastAction = mInjectedPointerTracker.getLastInjectedHoverAction();
-        if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
-            sendMotionEvent(prototype, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
-                    policyFlags);
+    private void sendExitEventsIfNeeded(int policyFlags) {
+        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+        if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
+            final int pointerIdBits = event.getPointerIdBits();
+            mAms.touchExplorationGestureEnded();
+            sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
+        }
+    }
+
+    /**
+     * Sends the enter events if needed. Such events are hover enter and touch explore
+     * gesture start.
+     *
+     * @param policyFlags The policy flags associated with the event.
+     */
+    private void sendEnterEventsIfNeeded(int policyFlags) {
+        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+        if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
+            final int pointerIdBits = event.getPointerIdBits();
+            mAms.touchExplorationGestureStarted();
+            sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
         }
     }
 
@@ -826,6 +880,36 @@
             event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
         }
 
+        // If the user is long pressing but the long pressing pointer
+        // was not exactly over the accessibility focused item we need
+        // to remap the location of that pointer so the user does not
+        // have to explicitly touch explore something to be able to
+        // long press it, or even worse to avoid the user long pressing
+        // on the wrong item since click and long press behave differently.
+        if (mLongPressingPointerId >= 0) {
+            final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+            final int pointerCount = event.getPointerCount();
+            PointerProperties[] props = PointerProperties.createArray(pointerCount);
+            PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+            for (int i = 0; i < pointerCount; i++) {
+                event.getPointerProperties(i, props[i]);
+                event.getPointerCoords(i, coords[i]);
+                if (i == remappedIndex) {
+                    coords[i].x -= mLongPressingPointerDeltaX;
+                    coords[i].y -= mLongPressingPointerDeltaY;
+                }
+            }
+            MotionEvent remapped = MotionEvent.obtain(event.getDownTime(),
+                    event.getEventTime(), event.getAction(), event.getPointerCount(),
+                    props, coords, event.getMetaState(), event.getButtonState(),
+                    1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
+                    event.getSource(), event.getFlags());
+            if (event != prototype) {
+                event.recycle();
+            }
+            event = remapped;
+        }
+
         if (DEBUG) {
             Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
                     + Integer.toHexString(policyFlags));
@@ -878,6 +962,172 @@
         }
     }
 
+    private class DoubleTapDetector {
+        private MotionEvent mDownEvent;
+        private MotionEvent mFirstTapEvent;
+
+        public void onMotionEvent(MotionEvent event, int policyFlags) {
+            final int action = event.getActionMasked();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_POINTER_DOWN: {
+                    if (mFirstTapEvent != null && !isSamePointerContext(mFirstTapEvent, event)) {
+                        clear();
+                    }
+                    mDownEvent = MotionEvent.obtain(event);
+                } break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_POINTER_UP: {
+                    if (mDownEvent == null) {
+                        return;
+                    }
+                    if (!isSamePointerContext(mDownEvent, event)) {
+                        clear();
+                        return;
+                    }
+                    if (isTap(mDownEvent, event)) {
+                        if (mFirstTapEvent == null || isTimedOut(mFirstTapEvent, event,
+                                mDoubleTapTimeout)) {
+                            mFirstTapEvent = MotionEvent.obtain(event);
+                            mDownEvent.recycle();
+                            mDownEvent = null;
+                            return;
+                        }
+                        if (isDoubleTap(mFirstTapEvent, event)) {
+                            onDoubleTap(event, policyFlags);
+                            mFirstTapEvent.recycle();
+                            mFirstTapEvent = null;
+                            mDownEvent.recycle();
+                            mDownEvent = null;
+                            return;
+                        }
+                        mFirstTapEvent.recycle();
+                        mFirstTapEvent = null;
+                    } else {
+                        if (mFirstTapEvent != null) {
+                            mFirstTapEvent.recycle();
+                            mFirstTapEvent = null;
+                        }
+                    }
+                    mDownEvent.recycle();
+                    mDownEvent = null;
+                } break;
+            }
+        }
+
+        public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
+            // This should never be called when more than two pointers are down.
+            if (secondTapUp.getPointerCount() > 2) {
+                return;
+            }
+
+            // Remove pending event deliveries.
+            mSendHoverEnterDelayed.remove();
+            mSendHoverExitDelayed.remove();
+            mPerformLongPressDelayed.remove();
+
+            // This is a tap so do not send hover events since
+            // this events will result in firing the corresponding
+            // accessibility events confusing the user about what
+            // is actually clicked.
+            sendExitEventsIfNeeded(policyFlags);
+
+            // If the last touched explored location is not within the focused
+            // window we will click at that exact spot, otherwise we find the
+            // accessibility focus and if the tap is within its bounds we click
+            // there, otherwise we pick the middle of the focus rectangle.
+            MotionEvent lastEvent = mInjectedPointerTracker.getLastInjectedHoverEvent();
+            if (lastEvent == null) {
+                return;
+            }
+
+            final int exploreLocationX = (int) lastEvent.getX(lastEvent.getActionIndex());
+            final int exploreLocationY = (int) lastEvent.getY(lastEvent.getActionIndex());
+
+            Rect bounds = mTempRect;
+            boolean useLastHoverLocation = false;
+
+            final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
+            final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
+            if (mAms.getAccessibilityFocusBounds(exploreLocationX, exploreLocationY, bounds)) {
+                // If the user's last touch explored location is not
+                // within the accessibility focus bounds we use the center
+                // of the accessibility focused rectangle.
+                if (!bounds.contains((int) secondTapUp.getX(pointerIndex),
+                        (int) secondTapUp.getY(pointerIndex))) {
+                    useLastHoverLocation = true;
+                }
+            }
+
+            // Do the click.
+            PointerProperties[] properties = new PointerProperties[1];
+            properties[0] = new PointerProperties();
+            secondTapUp.getPointerProperties(pointerIndex, properties[0]);
+            PointerCoords[] coords = new PointerCoords[1];
+            coords[0] = new PointerCoords();
+            coords[0].x = (useLastHoverLocation) ? bounds.centerX() : exploreLocationX;
+            coords[0].y = (useLastHoverLocation) ? bounds.centerY() : exploreLocationY;
+            MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(),
+                    secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
+                    coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
+                    secondTapUp.getSource(), secondTapUp.getFlags());
+            sendActionDownAndUp(event, policyFlags);
+            event.recycle();
+        }
+
+        public void clear() {
+            if (mDownEvent != null) {
+                mDownEvent.recycle();
+                mDownEvent = null;
+            }
+            if (mFirstTapEvent != null) {
+                mFirstTapEvent.recycle();
+                mFirstTapEvent = null;
+            }
+        }
+
+        public boolean isTap(MotionEvent down, MotionEvent up) {
+            return eventsWithinTimeoutAndDistance(down, up, mTapTimeout, mTouchSlop);
+        }
+
+        private boolean isDoubleTap(MotionEvent firstUp, MotionEvent secondUp) {
+            return eventsWithinTimeoutAndDistance(firstUp, secondUp, mDoubleTapTimeout,
+                    mDoubleTapSlop);
+        }
+
+        private boolean eventsWithinTimeoutAndDistance(MotionEvent first, MotionEvent second,
+                int timeout, int distance) {
+            if (isTimedOut(first, second, timeout)) {
+                return false;
+            }
+            final int downPtrIndex = first.getActionIndex();
+            final int upPtrIndex = second.getActionIndex();
+            final float deltaX = second.getX(upPtrIndex) - first.getX(downPtrIndex);
+            final float deltaY = second.getY(upPtrIndex) - first.getY(downPtrIndex);
+            final double deltaMove = Math.hypot(deltaX, deltaY);
+            if (deltaMove >= distance) {
+                return false;
+            }
+            return true;
+        }
+
+        private boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
+            final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
+            return (deltaTime >= timeout);
+        }
+
+        private boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
+            return (first.getPointerIdBits() == second.getPointerIdBits()
+                    && first.getPointerId(first.getActionIndex())
+                            == second.getPointerId(second.getActionIndex()));
+        }
+
+        public boolean firstTapDetected() {
+            return mFirstTapEvent != null
+                && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
+        }
+    }
+
     /**
      * Determines whether a two pointer gesture is a dragging one.
      *
@@ -940,30 +1190,6 @@
         return true;
     }
 
-   /**
-    * Sends an event announcing the start/end of a touch exploration gesture.
-    *
-    * @param eventType The type of the event to send.
-    */
-    private void sendAccessibilityEvent(int eventType) {
-        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        mAccessibilityManager.sendAccessibilityEvent(event);
-    }
-
-    /**
-     * Clears the internal state of this explorer.
-     */
-    public void clear() {
-        mSendHoverDelayed.remove();
-        mPerformLongPressDelayed.remove();
-        mReceivedPointerTracker.clear();
-        mInjectedPointerTracker.clear();
-        mLastTouchExploreEvent = null;
-        mCurrentState = STATE_TOUCH_EXPLORING;
-        mTouchExploreGestureInProgress = false;
-        mDraggingPointerId = INVALID_POINTER_ID;
-    }
-
     /**
      * Gets the symbolic name of a state.
      *
@@ -1002,10 +1228,10 @@
         private MotionEvent mEvent;
         private int mPolicyFlags;
 
-        public void post(MotionEvent prototype, int policyFlags, long delay) {
+        public void post(MotionEvent prototype, int policyFlags) {
             mEvent = MotionEvent.obtain(prototype);
             mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, delay);
+            mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
         }
 
         public void remove() {
@@ -1021,16 +1247,29 @@
 
         @Override
         public void run() {
-            mCurrentState = STATE_DELEGATING;
-            // Make sure the scheduled hover exit is delivered.
-            mSendHoverDelayed.remove();
+            final int pointerIndex = mEvent.getActionIndex();
+            final int eventX = (int) mEvent.getX(pointerIndex);
+            final int eventY = (int) mEvent.getY(pointerIndex);
+            Rect bounds = mTempRect;
+            if (mAms.getAccessibilityFocusBounds(eventX, eventY, bounds)
+                    && !bounds.contains(eventX, eventY)) {
+                mLongPressingPointerId = mEvent.getPointerId(pointerIndex);
+                mLongPressingPointerDeltaX = eventX - bounds.centerX();
+                mLongPressingPointerDeltaY = eventY - bounds.centerY();
+            } else {
+                mLongPressingPointerId = -1;
+                mLongPressingPointerDeltaX = 0;
+                mLongPressingPointerDeltaY = 0;
+            }
+            // We are sending events so send exit and gesture
+            // end since we transition to another state.
             final int pointerId = mReceivedPointerTracker.getPrimaryActivePointerId();
             final int pointerIdBits = (1 << pointerId);
-            ensureHoverExitSent(mEvent, pointerIdBits, mPolicyFlags);
+            mAms.touchExplorationGestureEnded();
+            sendExitEventsIfNeeded(mPolicyFlags);
 
+            mCurrentState = STATE_DELEGATING;
             sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
-            mTouchExploreGestureInProgress = false;
-            mLastTouchExploreEvent = null;
             clear();
         }
 
@@ -1047,20 +1286,41 @@
     /**
      * Class for delayed sending of hover events.
      */
-    private final class SendHoverDelayed implements Runnable {
-        private MotionEvent mEvent;
-        private int mAction;
+    class SendHoverDelayed implements Runnable {
+        private final String LOG_TAG_SEND_HOVER_DELAYED = SendHoverDelayed.class.getName();
+
+        private final int mHoverAction;
+        private final boolean mGestureStarted;
+
+        private MotionEvent mPrototype;
         private int mPointerIdBits;
         private int mPolicyFlags;
 
-        public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
-                long delay) {
+        public SendHoverDelayed(int hoverAction, boolean gestureStarted) {
+            mHoverAction = hoverAction;
+            mGestureStarted = gestureStarted;
+        }
+
+        public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
             remove();
-            mEvent = MotionEvent.obtain(prototype);
-            mAction = action;
+            mPrototype = MotionEvent.obtain(prototype);
             mPointerIdBits = pointerIdBits;
             mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, delay);
+            mHandler.postDelayed(this, mTapTimeout);
+        }
+
+        public float getX() {
+            if (isPending()) {
+                return mPrototype.getX();
+            }
+            return 0;
+        }
+
+        public float getY() {
+            if (isPending()) {
+                return mPrototype.getY();
+            }
+            return 0;
         }
 
         public void remove() {
@@ -1068,23 +1328,22 @@
             clear();
         }
 
-        private boolean isPenidng() {
-            return (mEvent != null);
+        private boolean isPending() {
+            return (mPrototype != null);
         }
 
         private void clear() {
-            if (!isPenidng()) {
+            if (!isPending()) {
                 return;
             }
-            mEvent.recycle();
-            mEvent = null;
-            mAction = 0;
+            mPrototype.recycle();
+            mPrototype = null;
             mPointerIdBits = -1;
             mPolicyFlags = 0;
         }
 
         public void forceSendAndRemove() {
-            if (isPenidng()) {
+            if (isPending()) {
                 run();
                 remove();
             }
@@ -1092,16 +1351,17 @@
 
         public void run() {
             if (DEBUG) {
-                if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
-                    Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
-                } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
-                    Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
-                } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
-                    Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
-                }
+                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event: "
+                        + MotionEvent.actionToString(mHoverAction));
+                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ?
+                        "touchExplorationGestureStarted" : "touchExplorationGestureEnded");
             }
-
-            sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
+            if (mGestureStarted) {
+                mAms.touchExplorationGestureStarted();
+            } else {
+                mAms.touchExplorationGestureEnded();
+            }
+            sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags);
             clear();
         }
     }
@@ -1120,8 +1380,8 @@
         // The time of the last injected down.
         private long mLastInjectedDownEventTime;
 
-        // The action of the last injected hover event.
-        private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+        // The last injected hover event.
+        private MotionEvent mLastInjectedHoverEvent;
 
         /**
          * Processes an injected {@link MotionEvent} event.
@@ -1150,11 +1410,14 @@
                 case MotionEvent.ACTION_HOVER_ENTER:
                 case MotionEvent.ACTION_HOVER_MOVE:
                 case MotionEvent.ACTION_HOVER_EXIT: {
-                    mLastInjectedHoverEventAction = event.getActionMasked();
+                    if (mLastInjectedHoverEvent != null) {
+                        mLastInjectedHoverEvent.recycle();
+                    }
+                    mLastInjectedHoverEvent = MotionEvent.obtain(event);
                 } break;
             }
             if (DEBUG) {
-                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer: " + toString());
+                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
             }
         }
 
@@ -1198,10 +1461,10 @@
         }
 
         /**
-         * @return The action of the last injected hover event.
+         * @return The the last injected hover event.
          */
-        public int getLastInjectedHoverAction() {
-            return mLastInjectedHoverEventAction;
+        public MotionEvent getLastInjectedHoverEvent() {
+            return mLastInjectedHoverEvent;
         }
 
         @Override
@@ -1260,6 +1523,8 @@
         private float mLastReceivedUpPointerDownX;
         private float mLastReceivedUpPointerDownY;
 
+        private MotionEvent mLastReceivedEvent;
+
         /**
          * Creates a new instance.
          *
@@ -1294,6 +1559,11 @@
          * @param event The event to process.
          */
         public void onMotionEvent(MotionEvent event) {
+            if (mLastReceivedEvent != null) {
+                mLastReceivedEvent.recycle();
+            }
+            mLastReceivedEvent = MotionEvent.obtain(event);
+
             final int action = event.getActionMasked();
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
@@ -1318,6 +1588,13 @@
         }
 
         /**
+         * @return The last received event.
+         */
+        public MotionEvent getLastReceivedEvent() {
+            return mLastReceivedEvent;
+        }
+
+        /**
          * @return The number of received pointers that are down.
          */
         public int getReceivedPointerDownCount() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 54ef724..6464d7f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3510,31 +3510,36 @@
 
     public void closeSystemDialogs(String reason) {
         enforceNotIsolatedCaller("closeSystemDialogs");
+
+        final int uid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (this) {
+            closeSystemDialogsLocked(uid, reason);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    void closeSystemDialogsLocked(int callingUid, String reason) {
         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         if (reason != null) {
             intent.putExtra("reason", reason);
         }
+        mWindowManager.closeSystemDialogs(reason);
         
-        final int uid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (this) {
-            mWindowManager.closeSystemDialogs(reason);
-            
-            for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
-                if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    r.stack.finishActivityLocked(r, i,
-                            Activity.RESULT_CANCELED, null, "close-sys");
-                }
+        for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
+            if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+                r.stack.finishActivityLocked(r, i,
+                        Activity.RESULT_CANCELED, null, "close-sys");
             }
-            
-            broadcastIntentLocked(null, null, intent, null,
-                    null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
         }
-        Binder.restoreCallingIdentity(origId);
+        
+        broadcastIntentLocked(null, null, intent, null,
+                null, 0, null, null, null, false, false, -1,
+                callingUid, 0 /* TODO: Verify */);
     }
-    
+
     public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
             throws RemoteException {
         enforceNotIsolatedCaller("getProcessMemoryInfo");
@@ -4634,35 +4639,12 @@
             pid = tlsIdentity.pid;
         }
 
-        // Root, system server and our own process get to do everything.
-        if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
+        if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-        // Isolated processes don't get any permissions.
-        if (UserId.isIsolated(uid)) {
-            return PackageManager.PERMISSION_DENIED;
-        }
-        // If there is a uid that owns whatever is being accessed, it has
-        // blanket access to it regardless of the permissions it requires.
-        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-        // If the target is not exported, then nobody else can get to it.
-        if (!exported) {
-            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
-            return PackageManager.PERMISSION_DENIED;
-        }
-        if (permission == null) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-        try {
-            return AppGlobals.getPackageManager()
-                    .checkUidPermission(permission, uid);
-        } catch (RemoteException e) {
-            // Should never happen, but if it does... deny!
-            Slog.e(TAG, "PackageManager is dead?!?", e);
-        }
-        return PackageManager.PERMISSION_DENIED;
+
+        return ActivityManager.checkComponentPermission(permission, uid,
+                owningUid, exported);
     }
 
     /**
@@ -13539,6 +13521,14 @@
         }
     }
 
+    public int getLaunchedFromUid(IBinder activityToken) {
+        ActivityRecord srec = ActivityRecord.forToken(activityToken);
+        if (srec == null) {
+            return -1;
+        }
+        return srec.launchedFromUid;
+    }
+
     // =========================================================
     // LIFETIME MANAGEMENT
     // =========================================================
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b9f63cf..5b15e50 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -52,6 +52,7 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserId;
@@ -2532,6 +2533,10 @@
             mDismissKeyguardOnNextActivity = false;
             mService.mWindowManager.dismissKeyguard();
         }
+        if (err >= ActivityManager.START_SUCCESS &&
+                (launchFlags&Intent.FLAG_ACTIVITY_CLOSE_SYSTEM_DIALOGS) != 0) {
+            mService.closeSystemDialogsLocked(Process.myUid(), "launch");
+        }
         return err;
     }
   
@@ -2619,6 +2624,7 @@
         }
 
         boolean addingToTask = false;
+        boolean movedHome = false;
         TaskRecord reuseTask = null;
         if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                 (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
@@ -2657,6 +2663,7 @@
                         if (callerAtFront) {
                             // We really do want to push this one into the
                             // user's face, right now.
+                            movedHome = true;
                             moveHomeToFrontFromLaunchLocked(launchFlags);
                             moveTaskToFrontLocked(taskTop.task, r, options);
                         }
@@ -2835,7 +2842,9 @@
                 r.setTask(reuseTask, reuseTask, true);
             }
             newTask = true;
-            moveHomeToFrontFromLaunchLocked(launchFlags);
+            if (!movedHome) {
+                moveHomeToFrontFromLaunchLocked(launchFlags);
+            }
             
         } else if (sourceRecord != null) {
             if (!addingToTask &&
@@ -3543,7 +3552,7 @@
         }
         ActivityRecord r = mHistory.get(index);
 
-        while (index > 0) {
+        while (index >= 0) {
             ActivityRecord cur = mHistory.get(index);
             if (cur.task != r.task) {
                 break;
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 9e94b52..d85facc 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -16,16 +16,16 @@
 
 package com.android.server.input;
 
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.R;
 import com.android.internal.util.XmlUtils;
 import com.android.server.Watchdog;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
@@ -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;
@@ -71,23 +70,19 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
+import android.widget.Toast;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
 
-import libcore.io.IoUtils;
 import libcore.io.Streams;
 import libcore.util.Objects;
 
@@ -96,11 +91,15 @@
  */
 public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
     static final String TAG = "InputManager";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
 
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
+    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
+    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
+    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
+    private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
 
     // Pointer to native input manager service object.
     private final int mPtr;
@@ -110,6 +109,7 @@
     private final InputManagerHandler mHandler;
     private boolean mSystemReady;
     private BluetoothService mBluetoothService;
+    private NotificationManager mNotificationManager;
 
     // Persistent data store.  Must be locked each time during use.
     private final PersistentDataStore mDataStore = new PersistentDataStore();
@@ -123,6 +123,11 @@
     private final ArrayList<InputDevicesChangedListenerRecord>
             mTempInputDevicesChangedListenersToNotify =
                     new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
+    private final ArrayList<InputDevice>
+            mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
+    private boolean mKeyboardLayoutNotificationShown;
+    private PendingIntent mKeyboardLayoutIntent;
+    private Toast mSwitchedKeyboardLayoutToast;
 
     // State for vibrator tokens.
     private Object mVibratorLock = new Object();
@@ -236,6 +241,8 @@
             Slog.d(TAG, "System ready.");
         }
         mBluetoothService = bluetoothService;
+        mNotificationManager = (NotificationManager)mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
         mSystemReady = true;
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -245,10 +252,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Packages changed, reloading keyboard layouts.");
-                }
-                reloadKeyboardLayouts();
+                updateKeyboardLayouts();
             }
         }, filter, null, mHandler);
 
@@ -256,22 +260,25 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Bluetooth alias changed, reloading device names.");
-                }
                 reloadDeviceAliases();
             }
         }, filter, null, mHandler);
 
-        reloadKeyboardLayouts();
-        reloadDeviceAliases();
+        mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
+        mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
     }
 
     private void reloadKeyboardLayouts() {
+        if (DEBUG) {
+            Slog.d(TAG, "Reloading keyboard layouts.");
+        }
         nativeReloadKeyboardLayouts(mPtr);
     }
 
     private void reloadDeviceAliases() {
+        if (DEBUG) {
+            Slog.d(TAG, "Reloading device names.");
+        }
         nativeReloadDeviceAliases(mPtr);
     }
 
@@ -559,9 +566,11 @@
     }
 
     // Must be called on handler.
-    private void deliverInputDevicesChanged() {
+    private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) {
+        // Scan for changes.
+        int numFullKeyboardsAdded = 0;
         mTempInputDevicesChangedListenersToNotify.clear();
-
+        mTempFullKeyboards.clear();
         final int numListeners;
         final int[] deviceIdAndGeneration;
         synchronized (mInputDevicesLock) {
@@ -582,13 +591,126 @@
                 final InputDevice inputDevice = mInputDevices[i];
                 deviceIdAndGeneration[i * 2] = inputDevice.getId();
                 deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
+
+                if (isFullKeyboard(inputDevice)) {
+                    if (!containsInputDeviceWithDescriptor(oldInputDevices,
+                            inputDevice.getDescriptor())) {
+                        mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
+                    } else {
+                        mTempFullKeyboards.add(inputDevice);
+                    }
+                }
             }
         }
 
+        // Notify listeners.
         for (int i = 0; i < numListeners; i++) {
             mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
                     deviceIdAndGeneration);
         }
+        mTempInputDevicesChangedListenersToNotify.clear();
+
+        // Check for missing keyboard layouts.
+        if (mNotificationManager != null) {
+            final int numFullKeyboards = mTempFullKeyboards.size();
+            boolean missingLayoutForExternalKeyboard = false;
+            boolean missingLayoutForExternalKeyboardAdded = false;
+            synchronized (mDataStore) {
+                for (int i = 0; i < numFullKeyboards; i++) {
+                    final InputDevice inputDevice = mTempFullKeyboards.get(i);
+                    if (mDataStore.getCurrentKeyboardLayout(inputDevice.getDescriptor()) == null) {
+                        missingLayoutForExternalKeyboard = true;
+                        if (i < numFullKeyboardsAdded) {
+                            missingLayoutForExternalKeyboardAdded = true;
+                        }
+                    }
+                }
+            }
+            if (missingLayoutForExternalKeyboard) {
+                if (missingLayoutForExternalKeyboardAdded) {
+                    showMissingKeyboardLayoutNotification();
+                }
+            } else if (mKeyboardLayoutNotificationShown) {
+                hideMissingKeyboardLayoutNotification();
+            }
+        }
+        mTempFullKeyboards.clear();
+    }
+
+    // Must be called on handler.
+    private void showMissingKeyboardLayoutNotification() {
+        if (!mKeyboardLayoutNotificationShown) {
+            if (mKeyboardLayoutIntent == null) {
+                final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS");
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mKeyboardLayoutIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+            }
+
+            Resources r = mContext.getResources();
+            Notification notification = new Notification.Builder(mContext)
+                    .setContentTitle(r.getString(
+                            R.string.select_keyboard_layout_notification_title))
+                    .setContentText(r.getString(
+                            R.string.select_keyboard_layout_notification_message))
+                    .setContentIntent(mKeyboardLayoutIntent)
+                    .setSmallIcon(R.drawable.ic_settings_language)
+                    .setPriority(Notification.PRIORITY_LOW)
+                    .build();
+            mNotificationManager.notify(R.string.select_keyboard_layout_notification_title,
+                    notification);
+            mKeyboardLayoutNotificationShown = true;
+        }
+    }
+
+    // Must be called on handler.
+    private void hideMissingKeyboardLayoutNotification() {
+        if (mKeyboardLayoutNotificationShown) {
+            mKeyboardLayoutNotificationShown = false;
+            mNotificationManager.cancel(R.string.select_keyboard_layout_notification_title);
+        }
+    }
+
+    // Must be called on handler.
+    private void updateKeyboardLayouts() {
+        // Scan all input devices state for keyboard layouts that have been uninstalled.
+        final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
+        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+            @Override
+            public void visitKeyboardLayout(Resources resources,
+                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+                availableKeyboardLayouts.add(descriptor);
+            }
+        });
+        synchronized (mDataStore) {
+            try {
+                mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+
+        // Reload keyboard layouts.
+        reloadKeyboardLayouts();
+    }
+
+    private static boolean isFullKeyboard(InputDevice inputDevice) {
+        return !inputDevice.isVirtual()
+                && (inputDevice.getSources() & InputDevice.SOURCE_KEYBOARD) != 0
+                && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC;
+    }
+
+    private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices,
+            String descriptor) {
+        final int numDevices = inputDevices.length;
+        for (int i = 0; i < numDevices; i++) {
+            final InputDevice inputDevice = inputDevices[i];
+            if (inputDevice.getDescriptor().equals(descriptor)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override // Binder call
@@ -597,8 +719,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 +736,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 +785,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 +821,7 @@
                                         receiver.packageName, receiver.name, name);
                                 if (keyboardName == null || name.equals(keyboardName)) {
                                     visitor.visitKeyboardLayout(resources, descriptor,
-                                            label, keyboardLayoutResId);
+                                            label, collection, keyboardLayoutResId);
                                 }
                             }
                         } finally {
@@ -718,43 +843,147 @@
     }
 
     @Override // Binder call
-    public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
+    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         synchronized (mDataStore) {
-            return mDataStore.getKeyboardLayout(inputDeviceDescriptor);
+            return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
         }
     }
 
     @Override // Binder call
-    public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
             String keyboardLayoutDescriptor) {
         if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
-                "setKeyboardLayoutForInputDevice()")) {
+                "setCurrentKeyboardLayoutForInputDevice()")) {
             throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
         }
-
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
 
-        final boolean changed;
         synchronized (mDataStore) {
             try {
-                changed = mDataStore.setKeyboardLayout(
-                        inputDeviceDescriptor, keyboardLayoutDescriptor);
+                if (mDataStore.setCurrentKeyboardLayout(
+                        inputDeviceDescriptor, keyboardLayoutDescriptor)) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
             } finally {
                 mDataStore.saveIfNeeded();
             }
         }
+    }
 
-        if (changed) {
-            if (DEBUG) {
-                Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
+    @Override // Binder call
+    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            return mDataStore.getKeyboardLayouts(inputDeviceDescriptor);
+        }
+    }
+
+    @Override // Binder call
+    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+                "addKeyboardLayoutForInputDevice()")) {
+            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+                if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor)
+                        && !Objects.equal(oldLayout,
+                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
             }
-            reloadKeyboardLayouts();
+        }
+    }
+
+    @Override // Binder call
+    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+                "removeKeyboardLayoutForInputDevice()")) {
+            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (keyboardLayoutDescriptor == null) {
+            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+                if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor,
+                        keyboardLayoutDescriptor)
+                        && !Objects.equal(oldLayout,
+                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+    }
+
+    public void switchKeyboardLayout(int deviceId, int direction) {
+        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
+    }
+
+    // Must be called on handler.
+    private void handleSwitchKeyboardLayout(int deviceId, int direction) {
+        final InputDevice device = getInputDevice(deviceId);
+        final String inputDeviceDescriptor = device.getDescriptor();
+        if (device != null) {
+            final boolean changed;
+            final String keyboardLayoutDescriptor;
+            synchronized (mDataStore) {
+                try {
+                    changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction);
+                    keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
+                            inputDeviceDescriptor);
+                } finally {
+                    mDataStore.saveIfNeeded();
+                }
+            }
+
+            if (changed) {
+                if (mSwitchedKeyboardLayoutToast != null) {
+                    mSwitchedKeyboardLayoutToast.cancel();
+                    mSwitchedKeyboardLayoutToast = null;
+                }
+                if (keyboardLayoutDescriptor != null) {
+                    KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
+                    if (keyboardLayout != null) {
+                        mSwitchedKeyboardLayoutToast = Toast.makeText(
+                                mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
+                        mSwitchedKeyboardLayoutToast.show();
+                    }
+                }
+
+                reloadKeyboardLayouts();
+            }
         }
     }
 
@@ -976,12 +1205,13 @@
     // Native callback.
     private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
         synchronized (mInputDevicesLock) {
-            mInputDevices = inputDevices;
-
             if (!mInputDevicesChangedPending) {
                 mInputDevicesChangedPending = true;
-                mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED);
+                mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
+                        mInputDevices).sendToTarget();
             }
+
+            mInputDevices = inputDevices;
         }
     }
 
@@ -1130,7 +1360,8 @@
             return null;
         }
 
-        String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+        String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
+                inputDeviceDescriptor);
         if (keyboardLayoutDescriptor == null) {
             return null;
         }
@@ -1139,7 +1370,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(
@@ -1201,7 +1432,19 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_DELIVER_INPUT_DEVICES_CHANGED:
-                    deliverInputDevicesChanged();
+                    deliverInputDevicesChanged((InputDevice[])msg.obj);
+                    break;
+                case MSG_SWITCH_KEYBOARD_LAYOUT:
+                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+                    break;
+                case MSG_RELOAD_KEYBOARD_LAYOUTS:
+                    reloadKeyboardLayouts();
+                    break;
+                case MSG_UPDATE_KEYBOARD_LAYOUTS:
+                    updateKeyboardLayouts();
+                    break;
+                case MSG_RELOAD_DEVICE_ALIASES:
+                    reloadDeviceAliases();
                     break;
             }
         }
@@ -1262,7 +1505,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 {
@@ -1314,186 +1557,4 @@
             onVibratorTokenDied(this);
         }
     }
-
-    /**
-     * Manages persistent state recorded by the input manager service as an XML file.
-     * Caller must acquire lock on the data store before accessing it.
-     *
-     * File format:
-     * <code>
-     * &lt;input-mananger-state>
-     *   &lt;input-devices>
-     *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
-     *   &gt;input-devices>
-     * &gt;/input-manager-state>
-     * </code>
-     */
-    private static final class PersistentDataStore {
-        // Input device state by descriptor.
-        private final HashMap<String, InputDeviceState> mInputDevices =
-                new HashMap<String, InputDeviceState>();
-        private final AtomicFile mAtomicFile;
-
-        // True if the data has been loaded.
-        private boolean mLoaded;
-
-        // True if there are changes to be saved.
-        private boolean mDirty;
-
-        public PersistentDataStore() {
-            mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
-        }
-
-        public void saveIfNeeded() {
-            if (mDirty) {
-                save();
-                mDirty = false;
-            }
-        }
-
-        public String getKeyboardLayout(String inputDeviceDescriptor) {
-            InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
-            return state != null ? state.keyboardLayoutDescriptor : null;
-        }
-
-        public boolean setKeyboardLayout(String inputDeviceDescriptor,
-                String keyboardLayoutDescriptor) {
-            InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
-            if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) {
-                state.keyboardLayoutDescriptor = keyboardLayoutDescriptor;
-                setDirty();
-                return true;
-            }
-            return false;
-        }
-
-        private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
-                boolean createIfAbsent) {
-            loadIfNeeded();
-            InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
-            if (state == null && createIfAbsent) {
-                state = new InputDeviceState();
-                mInputDevices.put(inputDeviceDescriptor, state);
-                setDirty();
-            }
-            return state;
-        }
-
-        private void loadIfNeeded() {
-            if (!mLoaded) {
-                load();
-                mLoaded = true;
-            }
-        }
-
-        private void setDirty() {
-            mDirty = true;
-        }
-
-        private void clearState() {
-            mInputDevices.clear();
-        }
-
-        private void load() {
-            clearState();
-
-            final InputStream is;
-            try {
-                is = mAtomicFile.openRead();
-            } catch (FileNotFoundException ex) {
-                return;
-            }
-
-            XmlPullParser parser;
-            try {
-                parser = Xml.newPullParser();
-                parser.setInput(new BufferedInputStream(is), null);
-                loadFromXml(parser);
-            } catch (IOException ex) {
-                Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
-                clearState();
-            } catch (XmlPullParserException ex) {
-                Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
-                clearState();
-            } finally {
-                IoUtils.closeQuietly(is);
-            }
-        }
-
-        private void save() {
-            final FileOutputStream os;
-            try {
-                os = mAtomicFile.startWrite();
-                boolean success = false;
-                try {
-                    XmlSerializer serializer = new FastXmlSerializer();
-                    serializer.setOutput(new BufferedOutputStream(os), "utf-8");
-                    saveToXml(serializer);
-                    serializer.flush();
-                    success = true;
-                } finally {
-                    if (success) {
-                        mAtomicFile.finishWrite(os);
-                    } else {
-                        mAtomicFile.failWrite(os);
-                    }
-                }
-            } catch (IOException ex) {
-                Slog.w(TAG, "Failed to save input manager persistent store data.", ex);
-            }
-        }
-
-        private void loadFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            XmlUtils.beginDocument(parser, "input-manager-state");
-            final int outerDepth = parser.getDepth();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals("input-devices")) {
-                    loadInputDevicesFromXml(parser);
-                }
-            }
-        }
-
-        private void loadInputDevicesFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals("input-device")) {
-                    String descriptor = parser.getAttributeValue(null, "descriptor");
-                    if (descriptor == null) {
-                        throw new XmlPullParserException(
-                                "Missing descriptor attribute on input-device");
-                    }
-                    InputDeviceState state = new InputDeviceState();
-                    state.keyboardLayoutDescriptor =
-                            parser.getAttributeValue(null, "keyboard-layout");
-                    mInputDevices.put(descriptor, state);
-                }
-            }
-        }
-
-        private void saveToXml(XmlSerializer serializer) throws IOException {
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startTag(null, "input-manager-state");
-            serializer.startTag(null, "input-devices");
-            for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
-                final String descriptor = entry.getKey();
-                final InputDeviceState state = entry.getValue();
-                serializer.startTag(null, "input-device");
-                serializer.attribute(null, "descriptor", descriptor);
-                if (state.keyboardLayoutDescriptor != null) {
-                    serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor);
-                }
-                serializer.endTag(null, "input-device");
-            }
-            serializer.endTag(null, "input-devices");
-            serializer.endTag(null, "input-manager-state");
-            serializer.endDocument();
-        }
-    }
-
-    private static final class InputDeviceState {
-        public String keyboardLayoutDescriptor;
-    }
 }
diff --git a/services/java/com/android/server/input/PersistentDataStore.java b/services/java/com/android/server/input/PersistentDataStore.java
new file mode 100644
index 0000000..fbe3e8b
--- /dev/null
+++ b/services/java/com/android/server/input/PersistentDataStore.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.util.Slog;
+import android.util.Xml;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
+
+/**
+ * Manages persistent state recorded by the input manager service as an XML file.
+ * Caller must acquire lock on the data store before accessing it.
+ *
+ * File format:
+ * <code>
+ * &lt;input-mananger-state>
+ *   &lt;input-devices>
+ *     &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
+ *   &gt;input-devices>
+ * &gt;/input-manager-state>
+ * </code>
+ */
+final class PersistentDataStore {
+    static final String TAG = "InputManager";
+
+    // Input device state by descriptor.
+    private final HashMap<String, InputDeviceState> mInputDevices =
+            new HashMap<String, InputDeviceState>();
+    private final AtomicFile mAtomicFile;
+
+    // True if the data has been loaded.
+    private boolean mLoaded;
+
+    // True if there are changes to be saved.
+    private boolean mDirty;
+
+    public PersistentDataStore() {
+        mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
+    }
+
+    public void saveIfNeeded() {
+        if (mDirty) {
+            save();
+            mDirty = false;
+        }
+    }
+
+    public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        return state != null ? state.getCurrentKeyboardLayout() : null;
+    }
+
+    public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public String[] getKeyboardLayouts(String inputDeviceDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state == null) {
+            return (String[])ArrayUtils.emptyArray(String.class);
+        }
+        return state.getKeyboardLayouts();
+    }
+
+    public boolean addKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeKeyboardLayout(String inputDeviceDescriptor,
+            String keyboardLayoutDescriptor) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+        if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state != null && state.switchKeyboardLayout(direction)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
+        boolean changed = false;
+        for (InputDeviceState state : mInputDevices.values()) {
+            if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) {
+                changed = true;
+            }
+        }
+        if (changed) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
+            boolean createIfAbsent) {
+        loadIfNeeded();
+        InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
+        if (state == null && createIfAbsent) {
+            state = new InputDeviceState();
+            mInputDevices.put(inputDeviceDescriptor, state);
+            setDirty();
+        }
+        return state;
+    }
+
+    private void loadIfNeeded() {
+        if (!mLoaded) {
+            load();
+            mLoaded = true;
+        }
+    }
+
+    private void setDirty() {
+        mDirty = true;
+    }
+
+    private void clearState() {
+        mInputDevices.clear();
+    }
+
+    private void load() {
+        clearState();
+
+        final InputStream is;
+        try {
+            is = mAtomicFile.openRead();
+        } catch (FileNotFoundException ex) {
+            return;
+        }
+
+        XmlPullParser parser;
+        try {
+            parser = Xml.newPullParser();
+            parser.setInput(new BufferedInputStream(is), null);
+            loadFromXml(parser);
+        } catch (IOException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
+            clearState();
+        } catch (XmlPullParserException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex);
+            clearState();
+        } finally {
+            IoUtils.closeQuietly(is);
+        }
+    }
+
+    private void save() {
+        final FileOutputStream os;
+        try {
+            os = mAtomicFile.startWrite();
+            boolean success = false;
+            try {
+                XmlSerializer serializer = new FastXmlSerializer();
+                serializer.setOutput(new BufferedOutputStream(os), "utf-8");
+                saveToXml(serializer);
+                serializer.flush();
+                success = true;
+            } finally {
+                if (success) {
+                    mAtomicFile.finishWrite(os);
+                } else {
+                    mAtomicFile.failWrite(os);
+                }
+            }
+        } catch (IOException ex) {
+            Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex);
+        }
+    }
+
+    private void loadFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        XmlUtils.beginDocument(parser, "input-manager-state");
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals("input-devices")) {
+                loadInputDevicesFromXml(parser);
+            }
+        }
+    }
+
+    private void loadInputDevicesFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals("input-device")) {
+                String descriptor = parser.getAttributeValue(null, "descriptor");
+                if (descriptor == null) {
+                    throw new XmlPullParserException(
+                            "Missing descriptor attribute on input-device.");
+                }
+                if (mInputDevices.containsKey(descriptor)) {
+                    throw new XmlPullParserException("Found duplicate input device.");
+                }
+
+                InputDeviceState state = new InputDeviceState();
+                state.loadFromXml(parser);
+                mInputDevices.put(descriptor, state);
+            }
+        }
+    }
+
+    private void saveToXml(XmlSerializer serializer) throws IOException {
+        serializer.startDocument(null, true);
+        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+        serializer.startTag(null, "input-manager-state");
+        serializer.startTag(null, "input-devices");
+        for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
+            final String descriptor = entry.getKey();
+            final InputDeviceState state = entry.getValue();
+            serializer.startTag(null, "input-device");
+            serializer.attribute(null, "descriptor", descriptor);
+            state.saveToXml(serializer);
+            serializer.endTag(null, "input-device");
+        }
+        serializer.endTag(null, "input-devices");
+        serializer.endTag(null, "input-manager-state");
+        serializer.endDocument();
+    }
+
+    private static final class InputDeviceState {
+        private String mCurrentKeyboardLayout;
+        private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
+
+        public String getCurrentKeyboardLayout() {
+            return mCurrentKeyboardLayout;
+        }
+
+        public boolean setCurrentKeyboardLayout(String keyboardLayout) {
+            if (Objects.equal(mCurrentKeyboardLayout, keyboardLayout)) {
+                return false;
+            }
+            addKeyboardLayout(keyboardLayout);
+            mCurrentKeyboardLayout = keyboardLayout;
+            return true;
+        }
+
+        public String[] getKeyboardLayouts() {
+            if (mKeyboardLayouts.isEmpty()) {
+                return (String[])ArrayUtils.emptyArray(String.class);
+            }
+            return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
+        }
+
+        public boolean addKeyboardLayout(String keyboardLayout) {
+            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+            if (index >= 0) {
+                return false;
+            }
+            mKeyboardLayouts.add(-index - 1, keyboardLayout);
+            if (mCurrentKeyboardLayout == null) {
+                mCurrentKeyboardLayout = keyboardLayout;
+            }
+            return true;
+        }
+
+        public boolean removeKeyboardLayout(String keyboardLayout) {
+            int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+            if (index < 0) {
+                return false;
+            }
+            mKeyboardLayouts.remove(index);
+            updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
+            return true;
+        }
+
+        private void updateCurrentKeyboardLayoutIfRemoved(
+                String removedKeyboardLayout, int removedIndex) {
+            if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) {
+                if (!mKeyboardLayouts.isEmpty()) {
+                    int index = removedIndex;
+                    if (index == mKeyboardLayouts.size()) {
+                        index = 0;
+                    }
+                    mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
+                } else {
+                    mCurrentKeyboardLayout = null;
+                }
+            }
+        }
+
+        public boolean switchKeyboardLayout(int direction) {
+            final int size = mKeyboardLayouts.size();
+            if (size < 2) {
+                return false;
+            }
+            int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout);
+            assert index >= 0;
+            if (direction > 0) {
+                index = (index + 1) % size;
+            } else {
+                index = (index + size - 1) % size;
+            }
+            mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
+            return true;
+        }
+
+        public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
+            boolean changed = false;
+            for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
+                String keyboardLayout = mKeyboardLayouts.get(i);
+                if (!availableKeyboardLayouts.contains(keyboardLayout)) {
+                    Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
+                    mKeyboardLayouts.remove(i);
+                    updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
+                    changed = true;
+                }
+            }
+            return changed;
+        }
+
+        public void loadFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (parser.getName().equals("keyboard-layout")) {
+                    String descriptor = parser.getAttributeValue(null, "descriptor");
+                    if (descriptor == null) {
+                        throw new XmlPullParserException(
+                                "Missing descriptor attribute on keyboard-layout.");
+                    }
+                    String current = parser.getAttributeValue(null, "current");
+                    if (mKeyboardLayouts.contains(descriptor)) {
+                        throw new XmlPullParserException(
+                                "Found duplicate keyboard layout.");
+                    }
+
+                    mKeyboardLayouts.add(descriptor);
+                    if (current != null && current.equals("true")) {
+                        if (mCurrentKeyboardLayout != null) {
+                            throw new XmlPullParserException(
+                                    "Found multiple current keyboard layouts.");
+                        }
+                        mCurrentKeyboardLayout = descriptor;
+                    }
+                }
+            }
+
+            // Maintain invariant that layouts are sorted.
+            Collections.sort(mKeyboardLayouts);
+
+            // Maintain invariant that there is always a current keyboard layout unless
+            // there are none installed.
+            if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
+                mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
+            }
+        }
+
+        public void saveToXml(XmlSerializer serializer) throws IOException {
+            for (String layout : mKeyboardLayouts) {
+                serializer.startTag(null, "keyboard-layout");
+                serializer.attribute(null, "descriptor", layout);
+                if (layout.equals(mCurrentKeyboardLayout)) {
+                    serializer.attribute(null, "current", "true");
+                }
+                serializer.endTag(null, "keyboard-layout");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java
index c2e475a..9ddf011 100644
--- a/services/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/java/com/android/server/net/NetworkStatsCollection.java
@@ -71,7 +71,7 @@
 
     private HashMap<Key, NetworkStatsHistory> mStats = Maps.newHashMap();
 
-    private long mBucketDuration;
+    private final long mBucketDuration;
 
     private long mStartMillis;
     private long mEndMillis;
@@ -95,6 +95,18 @@
         return mStartMillis;
     }
 
+    /**
+     * Return first atomic bucket in this collection, which is more conservative
+     * than {@link #mStartMillis}.
+     */
+    public long getFirstAtomicBucketMillis() {
+        if (mStartMillis == Long.MAX_VALUE) {
+            return Long.MAX_VALUE;
+        } else {
+            return mStartMillis + mBucketDuration;
+        }
+    }
+
     public long getEndMillis() {
         return mEndMillis;
     }
@@ -121,6 +133,15 @@
      */
     public NetworkStatsHistory getHistory(
             NetworkTemplate template, int uid, int set, int tag, int fields) {
+        return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE);
+    }
+
+    /**
+     * Combine all {@link NetworkStatsHistory} in this collection which match
+     * the requested parameters.
+     */
+    public NetworkStatsHistory getHistory(
+            NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) {
         final NetworkStatsHistory combined = new NetworkStatsHistory(
                 mBucketDuration, estimateBuckets(), fields);
         for (Map.Entry<Key, NetworkStatsHistory> entry : mStats.entrySet()) {
@@ -128,7 +149,7 @@
             final boolean setMatches = set == SET_ALL || key.set == set;
             if (key.uid == uid && setMatches && key.tag == tag
                     && templateMatches(template, key.ident)) {
-                combined.recordEntireHistory(entry.getValue());
+                combined.recordHistory(entry.getValue(), start, end);
             }
         }
         return combined;
@@ -145,6 +166,9 @@
         final NetworkStats.Entry entry = new NetworkStats.Entry();
         NetworkStatsHistory.Entry historyEntry = null;
 
+        // shortcut when we know stats will be empty
+        if (start == end) return stats;
+
         for (Map.Entry<Key, NetworkStatsHistory> mapEntry : mStats.entrySet()) {
             final Key key = mapEntry.getKey();
             if (templateMatches(template, key.ident)) {
@@ -175,8 +199,9 @@
      */
     public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
             long end, NetworkStats.Entry entry) {
-        noteRecordedHistory(start, end, entry.rxBytes + entry.txBytes);
-        findOrCreateHistory(ident, uid, set, tag).recordData(start, end, entry);
+        final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
+        history.recordData(start, end, entry);
+        noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
     }
 
     /**
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 0e93b0a..ba122ec 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -44,6 +44,7 @@
 import static android.provider.Settings.Secure.NETSTATS_DEV_ROTATE_AGE;
 import static android.provider.Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES;
 import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Secure.NETSTATS_REPORT_XT_OVER_DEV;
 import static android.provider.Settings.Secure.NETSTATS_SAMPLE_ENABLED;
 import static android.provider.Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE;
 import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
@@ -177,6 +178,7 @@
         public long getPollInterval();
         public long getTimeCacheMaxAge();
         public boolean getSampleEnabled();
+        public boolean getReportXtOverDev();
 
         public static class Config {
             public final long bucketDuration;
@@ -221,6 +223,8 @@
 
     /** Cached {@link #mDevRecorder} stats. */
     private NetworkStatsCollection mDevStatsCached;
+    /** Cached {@link #mXtRecorder} stats. */
+    private NetworkStatsCollection mXtStatsCached;
 
     /** Current counter sets for each UID. */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
@@ -295,6 +299,7 @@
             // read historical network stats from disk, since policy service
             // might need them right away.
             mDevStatsCached = mDevRecorder.getOrLoadCompleteLocked();
+            mXtStatsCached = mXtRecorder.getOrLoadCompleteLocked();
 
             // bootstrap initial stats to prevent double-counting later
             bootstrapStatsLocked();
@@ -371,6 +376,7 @@
         mUidTagRecorder = null;
 
         mDevStatsCached = null;
+        mXtStatsCached = null;
 
         mSystemReady = false;
     }
@@ -469,12 +475,12 @@
             @Override
             public NetworkStats getSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return mDevStatsCached.getSummary(template, start, end);
+                return internalGetSummaryForNetwork(template, start, end);
             }
 
             @Override
             public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
-                return mDevStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
+                return internalGetHistoryForNetwork(template, fields);
             }
 
             @Override
@@ -507,11 +513,56 @@
         };
     }
 
+    /**
+     * Return network summary, splicing between {@link #mDevStatsCached}
+     * and {@link #mXtStatsCached} when appropriate.
+     */
+    private NetworkStats internalGetSummaryForNetwork(
+            NetworkTemplate template, long start, long end) {
+        if (!mSettings.getReportXtOverDev()) {
+            // shortcut when XT reporting disabled
+            return mDevStatsCached.getSummary(template, start, end);
+        }
+
+        // splice stats between DEV and XT, switching over from DEV to XT at
+        // first atomic bucket.
+        final long firstAtomicBucket = mXtStatsCached.getFirstAtomicBucketMillis();
+        final NetworkStats dev = mDevStatsCached.getSummary(
+                template, Math.min(start, firstAtomicBucket), Math.min(end, firstAtomicBucket));
+        final NetworkStats xt = mXtStatsCached.getSummary(
+                template, Math.max(start, firstAtomicBucket), Math.max(end, firstAtomicBucket));
+
+        xt.combineAllValues(dev);
+        return xt;
+    }
+
+    /**
+     * Return network history, splicing between {@link #mDevStatsCached}
+     * and {@link #mXtStatsCached} when appropriate.
+     */
+    private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields) {
+        if (!mSettings.getReportXtOverDev()) {
+            // shortcut when XT reporting disabled
+            return mDevStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
+        }
+
+        // splice stats between DEV and XT, switching over from DEV to XT at
+        // first atomic bucket.
+        final long firstAtomicBucket = mXtStatsCached.getFirstAtomicBucketMillis();
+        final NetworkStatsHistory dev = mDevStatsCached.getHistory(
+                template, UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, firstAtomicBucket);
+        final NetworkStatsHistory xt = mXtStatsCached.getHistory(
+                template, UID_ALL, SET_ALL, TAG_NONE, fields, firstAtomicBucket, Long.MAX_VALUE);
+
+        xt.recordEntireHistory(dev);
+        return xt;
+    }
+
     @Override
     public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
         assertBandwidthControlEnabled();
-        return mDevStatsCached.getSummary(template, start, end).getTotalBytes();
+        return internalGetSummaryForNetwork(template, start, end).getTotalBytes();
     }
 
     @Override
@@ -1190,6 +1241,10 @@
             return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true);
         }
         @Override
+        public boolean getReportXtOverDev() {
+            return getSecureBoolean(NETSTATS_REPORT_XT_OVER_DEV, true);
+        }
+        @Override
         public Config getDevConfig() {
             return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
                     getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 497ee4b..88f5ec5 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;
 
@@ -520,7 +523,6 @@
                         // Something seriously wrong. Bail out
                         Slog.e(TAG, "Cannot bind to media container service");
                         for (HandlerParams params : mPendingInstalls) {
-                            mPendingInstalls.remove(0);
                             // Indicate service bind error
                             params.serviceError();
                         }
@@ -572,7 +574,6 @@
                         if (!connectToService()) {
                             Slog.e(TAG, "Failed to bind to media container service");
                             for (HandlerParams params : mPendingInstalls) {
-                                mPendingInstalls.remove(0);
                                 // Indicate service bind error
                                 params.serviceError();
                             }
@@ -907,6 +908,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 +1045,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(
@@ -1064,7 +1066,7 @@
             mInstaller.moveFiles();
 
             // Prune any system packages that no longer exist.
-            final List<String> possiblyDeletedSystemApps = new ArrayList<String>();
+            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
             if (!mOnlyCore) {
                 Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                 while (psit.hasNext()) {
@@ -1104,7 +1106,10 @@
                         mInstaller.remove(ps.name, 0);
                         sUserManager.removePackageForAllUsers(ps.name);
                     } else {
-                        possiblyDeletedSystemApps.add(ps.name);
+                        final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
+                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
+                            possiblyDeletedUpdatedSystemApps.add(ps.name);
+                        }
                     }
                 }
             }
@@ -1135,18 +1140,33 @@
                         scanMode, 0);
 
                 /**
-                 * Remove disable package settings for any system apps
-                 * that were removed via an OTA.
+                 * Remove disable package settings for any updated system
+                 * apps that were removed via an OTA. If they're not a
+                 * previously-updated app, remove them completely.
+                 * Otherwise, just revoke their system-level permissions.
                  */
-                for (String deletedAppName : possiblyDeletedSystemApps) {
+                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                     PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
-                    if (deletedPkg != null) {
-                        mSettings.removeDisabledSystemPackageLPw(deletedAppName);
+                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
+
+                    String msg;
+                    if (deletedPkg == null) {
+                        msg = "Updated system package " + deletedAppName
+                                + " no longer exists; wiping its data";
+
+                        mInstaller.remove(deletedAppName, 0);
+                        sUserManager.removePackageForAllUsers(deletedAppName);
+                    } else {
+                        msg = "Updated system app + " + deletedAppName
+                                + " no longer present; removing system privileges for "
+                                + deletedAppName;
+
                         deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
 
                         PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                         deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                     }
+                    reportSettingsProblem(Log.WARN, msg);
                 }
             } else {
                 mAppInstallObserver = null;
@@ -6461,6 +6481,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.
@@ -6499,7 +6524,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));
         }
@@ -8641,6 +8666,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.
@@ -8775,7 +8808,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/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 480992b..97f65ad8 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -269,9 +269,19 @@
                                 mPendingLayoutChanges);
                         }
                         mService.mFocusMayChange = true;
-                    } else if (win.isReadyForDisplay()) {
+                    }
+                    if (win.isReadyForDisplay()) {
                         mForceHiding = true;
                     }
+                    if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                            "Force hide " + mForceHiding
+                            + " hasSurface=" + win.mHasSurface
+                            + " policyVis=" + win.mPolicyVisibility
+                            + " destroying=" + win.mDestroying
+                            + " attHidden=" + win.mAttachedHidden
+                            + " vis=" + win.mViewVisibility
+                            + " hidden=" + win.mRootToken.hidden
+                            + " anim=" + win.mWinAnimator.mAnimation);
                 } else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
                     final boolean changed;
                     if (mForceHiding) {
@@ -538,8 +548,16 @@
         if (mDimAnimator == null) {
             mDimAnimator = new DimAnimator(mService.mFxSession);
         }
-        mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
-                new DimAnimator.Parameters(winAnimator, width, height, target)));
+        // Only set dim params on the highest dimmed layer.
+        final WindowStateAnimator dimWinAnimator = mDimParams == null
+                ? null : mDimParams.mDimWinAnimator;
+        // Don't turn on for an unshown surface, or for any layer but the highest dimmed one.
+        if (winAnimator.mSurfaceShown &&
+                (dimWinAnimator == null || !dimWinAnimator.mSurfaceShown
+                || dimWinAnimator.mAnimLayer < winAnimator.mAnimLayer)) {
+            mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+                    new DimAnimator.Parameters(winAnimator, width, height, target)));
+        }
     }
 
     // TODO(cmautner): Move into Handler
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b3ac6f1..895b5c1 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,10 +1175,11 @@
             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
+                            + " policyVis=" + w.mPolicyVisibility
+                            + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
+                            + " attachHid=" + w.mAttachedHidden
                             + " exiting=" + w.mExiting + " destroying=" + w.mDestroying);
                     if (w.mAppToken != null) {
                         Slog.i(TAG, "  mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested);
@@ -2651,7 +2654,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 +2757,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 +2769,7 @@
                 if (oldVisibility == View.GONE) {
                     winAnimator.mEnterAnimationPending = true;
                 }
-                if (displayed) {
+                if (toBeDisplayed) {
                     if (win.isDrawnLw() && okToDisplay()) {
                         winAnimator.applyEnterAnimationLocked();
                     }
@@ -2792,7 +2795,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 +2805,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 +2821,7 @@
                     Binder.restoreCallingIdentity(origId);
                     return 0;
                 }
-                if (displayed) {
+                if (toBeDisplayed) {
                     focusMayChange = true;
                 }
                 if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2845,11 +2846,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 +2857,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 +2880,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 +2898,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 +2920,7 @@
             }
             configChanged = updateOrientationFromAppTokensLocked(false);
             performLayoutAndPlaceSurfacesLocked();
-            if (displayed && win.mIsWallpaper) {
+            if (toBeDisplayed && win.mIsWallpaper) {
                 updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, false);
             }
             if (win.mAppToken != null) {
@@ -2970,7 +2956,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);
     }
@@ -5023,6 +5009,12 @@
 
     // Called by window manager policy.  Not exposed externally.
     @Override
+    public void switchKeyboardLayout(int deviceId, int direction) {
+        mInputManager.switchKeyboardLayout(deviceId, direction);
+    }
+
+    // Called by window manager policy.  Not exposed externally.
+    @Override
     public void shutdown() {
         ShutdownThread.shutdown(mContext, true);
     }
@@ -5256,9 +5248,14 @@
 
     // TODO: more accounting of which pid(s) turned it on, keep count,
     // only allow disables from pids which have count on, etc.
+    @Override
     public void showStrictModeViolation(boolean on) {
         if (mHeadless) return;
+        mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, 0));
+    }
 
+    private void showStrictModeViolation(int arg) {
+        final boolean on = arg != 0;
         int pid = Binder.getCallingPid();
         synchronized(mWindowMap) {
             // Ignoring requests to enable the red border from clients
@@ -6391,8 +6388,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) {
@@ -6573,6 +6574,16 @@
         sendScreenStatusToClients();
     }
 
+    public IBinder getFocusedWindowClientToken() {
+        synchronized (mWindowMap) {
+            WindowState windowState = getFocusedWindowLocked();
+            if (windowState != null) {
+                return windowState.mClient.asBinder();
+            }
+            return null;
+        }
+    }
+
     private WindowState getFocusedWindow() {
         synchronized (mWindowMap) {
             return getFocusedWindowLocked();
@@ -6626,6 +6637,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();
@@ -6706,6 +6719,7 @@
         public static final int BOOT_TIMEOUT = 23;
         public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
         public static final int BULK_UPDATE_PARAMETERS = 25;
+        public static final int SHOW_STRICT_MODE_VIOLATION = 26;
 
         public static final int ANIMATOR_WHAT_OFFSET = 100000;
         public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
@@ -7171,6 +7185,11 @@
                     break;
                 }
 
+                case SHOW_STRICT_MODE_VIOLATION: {
+                    showStrictModeViolation(msg.arg1);
+                    break;
+                }
+
                 // Animation messages. Move to Window{State}Animator
                 case SET_TRANSPARENT_REGION: {
                     Pair<WindowStateAnimator, Region> pair =
@@ -7228,9 +7247,11 @@
             WindowState imFocus;
             if (idx > 0) {
                 imFocus = mWindows.get(idx-1);
-                //Log.i(TAG, "Desired input method target: " + imFocus);
-                //Log.i(TAG, "Current focus: " + this.mCurrentFocus);
-                //Log.i(TAG, "Last focus: " + this.mLastFocus);
+                if (DEBUG_INPUT_METHOD) {
+                    Slog.i(TAG, "Desired input method target: " + imFocus);
+                    Slog.i(TAG, "Current focus: " + this.mCurrentFocus);
+                    Slog.i(TAG, "Last focus: " + this.mLastFocus);
+                }
                 if (imFocus != null) {
                     // This may be a starting window, in which case we still want
                     // to count it as okay.
@@ -7241,17 +7262,20 @@
                         for (int i=0; i<imFocus.mAppToken.windows.size(); i++) {
                             WindowState w = imFocus.mAppToken.windows.get(i);
                             if (w != imFocus) {
-                                //Log.i(TAG, "Switching to real app window: " + w);
+                                Log.i(TAG, "Switching to real app window: " + w);
                                 imFocus = w;
                                 break;
                             }
                         }
                     }
-                    //Log.i(TAG, "IM target client: " + imFocus.mSession.mClient);
-                    //if (imFocus.mSession.mClient != null) {
-                    //    Log.i(TAG, "IM target client binder: " + imFocus.mSession.mClient.asBinder());
-                    //    Log.i(TAG, "Requesting client binder: " + client.asBinder());
-                    //}
+                    if (DEBUG_INPUT_METHOD) {
+                        Slog.i(TAG, "IM target client: " + imFocus.mSession.mClient);
+                        if (imFocus.mSession.mClient != null) {
+                            Slog.i(TAG, "IM target client binder: "
+                                    + imFocus.mSession.mClient.asBinder());
+                            Slog.i(TAG, "Requesting client binder: " + client.asBinder());
+                        }
+                    }
                     if (imFocus.mSession.mClient != null &&
                             imFocus.mSession.mClient.asBinder() == client.asBinder()) {
                         return true;
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 cba92f3..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;
         }
     }
 
@@ -935,6 +920,59 @@
         mDtDy = mWin.mGlobalScale;
     }
 
+    void updateSurfaceWindowCrop(final boolean recoveringMemory) {
+        final WindowState w = mWin;
+
+        // Need to recompute a new system decor rect each time.
+        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+            // Currently can't do this cropping for scaled windows.  We'll
+            // just keep the crop rect the same as the source surface.
+            w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
+        } else if (w.mLayer >= mService.mSystemDecorLayer) {
+            // Above the decor layer is easy, just use the entire window.
+            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
+                    w.mCompatFrame.height());
+        } else {
+            final Rect decorRect = mService.mSystemDecorRect;
+            // Compute the offset of the window in relation to the decor rect.
+            final int offX = w.mXOffset + w.mFrame.left;
+            final int offY = w.mYOffset + w.mFrame.top;
+            // Initialize the decor rect to the entire frame.
+            w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height());
+            // Intersect with the decor rect, offsetted by window position.
+            w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY,
+                    decorRect.right-offX, decorRect.bottom-offY);
+            // If size compatibility is being applied to the window, the
+            // surface is scaled relative to the screen.  Also apply this
+            // scaling to the crop rect.  We aren't using the standard rect
+            // scale function because we want to round things to make the crop
+            // always round to a larger rect to ensure we don't crop too
+            // much and hide part of the window that should be seen.
+            if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
+                final float scale = w.mInvGlobalScale;
+                w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f);
+                w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f);
+                w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f);
+                w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f);
+            }
+        }
+
+        if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
+            w.mLastSystemDecorRect.set(w.mSystemDecorRect);
+            try {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "CROP " + w.mSystemDecorRect.toShortString(), null);
+                mSurface.setWindowCrop(w.mSystemDecorRect);
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error setting crop surface of " + w
+                        + " crop=" + w.mSystemDecorRect.toShortString(), e);
+                if (!recoveringMemory) {
+                    mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
+                }
+            }
+        }
+    }
+
     void setSurfaceBoundaries(final boolean recoveringMemory) {
         final WindowState w = mWin;
         int width, height;
@@ -1003,54 +1041,7 @@
             }
         }
 
-        // Need to recompute a new system decor rect each time.
-        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-            // Currently can't do this cropping for scaled windows.  We'll
-            // just keep the crop rect the same as the source surface.
-            w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
-        } else if (w.mLayer >= mService.mSystemDecorLayer) {
-            // Above the decor layer is easy, just use the entire window.
-            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
-                    w.mCompatFrame.height());
-        } else {
-            final Rect decorRect = mService.mSystemDecorRect;
-            // Compute the offset of the window in relation to the decor rect.
-            final int offX = w.mXOffset + w.mFrame.left;
-            final int offY = w.mYOffset + w.mFrame.top;
-            // Initialize the decor rect to the entire frame.
-            w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height());
-            // Intersect with the decor rect, offsetted by window position.
-            w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY,
-                    decorRect.right-offX, decorRect.bottom-offY);
-            // If size compatibility is being applied to the window, the
-            // surface is scaled relative to the screen.  Also apply this
-            // scaling to the crop rect.  We aren't using the standard rect
-            // scale function because we want to round things to make the crop
-            // always round to a larger rect to ensure we don't crop too
-            // much and hide part of the window that should be seen.
-            if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
-                final float scale = w.mInvGlobalScale;
-                w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f);
-                w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f);
-                w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f);
-                w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f);
-            }
-        }
-
-        if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
-            w.mLastSystemDecorRect.set(w.mSystemDecorRect);
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "CROP " + w.mSystemDecorRect.toShortString(), null);
-                mSurface.setWindowCrop(w.mSystemDecorRect);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error setting crop surface of " + w
-                        + " crop=" + w.mSystemDecorRect.toShortString(), e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
-                }
-            }
-        }
+        updateSurfaceWindowCrop(recoveringMemory);
     }
 
     public void prepareSurfaceLocked(final boolean recoveringMemory) {
@@ -1141,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;
         }
@@ -1181,17 +1172,31 @@
     }
 
     void setWallpaperOffset(int left, int top) {
+        mSurfaceX = left;
+        mSurfaceY = top;
+        if (mAnimating) {
+            // If this window (or its app token) is animating, then the position
+            // of the surface will be re-computed on the next animation frame.
+            // We can't poke it directly here because it depends on whatever
+            // transformation is being applied by the animation.
+            return;
+        }
+        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                ">>> OPEN TRANSACTION setWallpaperOffset");
         Surface.openTransaction();
         try {
-            mSurfaceX = left;
-            mSurfaceY = top;
-            mSurface.setPosition(left, top);
-            mSurface.setWindowCrop(null);
+            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
+                    "POS " + left + ", " + top, null);
+            mSurface.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
+            updateSurfaceWindowCrop(false);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Error positioning surface of " + mWin
                     + " pos=(" + left + "," + top + ")", e);
+        } finally {
+            Surface.closeTransaction();
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                    "<<< CLOSE TRANSACTION setWallpaperOffset");
         }
-        Surface.closeTransaction();
     }
 
     // This must be called while inside a transaction.
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index a47f8fd..0904bdd 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -83,11 +83,9 @@
 }
 
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
-    if (gPowerModule) {
-        // Tell the power HAL when user activity occurs.
-        if (gPowerModule->powerHint) {
-            gPowerModule->powerHint(gPowerModule, POWER_HINT_INTERACTION, NULL);
-        }
+    // Tell the power HAL when user activity occurs.
+    if (gPowerModule && gPowerModule->powerHint) {
+        gPowerModule->powerHint(gPowerModule, POWER_HINT_INTERACTION, NULL);
     }
 
     if (gPowerManagerServiceObj) {
@@ -131,16 +129,11 @@
 
     status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID,
             (hw_module_t const**)&gPowerModule);
-    if (err) {
-        String8 msg;
-        msg.appendFormat("Couldn't load %s module (%s)",
-                POWER_HARDWARE_MODULE_ID, strerror(-err));
-        ALOGE("%s", msg.string());
-        jniThrowRuntimeException(env, msg.string());
-        return;
+    if (!err) {
+        gPowerModule->init(gPowerModule);
+    } else {
+        ALOGE("Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
     }
-
-    gPowerModule->init(gPowerModule);
 }
 
 static void nativeSetPowerState(JNIEnv* env,
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cc3c328..3f66de6 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -37,13 +37,15 @@
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService">
+        <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
           <intent-filter>
             <action android:name="android.accessibilityservice.AccessibilityService"/>
           </intent-filter>
         </service>
 
-        <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService">
+        <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
           <intent-filter>
             <action android:name="android.accessibilityservice.AccessibilityService"/>
           </intent-filter>
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 332d198..cdc4d78 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -777,10 +777,80 @@
 
     }
 
+    public void testReportXtOverDev() throws Exception {
+        // bring mobile network online
+        expectCurrentTime();
+        expectDefaultSettings();
+        expectNetworkState(buildMobile3gState(IMSI_1));
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
+        verifyAndReset();
+
+        // create some traffic, but only for DEV, and across 1.5 buckets
+        incrementCurrentTime(90 * MINUTE_IN_MILLIS);
+        expectCurrentTime();
+        expectDefaultSettings();
+        expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 6000L, 60L, 3000L, 30L));
+        expectNetworkStatsSummaryXt(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+        // verify service recorded history:
+        // 4000(dev) + 2000(dev)
+        assertNetworkTotal(sTemplateImsi1, 6000L, 60L, 3000L, 30L, 0);
+        verifyAndReset();
+
+        // create traffic on both DEV and XT, across two buckets
+        incrementCurrentTime(2 * HOUR_IN_MILLIS);
+        expectCurrentTime();
+        expectDefaultSettings();
+        expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 6004L, 64L, 3004L, 34L));
+        expectNetworkStatsSummaryXt(new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 10240L, 0L, 0L, 0L));
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+        // verify that we switching reporting at the first atomic XT bucket,
+        // which should give us:
+        // 4000(dev) + 2000(dev) + 1(dev) + 5120(xt) + 2560(xt)
+        assertNetworkTotal(sTemplateImsi1, 13681L, 61L, 3001L, 31L, 0);
+
+        // also test pure-DEV and pure-XT ranges
+        assertNetworkTotal(sTemplateImsi1, startTimeMillis(),
+                startTimeMillis() + 2 * HOUR_IN_MILLIS, 6001L, 61L, 3001L, 31L, 0);
+        assertNetworkTotal(sTemplateImsi1, startTimeMillis() + 2 * HOUR_IN_MILLIS,
+                startTimeMillis() + 4 * HOUR_IN_MILLIS, 7680L, 0L, 0L, 0L, 0);
+
+        verifyAndReset();
+    }
+
     private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
             long txBytes, long txPackets, int operations) throws Exception {
+        assertNetworkTotal(template, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
+    }
+
+    private void assertNetworkTotal(NetworkTemplate template, long start, long end, long rxBytes,
+            long rxPackets, long txBytes, long txPackets, int operations) throws Exception {
+        // verify history API
         final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELD_ALL);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+        assertValues(history, start, end, rxBytes, rxPackets, txBytes, txPackets, operations);
+
+        // verify summary API
+        final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
+        assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes,
                 txPackets, operations);
     }
 
@@ -791,10 +861,17 @@
 
     private void assertUidTotal(NetworkTemplate template, int uid, int set, long rxBytes,
             long rxPackets, long txBytes, long txPackets, int operations) throws Exception {
+        // verify history API
         final NetworkStatsHistory history = mSession.getHistoryForUid(
                 template, uid, set, TAG_NONE, FIELD_ALL);
         assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
                 txPackets, operations);
+
+        // verify summary API
+        final NetworkStats stats = mSession.getSummaryForAllUid(
+                template, Long.MIN_VALUE, Long.MAX_VALUE, false);
+        assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
+                operations);
     }
 
     private void expectSystemReady() throws Exception {
@@ -819,7 +896,15 @@
     }
 
     private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
+        expectNetworkStatsSummaryDev(summary);
+        expectNetworkStatsSummaryXt(summary);
+    }
+
+    private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
         expect(mNetManager.getNetworkStatsSummaryDev()).andReturn(summary).atLeastOnce();
+    }
+
+    private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception {
         expect(mNetManager.getNetworkStatsSummaryXt()).andReturn(summary).atLeastOnce();
     }
 
@@ -847,6 +932,7 @@
         expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
         expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
         expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes();
+        expect(mSettings.getReportXtOverDev()).andReturn(true).anyTimes();
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
         expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
@@ -885,8 +971,20 @@
 
     private static void assertValues(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
-        final int i = stats.findIndex(iface, uid, set, tag);
-        final NetworkStats.Entry entry = stats.getValues(i, null);
+        final NetworkStats.Entry entry = new NetworkStats.Entry();
+        if (set == SET_DEFAULT || set == SET_ALL) {
+            final int i = stats.findIndex(iface, uid, SET_DEFAULT, tag);
+            if (i != -1) {
+                entry.add(stats.getValues(i, null));
+            }
+        }
+        if (set == SET_FOREGROUND || set == SET_ALL) {
+            final int i = stats.findIndex(iface, uid, SET_FOREGROUND, tag);
+            if (i != -1) {
+                entry.add(stats.getValues(i, null));
+            }
+        }
+
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
index 8634821..1a6c289 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.net;
 
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import android.content.res.Resources;
@@ -47,6 +51,14 @@
     private static final String TEST_FILE = "test.bin";
     private static final String TEST_IMSI = "310260000000000";
 
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // ignore any device overlay while testing
+        NetworkTemplate.forceAllNetworkTypes();
+    }
+
     public void testReadLegacyNetwork() throws Exception {
         final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
         stageFile(R.raw.netstats_v1, testFile);
@@ -125,6 +137,20 @@
                 77017831L, 100995L, 35436758L, 92344L);
     }
 
+    public void testStartEndAtomicBuckets() throws Exception {
+        final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
+
+        // record empty data straddling between buckets
+        final NetworkStats.Entry entry = new NetworkStats.Entry();
+        entry.rxBytes = 32;
+        collection.recordData(null, UID_ALL, SET_DEFAULT, TAG_NONE, 30 * MINUTE_IN_MILLIS,
+                90 * MINUTE_IN_MILLIS, entry);
+
+        // assert that we report boundary in atomic buckets
+        assertEquals(0, collection.getStartMillis());
+        assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
+    }
+
     /**
      * Copy a {@link Resources#openRawResource(int)} into {@link File} for
      * testing purposes.
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 7c70a7e..0033ceb 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -1256,6 +1256,7 @@
         pw.println(" mRetryOverride=" + mRetryOverride);
         pw.println(" mRefCount=" + mRefCount);
         pw.println(" userData=" + userData);
+        if (mRetryMgr != null) pw.println(" " + mRetryMgr);
         pw.flush();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index d738d7b..fcf2f92 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -579,10 +579,14 @@
             mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
         }
 
-        // Call onReady only when SIM or RUIM card becomes ready (not NV)
+        // Call onReady Record(s) on the IccCard becomes ready (not NV)
         if (oldState != State.READY && newState == State.READY &&
                 (is3gpp || isSubscriptionFromIccCard)) {
-            mIccFileHandler.setAid(getAid());
+            if (!(mIccFileHandler instanceof CdmaLteUiccFileHandler)) {
+                // CdmaLteUicc File Handler deals with both USIM and CSIM.
+                // Do not lock onto one AID for now.
+                mIccFileHandler.setAid(getAid());
+            }
             mIccRecords.onReady();
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java
index 29bd104..ae451b9 100644
--- a/telephony/java/com/android/internal/telephony/RetryManager.java
+++ b/telephony/java/com/android/internal/telephony/RetryManager.java
@@ -74,7 +74,8 @@
  */
 public class RetryManager {
     static public final String LOG_TAG = "RetryManager";
-    static public final boolean DBG = false;
+    static public final boolean DBG = true;
+    static public final boolean VDBG = false;
 
     /**
      * Retry record with times in milli-seconds
@@ -107,9 +108,20 @@
     /** Random number generator */
     private Random rng = new Random();
 
+    private String mConfig;
+
     /** Constructor */
     public RetryManager() {
-        if (DBG) log("constructor");
+        if (VDBG) log("constructor");
+    }
+
+    public String toString() {
+        String ret = "RetryManager: forever=" + mRetryForever + ", maxRetry=" + mMaxRetryCount +
+                ", retry=" + mRetryCount + ",\n    " + mConfig;
+        for (RetryRec r : mRetryArray) {
+            ret += "\n    " + r.mDelayTime + ":" + r.mRandomizationTime;
+        }
+        return ret;
     }
 
     /**
@@ -127,7 +139,7 @@
     public boolean configure(int maxRetryCount, int retryTime, int randomizationTime) {
         Pair<Boolean, Integer> value;
 
-        if (DBG) log("configure: " + maxRetryCount + ", " + retryTime + "," + randomizationTime);
+        if (VDBG) log("configure: " + maxRetryCount + ", " + retryTime + "," + randomizationTime);
 
         if (!validateNonNegativeInt("maxRetryCount", maxRetryCount)) {
             return false;
@@ -161,12 +173,13 @@
         if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
             configStr = configStr.substring(1, configStr.length()-1);
         }
-        if (DBG) log("configure: '" + configStr + "'");
+        if (VDBG) log("configure: '" + configStr + "'");
+        mConfig = configStr;
 
         if (!TextUtils.isEmpty(configStr)) {
             int defaultRandomization = 0;
 
-            if (DBG) log("configure: not empty");
+            if (VDBG) log("configure: not empty");
 
             mMaxRetryCount = 0;
             resetRetryCount();
@@ -174,14 +187,14 @@
 
             String strArray[] = configStr.split(",");
             for (int i = 0; i < strArray.length; i++) {
-                if (DBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
+                if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
                 Pair<Boolean, Integer> value;
                 String splitStr[] = strArray[i].split("=", 2);
                 splitStr[0] = splitStr[0].trim();
-                if (DBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
+                if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
                 if (splitStr.length > 1) {
                     splitStr[1] = splitStr[1].trim();
-                    if (DBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+                    if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
                     if (TextUtils.equals(splitStr[0], "default_randomization")) {
                         value = parseNonNegativeInt(splitStr[0], splitStr[1]);
                         if (!value.first) return false;
@@ -214,7 +227,7 @@
                     // Check if optional randomization value present
                     if (splitStr.length > 1) {
                         splitStr[1] = splitStr[1].trim();
-                        if (DBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+                        if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
                         value = parseNonNegativeInt("randomizationTime", splitStr[1]);
                         if (!value.first) return false;
                         rr.mRandomizationTime = value.second;
@@ -226,12 +239,12 @@
             }
             if (mRetryArray.size() > mMaxRetryCount) {
                 mMaxRetryCount = mRetryArray.size();
-                if (DBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
+                if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
             }
-            if (DBG) log("configure: true");
+            if (VDBG) log("configure: true");
             return true;
         } else {
-            if (DBG) log("configure: false it's empty");
+            if (VDBG) log("configure: false it's empty");
             return false;
         }
     }
@@ -349,7 +362,7 @@
             Log.e(LOG_TAG, name + " bad value: " + stringValue, e);
             retVal = new Pair<Boolean, Integer>(false, 0);
         }
-        if (DBG) log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
+        if (VDBG) log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
                     + retVal.first + ", " + retVal.second);
         return retVal;
     }
@@ -369,7 +382,7 @@
         } else {
             retVal = true;
         }
-        if (DBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
+        if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
         return retVal;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 69dd666..e4cfb23 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -146,6 +146,7 @@
         "ci", // Cote d'Ivoire
         "eh", // Western Sahara
         "fo", // Faroe Islands, Denmark
+        "gb", // United Kingdom of Great Britain and Northern Ireland
         "gh", // Ghana
         "gm", // Gambia
         "gn", // Guinea
@@ -161,7 +162,6 @@
         "sn", // Senegal
         "st", // Sao Tome and Principe
         "tg", // Togo
-        "uk", // U.K
     };
 
     /** Reason for registration denial. */
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);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index e4287c0..25cd112 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -293,8 +293,15 @@
                         EVENT_RUIM_RECORDS_LOADED, null);
                 mNeedToRegForRuimLoaded = false;
             }
-            if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
-            getSubscriptionInfoAndStartPollingThreads();
+
+            if (phone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+                // Subscription will be read from SIM I/O
+                if (DBG) log("Receive EVENT_RUIM_READY");
+                pollState();
+            } else {
+                if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
+                getSubscriptionInfoAndStartPollingThreads();
+            }
             phone.prepareEri();
             break;
 
@@ -878,19 +885,22 @@
             // For NITZ string without time zone,
             // need adjust time to reflect default time zone setting
             zone = TimeZone.getDefault();
-            long ctm = System.currentTimeMillis();
-            long tzOffset = zone.getOffset(ctm);
-            if (DBG) {
-                log("fixTimeZone: tzOffset=" + tzOffset + " ltod=" + TimeUtils.logTimeOfDay(ctm));
-            }
-            if (getAutoTime()) {
-                long adj = ctm - tzOffset;
-                if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
-                setAndBroadcastNetworkSetTime(adj);
-            } else {
-                // Adjust the saved NITZ time to account for tzOffset.
-                mSavedTime = mSavedTime - tzOffset;
-                if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
+            if (mNeedFixZone) {
+                long ctm = System.currentTimeMillis();
+                long tzOffset = zone.getOffset(ctm);
+                if (DBG) {
+                    log("fixTimeZone: tzOffset=" + tzOffset +
+                            " ltod=" + TimeUtils.logTimeOfDay(ctm));
+                }
+                if (getAutoTime()) {
+                    long adj = ctm - tzOffset;
+                    if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
+                    setAndBroadcastNetworkSetTime(adj);
+                } else {
+                    // Adjust the saved NITZ time to account for tzOffset.
+                    mSavedTime = mSavedTime - tzOffset;
+                    if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
+                }
             }
             if (DBG) log("fixTimeZone: using default TimeZone");
         } else if (isoCountryCode.equals("")) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index e2579e3..f8d09de 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1695,6 +1695,7 @@
 
     @Override
     protected void restartDataStallAlarm() {
+        if (isConnected() == false) return;
         // To be called on screen status change.
         // Do not cancel the alarm if it is set with aggressive timeout.
         int nextAction = getRecoveryAction();
@@ -2141,10 +2142,9 @@
                 // clean slate after call end.
                 resetPollStats();
             }
-        } else {
-            // reset reconnect timer
-            setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
         }
+        // reset reconnect timer
+        setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 1aa17c7..b7569da 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -111,7 +111,7 @@
      * are in. Keep the time zone information from the NITZ string so
      * we can fix the time zone once know the country.
      */
-    private boolean mNeedFixZone = false;
+    private boolean mNeedFixZoneAfterNitz = false;
     private int mZoneOffset;
     private boolean mZoneDst;
     private long mZoneTime;
@@ -906,7 +906,7 @@
                 }
 
                 if (shouldFixTimeZoneNow(phone, operatorNumeric, prevOperatorNumeric,
-                        mNeedFixZone)) {
+                        mNeedFixZoneAfterNitz)) {
                     // If the offset is (0, false) and the timezone property
                     // is set, use the timezone property rather than
                     // GMT.
@@ -917,25 +917,35 @@
                             " iso-cc='" + iso +
                             "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
                     }
+
+                    // "(mZoneOffset == 0) && (mZoneDst == false) &&
+                    //  (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)"
+                    // means that we received a NITZ string telling
+                    // it is in GMT+0 w/ DST time zone
+                    // BUT iso tells is NOT, e.g, a wrong NITZ reporting
+                    // local time w/ 0 offset.
                     if ((mZoneOffset == 0) && (mZoneDst == false) &&
                         (zoneName != null) && (zoneName.length() > 0) &&
                         (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
                         zone = TimeZone.getDefault();
-                        // For NITZ string without timezone,
-                        // need adjust time to reflect default timezone setting
-                        long ctm = System.currentTimeMillis();
-                        long tzOffset = zone.getOffset(ctm);
-                        if (DBG) {
-                            log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
+                        if (mNeedFixZoneAfterNitz) {
+                            // For wrong NITZ reporting local time w/ 0 offset,
+                            // need adjust time to reflect default timezone setting
+                            long ctm = System.currentTimeMillis();
+                            long tzOffset = zone.getOffset(ctm);
+                            if (DBG) {
+                                log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
                                         TimeUtils.logTimeOfDay(ctm));
-                        }
-                        if (getAutoTime()) {
-                            long adj = ctm - tzOffset;
-                            if (DBG) log("pollStateDone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
-                            setAndBroadcastNetworkSetTime(adj);
-                        } else {
-                            // Adjust the saved NITZ time to account for tzOffset.
-                            mSavedTime = mSavedTime - tzOffset;
+                            }
+                            if (getAutoTime()) {
+                                long adj = ctm - tzOffset;
+                                if (DBG) log("pollStateDone: adj ltod=" +
+                                        TimeUtils.logTimeOfDay(adj));
+                                setAndBroadcastNetworkSetTime(adj);
+                            } else {
+                                // Adjust the saved NITZ time to account for tzOffset.
+                                mSavedTime = mSavedTime - tzOffset;
+                            }
                         }
                         if (DBG) log("pollStateDone: using default TimeZone");
                     } else if (iso.equals("")){
@@ -948,7 +958,7 @@
                         if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
                     }
 
-                    mNeedFixZone = false;
+                    mNeedFixZoneAfterNitz = false;
 
                     if (zone != null) {
                         log("pollStateDone: zone != null zone.getID=" + zone.getID());
@@ -1440,7 +1450,7 @@
                 // so we don't know how to identify the DST rules yet.  Save
                 // the information and hope to fix it up later.
 
-                mNeedFixZone = true;
+                mNeedFixZoneAfterNitz = true;
                 mZoneOffset  = tzOffset;
                 mZoneDst     = dst != 0;
                 mZoneTime    = c.getTimeInMillis();
@@ -1696,7 +1706,7 @@
         pw.println(" mGsmRoaming=" + mGsmRoaming);
         pw.println(" mDataRoaming=" + mDataRoaming);
         pw.println(" mEmergencyOnly=" + mEmergencyOnly);
-        pw.println(" mNeedFixZone=" + mNeedFixZone);
+        pw.println(" mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
         pw.println(" mZoneOffset=" + mZoneOffset);
         pw.println(" mZoneDst=" + mZoneDst);
         pw.println(" mZoneTime=" + mZoneTime);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
index 414ae0d..0e75b80 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -210,28 +210,31 @@
             glEnableVertexAttribArray(attribTexCoords);
             checkGlError();
 
-            glUniform1i(uniformTexture, texture);
+            glUniform1i(uniformTexture, 0);
+            checkGlError();
+
+            // drawQuad
+            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
+                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+            checkGlError();
+
+            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
+                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+            checkGlError();
+
+            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
             checkGlError();
             
             while (!mFinished) {
                 checkCurrent();
 
-                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-                checkGlError();
-
                 glClear(GL_COLOR_BUFFER_BIT);
                 checkGlError();
 
-                // drawQuad
-                triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
-                glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
-                        TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
-
-                triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
-                glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
-                        TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
-
                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+                checkGlError();
 
                 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
                     throw new RuntimeException("Cannot swap buffers");
diff --git a/tests/RenderScriptTests/Balls/Android.mk b/tests/RenderScriptTests/Balls/Android.mk
new file mode 100644
index 0000000..b109584
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2008 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RsBalls
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Balls/AndroidManifest.xml b/tests/RenderScriptTests/Balls/AndroidManifest.xml
new file mode 100644
index 0000000..80e6b39
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.balls">
+    <uses-sdk android:minSdkVersion="14" />
+    <application 
+        android:label="RsBalls"
+        android:icon="@drawable/test_pattern">
+        <activity android:name="Balls"
+                  android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/Balls/_index.html b/tests/RenderScriptTests/Balls/_index.html
new file mode 100644
index 0000000..8760485
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/_index.html
@@ -0,0 +1 @@
+<p>A brute force physics simulation that renders many balls onto the screen and moves them according to user touch and gravity.</p>
\ No newline at end of file
diff --git a/tests/RenderScriptTests/Balls/res/drawable/flares.png b/tests/RenderScriptTests/Balls/res/drawable/flares.png
new file mode 100644
index 0000000..3a5c970
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/res/drawable/flares.png
Binary files differ
diff --git a/tests/RenderScriptTests/Balls/res/drawable/test_pattern.png b/tests/RenderScriptTests/Balls/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/res/drawable/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/Balls.java b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/Balls.java
new file mode 100644
index 0000000..2c6558e
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/Balls.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.balls;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+public class Balls extends Activity implements SensorEventListener {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = false;
+
+    private BallsView mView;
+    private SensorManager mSensorManager;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+    public void onSensorChanged(SensorEvent event) {
+        //android.util.Log.d("rs", "sensor: " + event.sensor + ", x: " + event.values[0] + ", y: " + event.values[1] + ", z: " + event.values[2]);
+        synchronized (this) {
+            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+                if(mView != null) {
+                    mView.setAccel(event.values[0], event.values[1], event.values[2]);
+                }
+            }
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new BallsView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        mSensorManager.registerListener(this,
+                                        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+                                        SensorManager.SENSOR_DELAY_FASTEST);
+
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mView.pause();
+        onStop();
+    }
+
+    @Override
+    protected void onStop() {
+        mSensorManager.unregisterListener(this);
+        super.onStop();
+    }
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsRS.java b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsRS.java
new file mode 100644
index 0000000..8cab9b8
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsRS.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.balls;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+public class BallsRS {
+    public static final int PART_COUNT = 900;
+
+    public BallsRS() {
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_balls mScript;
+    private ScriptC_ball_physics mPhysicsScript;
+    private ProgramFragment mPFLines;
+    private ProgramFragment mPFPoints;
+    private ProgramVertex mPV;
+    private ScriptField_Point mPoints;
+    private ScriptField_VpConsts mVpConsts;
+
+    void updateProjectionMatrices() {
+        mVpConsts = new ScriptField_VpConsts(mRS, 1,
+                                             Allocation.USAGE_SCRIPT |
+                                             Allocation.USAGE_GRAPHICS_CONSTANTS);
+        ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
+        Matrix4f mvp = new Matrix4f();
+        mvp.loadOrtho(0, mRS.getWidth(), mRS.getHeight(), 0, -1, 1);
+        i.MVP = mvp;
+        mVpConsts.set(i, 0, true);
+    }
+
+    private void createProgramVertex() {
+        updateProjectionMatrices();
+
+        ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS);
+        String t =  "varying vec4 varColor;\n" +
+                    "void main() {\n" +
+                    "  vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" +
+                    "  pos.xy = ATTRIB_position;\n" +
+                    "  gl_Position = UNI_MVP * pos;\n" +
+                    "  varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
+                    "  gl_PointSize = ATTRIB_size;\n" +
+                    "}\n";
+        sb.setShader(t);
+        sb.addConstant(mVpConsts.getType());
+        sb.addInput(mPoints.getElement());
+        ProgramVertex pvs = sb.create();
+        pvs.bindConstants(mVpConsts.getAllocation(), 0);
+        mRS.bindProgramVertex(pvs);
+    }
+
+    private Allocation loadTexture(int id) {
+        final Allocation allocation =
+            Allocation.createFromBitmapResource(mRS, mRes,
+                id, Allocation.MipmapControl.MIPMAP_NONE,
+                Allocation.USAGE_GRAPHICS_TEXTURE);
+        return allocation;
+    }
+
+    ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
+        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
+        builder.setDitherEnabled(false);
+        builder.setDepthMaskEnabled(false);
+        return builder.create();
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+
+        ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
+        pfb.setPointSpriteTexCoordinateReplacement(true);
+        pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
+                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+        pfb.setVaryingColor(true);
+        mPFPoints = pfb.create();
+
+        pfb = new ProgramFragmentFixedFunction.Builder(rs);
+        pfb.setVaryingColor(true);
+        mPFLines = pfb.create();
+
+        android.util.Log.e("rs", "Load texture");
+        mPFPoints.bindTexture(loadTexture(R.drawable.flares), 0);
+
+        mPoints = new ScriptField_Point(mRS, PART_COUNT, Allocation.USAGE_SCRIPT);
+
+        Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+        smb.addVertexAllocation(mPoints.getAllocation());
+        smb.addIndexSetType(Mesh.Primitive.POINT);
+        Mesh smP = smb.create();
+
+        mPhysicsScript = new ScriptC_ball_physics(mRS, mRes, R.raw.ball_physics);
+
+        mScript = new ScriptC_balls(mRS, mRes, R.raw.balls);
+        mScript.set_partMesh(smP);
+        mScript.set_physics_script(mPhysicsScript);
+        mScript.bind_point(mPoints);
+        mScript.bind_balls1(new ScriptField_Ball(mRS, PART_COUNT, Allocation.USAGE_SCRIPT));
+        mScript.bind_balls2(new ScriptField_Ball(mRS, PART_COUNT, Allocation.USAGE_SCRIPT));
+
+        mScript.set_gPFLines(mPFLines);
+        mScript.set_gPFPoints(mPFPoints);
+        createProgramVertex();
+
+        mRS.bindProgramStore(BLEND_ADD_DEPTH_NONE(mRS));
+
+        mPhysicsScript.set_gMinPos(new Float2(5, 5));
+        mPhysicsScript.set_gMaxPos(new Float2(width - 5, height - 5));
+
+        mScript.invoke_initParts(width, height);
+
+        mRS.bindRootScript(mScript);
+    }
+
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+        mPhysicsScript.invoke_touch(x, y, pressure, id);
+    }
+
+    public void setAccel(float x, float y) {
+        mPhysicsScript.set_gGravityVector(new Float2(x, y));
+    }
+
+}
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
new file mode 100644
index 0000000..b3b3756
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.balls;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class BallsView extends RSSurfaceView {
+
+    public BallsView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private BallsRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScriptGL(sc);
+            mRS.setSurface(holder, w, h);
+            mRender = new BallsRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+        mRender.updateProjectionMatrices();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        int act = ev.getActionMasked();
+        if (act == ev.ACTION_UP) {
+            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
+            return false;
+        } else if (act == MotionEvent.ACTION_POINTER_UP) {
+            // only one pointer going up, we can get the index like this
+            int pointerIndex = ev.getActionIndex();
+            int pointerId = ev.getPointerId(pointerIndex);
+            mRender.newTouchPosition(0, 0, 0, pointerId);
+            return false;
+        }
+        int count = ev.getHistorySize();
+        int pcount = ev.getPointerCount();
+
+        for (int p=0; p < pcount; p++) {
+            int id = ev.getPointerId(p);
+            mRender.newTouchPosition(ev.getX(p),
+                                     ev.getY(p),
+                                     ev.getPressure(p),
+                                     id);
+
+            for (int i=0; i < count; i++) {
+                mRender.newTouchPosition(ev.getHistoricalX(p, i),
+                                         ev.getHistoricalY(p, i),
+                                         ev.getHistoricalPressure(p, i),
+                                         id);
+            }
+        }
+        return true;
+    }
+
+    void setAccel(float x, float y, float z) {
+        if (mRender == null) {
+            return;
+        }
+        mRender.setAccel(x, -y);
+    }
+
+}
+
+
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/ball_physics.rs b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/ball_physics.rs
new file mode 100644
index 0000000..8a3db6d
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/ball_physics.rs
@@ -0,0 +1,146 @@
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.balls)
+
+#include "balls.rsh"
+
+float2 gGravityVector = {0.f, 9.8f};
+
+float2 gMinPos = {0.f, 0.f};
+float2 gMaxPos = {1280.f, 700.f};
+
+static float2 touchPos[10];
+static float touchPressure[10];
+
+void touch(float x, float y, float pressure, int id) {
+    if (id >= 10) {
+        return;
+    }
+
+    touchPos[id].x = x;
+    touchPos[id].y = y;
+    touchPressure[id] = pressure;
+}
+
+void root(const Ball_t *ballIn, Ball_t *ballOut, const BallControl_t *ctl, uint32_t x) {
+    float2 fv = {0, 0};
+    float2 pos = ballIn->position;
+
+    int arcID = -1;
+    float arcInvStr = 100000;
+
+    const Ball_t * bPtr = rsGetElementAt(ctl->ain, 0);
+    for (uint32_t xin = 0; xin < ctl->dimX; xin++) {
+        float2 vec = bPtr[xin].position - pos;
+        float2 vec2 = vec * vec;
+        float len2 = vec2.x + vec2.y;
+
+        if (len2 < 10000) {
+            //float minDist = ballIn->size + bPtr[xin].size;
+            float forceScale = ballIn->size * bPtr[xin].size;
+            forceScale *= forceScale;
+
+            if (len2 > 16 /* (minDist*minDist)*/)  {
+                // Repulsion
+                float len = sqrt(len2);
+                fv -= (vec / (len * len * len)) * 20000.f * forceScale;
+            } else {
+                if (len2 < 1) {
+                    if (xin == x) {
+                        continue;
+                    }
+                    ballOut->delta = 0.f;
+                    ballOut->position = ballIn->position;
+                    if (xin > x) {
+                        ballOut->position.x += 1.f;
+                    } else {
+                        ballOut->position.x -= 1.f;
+                    }
+                    //ballOut->color.rgb = 1.f;
+                    //ballOut->arcID = -1;
+                    //ballOut->arcStr = 0;
+                    continue;
+                }
+                // Collision
+                float2 axis = normalize(vec);
+                float e1 = dot(axis, ballIn->delta);
+                float e2 = dot(axis, bPtr[xin].delta);
+                float e = (e1 - e2) * 0.45f;
+                if (e1 > 0) {
+                    fv -= axis * e;
+                } else {
+                    fv += axis * e;
+                }
+            }
+        }
+    }
+
+    fv /= ballIn->size * ballIn->size * ballIn->size;
+    fv -= gGravityVector * 4.f;
+    fv *= ctl->dt;
+
+    for (int i=0; i < 10; i++) {
+        if (touchPressure[i] > 0.1f) {
+            float2 vec = touchPos[i] - ballIn->position;
+            float2 vec2 = vec * vec;
+            float len2 = max(2.f, vec2.x + vec2.y);
+            fv -= (vec / len2) * touchPressure[i] * 300.f;
+        }
+    }
+
+    ballOut->delta = (ballIn->delta * (1.f - 0.004f)) + fv;
+    ballOut->position = ballIn->position + (ballOut->delta * ctl->dt);
+
+    const float wallForce = 400.f;
+    if (ballOut->position.x > (gMaxPos.x - 20.f)) {
+        float d = gMaxPos.x - ballOut->position.x;
+        if (d < 0.f) {
+            if (ballOut->delta.x > 0) {
+                ballOut->delta.x *= -0.7f;
+            }
+            ballOut->position.x = gMaxPos.x;
+        } else {
+            ballOut->delta.x -= min(wallForce / (d * d), 10.f);
+        }
+    }
+
+    if (ballOut->position.x < (gMinPos.x + 20.f)) {
+        float d = ballOut->position.x - gMinPos.x;
+        if (d < 0.f) {
+            if (ballOut->delta.x < 0) {
+                ballOut->delta.x *= -0.7f;
+            }
+            ballOut->position.x = gMinPos.x + 1.f;
+        } else {
+            ballOut->delta.x += min(wallForce / (d * d), 10.f);
+        }
+    }
+
+    if (ballOut->position.y > (gMaxPos.y - 20.f)) {
+        float d = gMaxPos.y - ballOut->position.y;
+        if (d < 0.f) {
+            if (ballOut->delta.y > 0) {
+                ballOut->delta.y *= -0.7f;
+            }
+            ballOut->position.y = gMaxPos.y;
+        } else {
+            ballOut->delta.y -= min(wallForce / (d * d), 10.f);
+        }
+    }
+
+    if (ballOut->position.y < (gMinPos.y + 20.f)) {
+        float d = ballOut->position.y - gMinPos.y;
+        if (d < 0.f) {
+            if (ballOut->delta.y < 0) {
+                ballOut->delta.y *= -0.7f;
+            }
+            ballOut->position.y = gMinPos.y + 1.f;
+        } else {
+            ballOut->delta.y += min(wallForce / (d * d * d), 10.f);
+        }
+    }
+
+    ballOut->size = ballIn->size;
+
+    //rsDebug("physics pos out", ballOut->position);
+}
+
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rs b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rs
new file mode 100644
index 0000000..dcdd586
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rs
@@ -0,0 +1,83 @@
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.balls)
+#include "rs_graphics.rsh"
+
+#include "balls.rsh"
+
+#pragma stateVertex(parent)
+#pragma stateStore(parent)
+
+rs_program_fragment gPFPoints;
+rs_program_fragment gPFLines;
+rs_mesh partMesh;
+
+typedef struct __attribute__((packed, aligned(4))) Point {
+    float2 position;
+    float size;
+} Point_t;
+Point_t *point;
+
+typedef struct VpConsts {
+    rs_matrix4x4 MVP;
+} VpConsts_t;
+VpConsts_t *vpConstants;
+
+rs_script physics_script;
+
+Ball_t *balls1;
+Ball_t *balls2;
+
+static int frame = 0;
+
+void initParts(int w, int h)
+{
+    uint32_t dimX = rsAllocationGetDimX(rsGetAllocation(balls1));
+
+    for (uint32_t ct=0; ct < dimX; ct++) {
+        balls1[ct].position.x = rsRand(0.f, (float)w);
+        balls1[ct].position.y = rsRand(0.f, (float)h);
+        balls1[ct].delta.x = 0.f;
+        balls1[ct].delta.y = 0.f;
+        balls1[ct].size = 1.f;
+
+        float r = rsRand(100.f);
+        if (r > 90.f) {
+            balls1[ct].size += pow(10.f, rsRand(0.f, 2.f)) * 0.07f;
+        }
+    }
+}
+
+
+
+int root() {
+    rsgClearColor(0.f, 0.f, 0.f, 1.f);
+
+    BallControl_t bc;
+    Ball_t *bout;
+
+    if (frame & 1) {
+        bc.ain = rsGetAllocation(balls2);
+        bc.aout = rsGetAllocation(balls1);
+        bout = balls1;
+    } else {
+        bc.ain = rsGetAllocation(balls1);
+        bc.aout = rsGetAllocation(balls2);
+        bout = balls2;
+    }
+
+    bc.dimX = rsAllocationGetDimX(bc.ain);
+    bc.dt = 1.f / 30.f;
+
+    rsForEach(physics_script, bc.ain, bc.aout, &bc, sizeof(bc));
+
+    for (uint32_t ct=0; ct < bc.dimX; ct++) {
+        point[ct].position = bout[ct].position;
+        point[ct].size = 6.f /*+ bout[ct].color.g * 6.f*/ * bout[ct].size;
+    }
+
+    frame++;
+    rsgBindProgramFragment(gPFPoints);
+    rsgDrawMesh(partMesh);
+    return 1;
+}
+
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rsh b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rsh
new file mode 100644
index 0000000..fc886f9
--- /dev/null
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/balls.rsh
@@ -0,0 +1,18 @@
+
+typedef struct __attribute__((packed, aligned(4))) Ball {
+    float2 delta;
+    float2 position;
+    //float3 color;
+    float size;
+    //int arcID;
+    //float arcStr;
+} Ball_t;
+Ball_t *balls;
+
+
+typedef struct BallControl {
+    uint32_t dimX;
+    rs_allocation ain;
+    rs_allocation aout;
+    float dt;
+} BallControl_t;
diff --git a/tests/RenderScriptTests/Fountain/Android.mk b/tests/RenderScriptTests/Fountain/Android.mk
new file mode 100644
index 0000000..2049ecf
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+# TODO: build fails with this set
+# LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := RsFountain
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Fountain/AndroidManifest.xml b/tests/RenderScriptTests/Fountain/AndroidManifest.xml
new file mode 100644
index 0000000..d19b8c335
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.fountain">
+    <uses-sdk android:minSdkVersion="14" />
+    <application
+        android:label="RsFountain"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/test_pattern">
+        <activity android:name="Fountain">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/Fountain/_index.html b/tests/RenderScriptTests/Fountain/_index.html
new file mode 100644
index 0000000..223242f
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/_index.html
@@ -0,0 +1,5 @@
+<p>An example that renders many dots on the screen that follow a user's touch. The dots fall 
+to the bottom of the screen when the user releases the finger.</p>
+
+
+
diff --git a/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png b/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java
new file mode 100644
index 0000000..311455a
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountain;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Fountain extends Activity {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = false;
+
+    private FountainView mView;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new FountainView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        Log.e("rs", "onResume");
+
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        Log.e("rs", "onPause");
+
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.pause();
+
+
+
+        //Runtime.getRuntime().exit(0);
+    }
+
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java
new file mode 100644
index 0000000..646c807
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountain;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+
+public class FountainRS {
+    public static final int PART_COUNT = 50000;
+
+    public FountainRS() {
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_fountain mScript;
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+
+        ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
+        pfb.setVaryingColor(true);
+        rs.bindProgramFragment(pfb.create());
+
+        ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);//
+ //                                                        Allocation.USAGE_GRAPHICS_VERTEX);
+
+        Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+        smb.addVertexAllocation(points.getAllocation());
+        smb.addIndexSetType(Mesh.Primitive.POINT);
+        Mesh sm = smb.create();
+
+        mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
+        mScript.set_partMesh(sm);
+        mScript.bind_point(points);
+        mRS.bindRootScript(mScript);
+    }
+
+    boolean holdingColor[] = new boolean[10];
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+        if (id >= holdingColor.length) {
+            return;
+        }
+        int rate = (int)(pressure * pressure * 500.f);
+        if (rate > 500) {
+            rate = 500;
+        }
+        if (rate > 0) {
+            mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
+            holdingColor[id] = true;
+        } else {
+            holdingColor[id] = false;
+        }
+
+    }
+}
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
new file mode 100644
index 0000000..ba09421
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountain;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSTextureView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FountainView extends RSTextureView {
+
+    public FountainView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private FountainRS mRender;
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        android.util.Log.e("rs", "onAttachedToWindow");
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScriptGL(sc);
+            mRender = new FountainRS();
+            mRender.init(mRS, getResources());
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        android.util.Log.e("rs", "onDetachedFromWindow");
+        if (mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        int act = ev.getActionMasked();
+        if (act == ev.ACTION_UP) {
+            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
+            return false;
+        } else if (act == MotionEvent.ACTION_POINTER_UP) {
+            // only one pointer going up, we can get the index like this
+            int pointerIndex = ev.getActionIndex();
+            int pointerId = ev.getPointerId(pointerIndex);
+            mRender.newTouchPosition(0, 0, 0, pointerId);
+        }
+        int count = ev.getHistorySize();
+        int pcount = ev.getPointerCount();
+
+        for (int p=0; p < pcount; p++) {
+            int id = ev.getPointerId(p);
+            mRender.newTouchPosition(ev.getX(p),
+                                     ev.getY(p),
+                                     ev.getPressure(p),
+                                     id);
+
+            for (int i=0; i < count; i++) {
+                mRender.newTouchPosition(ev.getHistoricalX(p, i),
+                                         ev.getHistoricalY(p, i),
+                                         ev.getHistoricalPressure(p, i),
+                                         id);
+            }
+        }
+        return true;
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs
new file mode 100644
index 0000000..151b689
--- /dev/null
+++ b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs
@@ -0,0 +1,70 @@
+// Fountain test script
+#pragma version(1)
+#pragma rs_fp_relaxed
+
+#pragma rs java_package_name(com.example.android.rs.fountain)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+static int newPart = 0;
+rs_mesh partMesh;
+
+typedef struct __attribute__((packed, aligned(4))) Point {
+    float2 delta;
+    float2 position;
+    uchar4 color;
+} Point_t;
+Point_t *point;
+
+int root() {
+    float dt = min(rsGetDt(), 0.1f);
+    rsgClearColor(0.f, 0.f, 0.f, 1.f);
+    const float height = rsgGetHeight();
+    const int size = rsAllocationGetDimX(rsGetAllocation(point));
+    float dy2 = dt * (10.f);
+    Point_t * p = point;
+    for (int ct=0; ct < size; ct++) {
+        p->delta.y += dy2;
+        p->position += p->delta;
+        if ((p->position.y > height) && (p->delta.y > 0)) {
+            p->delta.y *= -0.3f;
+        }
+        p++;
+    }
+
+    rsgDrawMesh(partMesh);
+    return 1;
+}
+
+static float4 partColor[10];
+void addParticles(int rate, float x, float y, int index, bool newColor)
+{
+    if (newColor) {
+        partColor[index].x = rsRand(0.5f, 1.0f);
+        partColor[index].y = rsRand(1.0f);
+        partColor[index].z = rsRand(1.0f);
+    }
+    float rMax = ((float)rate) * 0.02f;
+    int size = rsAllocationGetDimX(rsGetAllocation(point));
+    uchar4 c = rsPackColorTo8888(partColor[index]);
+
+    Point_t * np = &point[newPart];
+    float2 p = {x, y};
+    while (rate--) {
+        float angle = rsRand(3.14f * 2.f);
+        float len = rsRand(rMax);
+        np->delta.x = len * sin(angle);
+        np->delta.y = len * cos(angle);
+        np->position = p;
+        np->color = c;
+        newPart++;
+        np++;
+        if (newPart >= size) {
+            newPart = 0;
+            np = &point[newPart];
+        }
+    }
+}
+
diff --git a/tests/RenderScriptTests/FountainFbo/Android.mk b/tests/RenderScriptTests/FountainFbo/Android.mk
new file mode 100644
index 0000000..55a4fd8
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+# TODO: build fails with this set
+# LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := RsFountainFbo
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml b/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml
new file mode 100644
index 0000000..082744b
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.fountainfbo">
+    <uses-sdk android:minSdkVersion="14" />
+    <application
+        android:label="RsFountainFbo"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/test_pattern">
+        <activity android:name="FountainFbo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/FountainFbo/_index.html b/tests/RenderScriptTests/FountainFbo/_index.html
new file mode 100644
index 0000000..5508657
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/_index.html
@@ -0,0 +1,7 @@
+<p>An example that renders many dots on the screen that follow a user's touch. The dots fall 
+to the bottom of the screen when no touch is detected. This example modifies
+the <a href="../Fountain/index.html">Fountain</a> sample to include rendering to a
+a framebuffer object as well as the default framebuffer.</p>
+
+
+
diff --git a/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png b/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java
new file mode 100644
index 0000000..d8ba30fd
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountainfbo;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class FountainFbo extends Activity {
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = false;
+
+    private FountainFboView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        /* Create our Preview view and set it as the content of our Activity */
+        mView = new FountainFboView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        Log.e("rs", "onResume");
+
+        /* Ideally a game should implement onResume() and onPause()
+         to take appropriate action when the activity loses focus */
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        Log.e("rs", "onPause");
+
+        /* Ideally a game should implement onResume() and onPause()
+        to take appropriate action when the activity loses focus */
+        super.onPause();
+        mView.pause();
+    }
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+}
+
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java
new file mode 100644
index 0000000..3bf3ff1
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountainfbo;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Mesh;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramFragmentFixedFunction;
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Type;
+
+public class FountainFboRS {
+    public static final int PART_COUNT = 50000;
+
+    public FountainFboRS() {
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_fountainfbo mScript;
+    private Allocation mColorBuffer;
+    private ProgramFragment mProgramFragment;
+    private ProgramFragment mTextureProgramFragment;
+    public void init(RenderScriptGL rs, Resources res) {
+      mRS = rs;
+      mRes = res;
+
+      ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
+
+      Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+      smb.addVertexAllocation(points.getAllocation());
+      smb.addIndexSetType(Mesh.Primitive.POINT);
+      Mesh sm = smb.create();
+
+      mScript = new ScriptC_fountainfbo(mRS, mRes, R.raw.fountainfbo);
+      mScript.set_partMesh(sm);
+      mScript.bind_point(points);
+
+      ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
+      pfb.setVaryingColor(true);
+      mProgramFragment = pfb.create();
+      mScript.set_gProgramFragment(mProgramFragment);
+
+      /* Second fragment shader to use a texture (framebuffer object) to draw with */
+      pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+          ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+
+      /* Set the fragment shader in the Renderscript runtime */
+      mTextureProgramFragment = pfb.create();
+      mScript.set_gTextureProgramFragment(mTextureProgramFragment);
+
+      /* Create the allocation for the color buffer */
+      Type.Builder colorBuilder = new Type.Builder(mRS, Element.RGBA_8888(mRS));
+      colorBuilder.setX(256).setY(256);
+      mColorBuffer = Allocation.createTyped(mRS, colorBuilder.create(),
+      Allocation.USAGE_GRAPHICS_TEXTURE |
+      Allocation.USAGE_GRAPHICS_RENDER_TARGET);
+
+      /* Set the allocation in the Renderscript runtime */
+      mScript.set_gColorBuffer(mColorBuffer);
+
+      mRS.bindRootScript(mScript);
+  }
+
+    boolean holdingColor[] = new boolean[10];
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+        if (id >= holdingColor.length) {
+            return;
+        }
+        int rate = (int)(pressure * pressure * 500.f);
+        if (rate > 500) {
+            rate = 500;
+        }
+        if (rate > 0) {
+            mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
+            holdingColor[id] = true;
+        } else {
+            holdingColor[id] = false;
+        }
+
+    }
+}
+
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
new file mode 100644
index 0000000..6e40da3
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.fountainfbo;
+
+
+import android.renderscript.RSTextureView;
+import android.renderscript.RenderScriptGL;
+import android.content.Context;
+import android.view.MotionEvent;
+
+public class FountainFboView extends RSTextureView {
+
+    public FountainFboView(Context context) {
+        super(context);
+    }
+
+    private RenderScriptGL mRS;
+    private FountainFboRS mRender;
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        android.util.Log.e("rs", "onAttachedToWindow");
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScriptGL(sc);
+            mRender = new FountainFboRS();
+            mRender.init(mRS, getResources());
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        android.util.Log.e("rs", "onDetachedFromWindow");
+        if (mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        int act = ev.getActionMasked();
+        if (act == ev.ACTION_UP) {
+            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
+            return false;
+        } else if (act == MotionEvent.ACTION_POINTER_UP) {
+            // only one pointer going up, we can get the index like this
+            int pointerIndex = ev.getActionIndex();
+            int pointerId = ev.getPointerId(pointerIndex);
+            mRender.newTouchPosition(0, 0, 0, pointerId);
+        }
+        int count = ev.getHistorySize();
+        int pcount = ev.getPointerCount();
+
+        for (int p=0; p < pcount; p++) {
+            int id = ev.getPointerId(p);
+            mRender.newTouchPosition(ev.getX(p),
+                                     ev.getY(p),
+                                     ev.getPressure(p),
+                                     id);
+
+            for (int i=0; i < count; i++) {
+                mRender.newTouchPosition(ev.getHistoricalX(p, i),
+                                         ev.getHistoricalY(p, i),
+                                         ev.getHistoricalPressure(p, i),
+                                         id);
+            }
+        }
+        return true;
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs
new file mode 100644
index 0000000..763f6ba
--- /dev/null
+++ b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs
@@ -0,0 +1,106 @@
+// Fountain test script
+#pragma version(1)
+
+#pragma rs java_package_name(com.example.android.rs.fountainfbo)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+static int newPart = 0;
+rs_mesh partMesh;
+rs_program_vertex gProgramVertex;
+
+//allocation for color buffer
+rs_allocation gColorBuffer;
+//fragment shader for rendering without a texture (used for rendering to framebuffer object)
+rs_program_fragment gProgramFragment;
+//fragment shader for rendering with a texture (used for rendering to default framebuffer)
+rs_program_fragment gTextureProgramFragment;
+
+typedef struct __attribute__((packed, aligned(4))) Point {
+    float2 delta;
+    float2 position;
+    uchar4 color;
+} Point_t;
+Point_t *point;
+
+int root() {
+    float dt = min(rsGetDt(), 0.1f);
+    rsgClearColor(0.f, 0.f, 0.f, 1.f);
+    const float height = rsgGetHeight();
+    const int size = rsAllocationGetDimX(rsGetAllocation(point));
+    float dy2 = dt * (10.f);
+    Point_t * p = point;
+    for (int ct=0; ct < size; ct++) {
+        p->delta.y += dy2;
+        p->position += p->delta;
+        if ((p->position.y > height) && (p->delta.y > 0)) {
+            p->delta.y *= -0.3f;
+        }
+        p++;
+    }
+    //Tell Renderscript runtime to render to the frame buffer object
+    rsgBindColorTarget(gColorBuffer, 0);
+
+    //Begin rendering on a white background
+    rsgClearColor(1.f, 1.f, 1.f, 1.f);
+    rsgDrawMesh(partMesh);
+
+    //When done, tell Renderscript runtime to stop rendering to framebuffer object
+    rsgClearAllRenderTargets();
+
+    //Bind a new fragment shader that declares the framebuffer object to be used as a texture
+    rsgBindProgramFragment(gTextureProgramFragment);
+
+    //Bind the framebuffer object to the fragment shader at slot 0 as a texture
+    rsgBindTexture(gTextureProgramFragment, 0, gColorBuffer);
+
+    //Draw a quad using the framebuffer object as the texture
+    float startX = 10, startY = 10;
+    float s = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
+                         startX, startY + s, 0, 0, 0,
+                         startX + s, startY + s, 0, 1, 0,
+                         startX + s, startY, 0, 1, 1);
+
+    //Rebind the original fragment shader to render as normal
+    rsgBindProgramFragment(gProgramFragment);
+
+    //Render the main scene
+    rsgDrawMesh(partMesh);
+
+    return 1;
+}
+
+static float4 partColor[10];
+void addParticles(int rate, float x, float y, int index, bool newColor)
+{
+    if (newColor) {
+        partColor[index].x = rsRand(0.5f, 1.0f);
+        partColor[index].y = rsRand(1.0f);
+        partColor[index].z = rsRand(1.0f);
+    }
+    float rMax = ((float)rate) * 0.02f;
+    int size = rsAllocationGetDimX(rsGetAllocation(point));
+    uchar4 c = rsPackColorTo8888(partColor[index]);
+
+    Point_t * np = &point[newPart];
+    float2 p = {x, y};
+    while (rate--) {
+        float angle = rsRand(3.14f * 2.f);
+        float len = rsRand(rMax);
+        np->delta.x = len * sin(angle);
+        np->delta.y = len * cos(angle);
+        np->position = p;
+        np->color = c;
+        newPart++;
+        np++;
+        if (newPart >= size) {
+            newPart = 0;
+            np = &point[newPart];
+        }
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/HelloWorld/Android.mk b/tests/RenderScriptTests/HelloWorld/Android.mk
new file mode 100644
index 0000000..2af1cdb
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RsHelloWorld
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml b/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml
new file mode 100644
index 0000000..1d37dc9
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+ 
+          http://www.apache.org/licenses/LICENSE-2.0
+ 
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.helloworld">
+    <uses-sdk android:minSdkVersion="11" />
+    <application android:label="RsHelloWorld"
+    android:icon="@drawable/test_pattern">
+        <activity android:name="HelloWorld"
+                  android:label="RsHelloWorld"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/HelloWorld/_index.html b/tests/RenderScriptTests/HelloWorld/_index.html
new file mode 100644
index 0000000..4cab738
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/_index.html
@@ -0,0 +1 @@
+<p>A Renderscript graphics application that draws the text "Hello, World!" where the user touches.</p>
\ No newline at end of file
diff --git a/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png b/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java
new file mode 100644
index 0000000..9b1697b
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.helloworld;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// Renderscript activity
+public class HelloWorld extends Activity {
+
+    // Custom view to use with RenderScript
+    private HelloWorldView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our view and set it as the content of our Activity
+        mView = new HelloWorldView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally an app should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally an app should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onPause();
+        mView.pause();
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java
new file mode 100644
index 0000000..4316411
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.helloworld;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+// This is the renderer for the HelloWorldView
+public class HelloWorldRS {
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    private ScriptC_helloworld mScript;
+
+    public HelloWorldRS() {
+    }
+
+    // This provides us with the renderscript context and resources that
+    // allow us to create the script that does rendering
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+        initRS();
+    }
+
+    public void onActionDown(int x, int y) {
+        mScript.set_gTouchX(x);
+        mScript.set_gTouchY(y);
+    }
+
+    private void initRS() {
+        mScript = new ScriptC_helloworld(mRS, mRes, R.raw.helloworld);
+        mRS.bindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java
new file mode 100644
index 0000000..557ebc5
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.helloworld;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+public class HelloWorldView extends RSSurfaceView {
+    // Renderscipt context
+    private RenderScriptGL mRS;
+    // Script that does the rendering
+    private HelloWorldRS mRender;
+
+    public HelloWorldView(Context context) {
+        super(context);
+        ensureRenderScript();
+    }
+
+    private void ensureRenderScript() {
+        if (mRS == null) {
+            // Initialize renderscript with desired surface characteristics.
+            // In this case, just use the defaults
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScriptGL(sc);
+            // Create an instance of the script that does the rendering
+            mRender = new HelloWorldRS();
+            mRender.init(mRS, getResources());
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ensureRenderScript();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        // Handle the system event and clean up
+        mRender = null;
+        if (mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        // Pass touch events from the system to the rendering script
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            return true;
+        }
+
+        return false;
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs
new file mode 100644
index 0000000..bcf624e2
--- /dev/null
+++ b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs
@@ -0,0 +1,47 @@
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma version(1)
+
+// Tell which java package name the reflected files should belong to
+#pragma rs java_package_name(com.example.android.rs.helloworld)
+
+// Built-in header with graphics API's
+#include "rs_graphics.rsh"
+
+// gTouchX and gTouchY are variables that will be reflected for use
+// by the java API. We can use them to notify the script of touch events.
+int gTouchX;
+int gTouchY;
+
+// This is invoked automatically when the script is created
+void init() {
+    gTouchX = 50.0f;
+    gTouchY = 50.0f;
+}
+
+int root(void) {
+
+    // Clear the background color
+    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    // Tell the runtime what the font color should be
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    // Introuduce ourselves to the world by drawing a greeting
+    // at the position user touched on the screen
+    rsgDrawText("Hello World!", gTouchX, gTouchY);
+
+    // Return value tells RS roughly how often to redraw
+    // in this case 20 ms
+    return 20;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/Android.mk b/tests/RenderScriptTests/MiscSamples/Android.mk
new file mode 100644
index 0000000..6b8b691
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2008 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RsMiscSamples
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml b/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml
new file mode 100644
index 0000000..08a3976
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.miscsamples">
+    <uses-sdk android:minSdkVersion="11" />
+    <application android:label="RsMiscSamples"
+    android:icon="@drawable/test_pattern">
+        <activity android:name="RsList"
+                  android:label="RsList"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity android:name="RsRenderStates"
+                  android:label="RsStates"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/MiscSamples/_index.html b/tests/RenderScriptTests/MiscSamples/_index.html
new file mode 100644
index 0000000..5872431
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/_index.html
@@ -0,0 +1 @@
+<p>A set of samples that demonstrate how to use various features of the Renderscript APIs.</p>
\ No newline at end of file
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png
new file mode 100644
index 0000000..b631e1e
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png
new file mode 100644
index 0000000..baf35d0
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png
new file mode 100644
index 0000000..8e34714
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png
new file mode 100644
index 0000000..3cd3775
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png
new file mode 100644
index 0000000..1e08f3b
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl
new file mode 100644
index 0000000..e492a47
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl
@@ -0,0 +1,13 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba;
+   lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba;
+   lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba;
+   col0.xyz = col0.xyz*col1.xyz*1.5;
+   col0.xyz = mix(col0.xyz, col2.xyz, col2.w);
+   col0.w = 0.5;
+   gl_FragColor = col0;
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl
new file mode 100644
index 0000000..5fc05f1
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl
@@ -0,0 +1,29 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(-varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - varWorldPos);
+   vec3 light0R = -reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
+   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
+
+   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - varWorldPos);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
+   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
+   float light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
+   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
+   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl
new file mode 100644
index 0000000..a2c807e
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl
@@ -0,0 +1,21 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 objPos = ATTRIB_position;
+   vec3 oldPos = objPos.xyz;
+   objPos.xyz += 0.1*sin(objPos.xyz*2.0 + UNI_time);
+   objPos.xyz += 0.05*sin(objPos.xyz*4.0 + UNI_time*0.5);
+   objPos.xyz += 0.02*sin(objPos.xyz*7.0 + UNI_time*0.75);
+   vec4 worldPos = UNI_model * objPos;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * (ATTRIB_normal + oldPos - objPos.xyz);
+
+   varWorldPos = worldPos.xyz;
+   varWorldNormal = worldNorm;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl
new file mode 100644
index 0000000..e6885a3
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl
@@ -0,0 +1,17 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 objPos = ATTRIB_position;
+   vec4 worldPos = UNI_model * objPos;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+
+   varWorldPos = worldPos.xyz;
+   varWorldNormal = worldNorm;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl
new file mode 100644
index 0000000..238ecad
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl
@@ -0,0 +1,16 @@
+
+varying lowp float light0_Diffuse;
+varying lowp float light0_Specular;
+varying lowp float light1_Diffuse;
+varying lowp float light1_Specular;
+varying vec2 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_light_DiffuseColor[0].xyz + light1_Diffuse * UNI_light_DiffuseColor[1].xyz);
+   col.xyz += light0_Specular * UNI_light_SpecularColor[0].xyz;
+   col.xyz += light1_Specular * UNI_light_SpecularColor[1].xyz;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl
new file mode 100644
index 0000000..7a1310a
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl
@@ -0,0 +1,32 @@
+varying float light0_Diffuse;
+varying float light0_Specular;
+varying float light1_Diffuse;
+varying float light1_Specular;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 worldPos = UNI_model[0] * ATTRIB_position;
+   worldPos = UNI_model[1] * worldPos;
+   gl_Position = UNI_proj * worldPos;
+
+   mat4 model0 = UNI_model[0];
+   mat3 model3 = mat3(model0[0].xyz, model0[1].xyz, model0[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+   vec3 V = normalize(-worldPos.xyz);
+
+   vec3 light0Vec = normalize(UNI_light_Posision[0].xyz - worldPos.xyz);
+   vec3 light0R = -reflect(light0Vec, worldNorm);
+   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light_Diffuse[0];
+   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
+   light0_Specular = pow(light0Spec, UNI_light_CosinePower[0]) * UNI_light_Specular[0];
+
+   vec3 light1Vec = normalize(UNI_light_Posision[1].xyz - worldPos.xyz);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light_Diffuse[1];
+   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
+   light1_Specular = pow(light1Spec, UNI_light_CosinePower[1]) * UNI_light_Specular[1];
+
+   gl_PointSize = 1.0;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl
new file mode 100644
index 0000000..15696a4
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl
@@ -0,0 +1,8 @@
+
+varying vec3 worldNormal;
+
+void main() {
+   lowp vec4 col = textureCube(UNI_Tex0, worldNormal);
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl
new file mode 100644
index 0000000..70f5cd6
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl
@@ -0,0 +1,10 @@
+varying vec3 worldNormal;
+
+// This is where actual shader code begins
+void main() {
+   vec4 worldPos = UNI_model * ATTRIB_position;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   worldNormal = model3 * ATTRIB_normal;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl
new file mode 100644
index 0000000..d56e203
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl
@@ -0,0 +1,16 @@
+
+varying lowp float light0_Diffuse;
+varying lowp float light0_Specular;
+varying lowp float light1_Diffuse;
+varying lowp float light1_Specular;
+varying vec2 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
+   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
+   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl
new file mode 100644
index 0000000..f7d01de
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl
@@ -0,0 +1,30 @@
+varying float light0_Diffuse;
+varying float light0_Specular;
+varying float light1_Diffuse;
+varying float light1_Specular;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 worldPos = UNI_model * ATTRIB_position;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+   vec3 V = normalize(-worldPos.xyz);
+
+   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - worldPos.xyz);
+   vec3 light0R = -reflect(light0Vec, worldNorm);
+   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
+   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
+   light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
+
+   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - worldPos.xyz);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
+   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
+   light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
+
+   gl_PointSize = 1.0;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d b/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d
new file mode 100644
index 0000000..0322b01
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java
new file mode 100644
index 0000000..dade3b3
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class RsList extends Activity {
+
+    private RsListView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RsListView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onPause();
+        mView.pause();
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java
new file mode 100644
index 0000000..eeb2480
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+
+import java.io.Writer;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+
+public class RsListRS {
+
+    private final int STATE_LAST_FOCUS = 1;
+
+    private static final String[] DATA_LIST = {
+    "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+    "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+    "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+    "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+    "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+    "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+    "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+    "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+    "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+    "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+    "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+    "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+    "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+    "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+    "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+    "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+    "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+    "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+    "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+    "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+    "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+    "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+    "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+    "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+    "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+    "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+    "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+    "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+    "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+    "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+    "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+    "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+    "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+    "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+    "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+    "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+    "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+    "Ukraine", "United Arab Emirates", "United Kingdom",
+    "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+    "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+    "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+    };
+
+    public RsListRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private Font mItalic;
+
+    ScriptField_ListAllocs_s mListAllocs;
+
+    private ScriptC_rslist mScript;
+
+    int mLastX;
+    int mLastY;
+
+    public void onActionDown(int x, int y) {
+        mScript.set_gDY(0.0f);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    public void onActionMove(int x, int y) {
+        int dx = mLastX - x;
+        int dy = mLastY - y;
+
+        if (Math.abs(dy) <= 2) {
+            dy = 0;
+        }
+
+        mScript.set_gDY(dy);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist);
+
+        mListAllocs = new ScriptField_ListAllocs_s(mRS, DATA_LIST.length);
+        for (int i = 0; i < DATA_LIST.length; i ++) {
+            ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item();
+            listElem.text = Allocation.createFromString(mRS, DATA_LIST[i], Allocation.USAGE_SCRIPT);
+            mListAllocs.set(listElem, i, false);
+        }
+
+        mListAllocs.copyAll();
+
+        mScript.bind_gList(mListAllocs);
+
+        mItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+        mScript.set_gItalic(mItalic);
+
+        mRS.bindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java
new file mode 100644
index 0000000..db6e6c5
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+public class RsListView extends RSSurfaceView {
+
+    public RsListView(Context context) {
+        super(context);
+        ensureRenderScript();
+    }
+
+    private RenderScriptGL mRS;
+    private RsListRS mRender;
+
+    private void ensureRenderScript() {
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScriptGL(sc);
+            mRender = new RsListRS();
+            mRender.init(mRS, getResources());
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ensureRenderScript();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mRender = null;
+        if (mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = false;
+        int act = ev.getAction();
+        if (act == MotionEvent.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        } else if (act == MotionEvent.ACTION_MOVE) {
+            mRender.onActionMove((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+
+        return ret;
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java
new file mode 100644
index 0000000..f4ea76e
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class RsRenderStates extends Activity {
+
+    private RsRenderStatesView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RsRenderStatesView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.pause();
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java
new file mode 100644
index 0000000..0e319fe
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.renderscript.*;
+import android.renderscript.Font.Style;
+import android.renderscript.Program.TextureType;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.renderscript.ProgramStore.BlendSrcFunc;
+import android.renderscript.ProgramStore.BlendDstFunc;
+import android.renderscript.Sampler.Value;
+import android.util.Log;
+
+
+public class RsRenderStatesRS {
+
+    int mWidth;
+    int mHeight;
+
+    public RsRenderStatesRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mWidth = mRS.getWidth();
+        mHeight = mRS.getHeight();
+        mRes = res;
+        mOptionsARGB.inScaled = false;
+        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        mMode = 0;
+        mMaxModes = 0;
+        initRS();
+    }
+
+    public void surfaceChanged() {
+        mWidth = mRS.getWidth();
+        mHeight = mRS.getHeight();
+
+        Matrix4f proj = new Matrix4f();
+        proj.loadOrthoWindow(mWidth, mHeight);
+        mPVA.setProjection(proj);
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    private Sampler mLinearClamp;
+    private Sampler mLinearWrap;
+    private Sampler mMipLinearWrap;
+    private Sampler mNearestClamp;
+    private Sampler mMipLinearAniso8;
+    private Sampler mMipLinearAniso15;
+
+    private ProgramStore mProgStoreBlendNoneDepth;
+    private ProgramStore mProgStoreBlendNone;
+    private ProgramStore mProgStoreBlendAlpha;
+    private ProgramStore mProgStoreBlendAdd;
+
+    private ProgramFragment mProgFragmentTexture;
+    private ProgramFragment mProgFragmentColor;
+
+    private ProgramVertex mProgVertex;
+    private ProgramVertexFixedFunction.Constants mPVA;
+
+    // Custom shaders
+    private ProgramVertex mProgVertexCustom;
+    private ProgramFragment mProgFragmentCustom;
+    private ProgramFragment mProgFragmentMultitex;
+    private ScriptField_VertexShaderConstants_s mVSConst;
+    private ScriptField_VertexShaderConstants2_s mVSConst2;
+    private ScriptField_FragentShaderConstants_s mFSConst;
+    private ScriptField_FragentShaderConstants2_s mFSConst2;
+
+    private ProgramVertex mProgVertexCustom2;
+    private ProgramFragment mProgFragmentCustom2;
+
+    private ProgramVertex mProgVertexCube;
+    private ProgramFragment mProgFragmentCube;
+
+    private ProgramRaster mCullBack;
+    private ProgramRaster mCullFront;
+    private ProgramRaster mCullNone;
+
+    private Allocation mTexTorus;
+    private Allocation mTexOpaque;
+    private Allocation mTexTransparent;
+    private Allocation mTexChecker;
+    private Allocation mTexCube;
+
+    private Mesh mMbyNMesh;
+    private Mesh mTorus;
+
+    Font mFontSans;
+    Font mFontSerif;
+    Font mFontSerifBold;
+    Font mFontSerifItalic;
+    Font mFontSerifBoldItalic;
+    Font mFontMono;
+    private Allocation mTextAlloc;
+
+    private ScriptC_rsrenderstates mScript;
+
+    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
+
+    int mMode;
+    int mMaxModes;
+
+    public void onActionDown(int x, int y) {
+        mMode ++;
+        mMode = mMode % mMaxModes;
+        mScript.set_gDisplayMode(mMode);
+    }
+
+    ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
+        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+        builder.setDitherEnabled(false);
+        builder.setDepthMaskEnabled(false);
+        return builder.create();
+    }
+
+    private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) {
+
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        for (int y = 0; y <= hResolution; y++) {
+            final float normalizedY = (float)y / hResolution;
+            final float yOffset = (normalizedY - 0.5f) * height;
+            for (int x = 0; x <= wResolution; x++) {
+                float normalizedX = (float)x / wResolution;
+                float xOffset = (normalizedX - 0.5f) * width;
+                tmb.setTexture(normalizedX, normalizedY);
+                tmb.addVertex(xOffset, yOffset);
+             }
+        }
+
+        for (int y = 0; y < hResolution; y++) {
+            final int curY = y * (wResolution + 1);
+            final int belowY = (y + 1) * (wResolution + 1);
+            for (int x = 0; x < wResolution; x++) {
+                int curV = curY + x;
+                int belowV = belowY + x;
+                tmb.addTriangle(curV, belowV, curV + 1);
+                tmb.addTriangle(belowV, belowV + 1, curV + 1);
+            }
+        }
+
+        return tmb.create(true);
+    }
+
+    private void initProgramStore() {
+        // Use stock the stock program store object
+        mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
+        mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(mRS);
+
+        // Create a custom program store
+        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
+                             ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+        builder.setDitherEnabled(false);
+        builder.setDepthMaskEnabled(false);
+        mProgStoreBlendAlpha = builder.create();
+
+        mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS);
+
+        mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
+        mScript.set_gProgStoreBlendNone(mProgStoreBlendNone);
+        mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha);
+        mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd);
+    }
+
+    private void initProgramFragment() {
+
+        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
+        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+        mProgFragmentTexture = texBuilder.create();
+        mProgFragmentTexture.bindSampler(mLinearClamp, 0);
+
+        ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
+        colBuilder.setVaryingColor(false);
+        mProgFragmentColor = colBuilder.create();
+
+        mScript.set_gProgFragmentColor(mProgFragmentColor);
+        mScript.set_gProgFragmentTexture(mProgFragmentTexture);
+    }
+
+    private void initProgramVertex() {
+        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
+        mProgVertex = pvb.create();
+
+        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
+        ((ProgramVertexFixedFunction)mProgVertex).bindConstants(mPVA);
+        Matrix4f proj = new Matrix4f();
+        proj.loadOrthoWindow(mWidth, mHeight);
+        mPVA.setProjection(proj);
+
+        mScript.set_gProgVertex(mProgVertex);
+    }
+
+    private void initCustomShaders() {
+        mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
+        mVSConst2 = new ScriptField_VertexShaderConstants2_s(mRS, 1);
+        mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1);
+        mFSConst2 = new ScriptField_FragentShaderConstants2_s(mRS, 1);
+
+        mScript.bind_gVSConstants(mVSConst);
+        mScript.bind_gVSConstants2(mVSConst2);
+        mScript.bind_gFSConstants(mFSConst);
+        mScript.bind_gFSConstants2(mFSConst2);
+
+        // Initialize the shader builder
+        ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS);
+        // Specify the resource that contains the shader string
+        pvbCustom.setShader(mRes, R.raw.shaderv);
+        // Use a script field to spcify the input layout
+        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
+        // Define the constant input layout
+        pvbCustom.addConstant(mVSConst.getAllocation().getType());
+        mProgVertexCustom = pvbCustom.create();
+        // Bind the source of constant data
+        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
+
+        ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS);
+        // Specify the resource that contains the shader string
+        pfbCustom.setShader(mRes, R.raw.shaderf);
+        //Tell the builder how many textures we have
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
+        // Define the constant input layout
+        pfbCustom.addConstant(mFSConst.getAllocation().getType());
+        mProgFragmentCustom = pfbCustom.create();
+        // Bind the source of constant data
+        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
+
+        pvbCustom = new ProgramVertex.Builder(mRS);
+        pvbCustom.setShader(mRes, R.raw.shaderarrayv);
+        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
+        pvbCustom.addConstant(mVSConst2.getAllocation().getType());
+        mProgVertexCustom2 = pvbCustom.create();
+        mProgVertexCustom2.bindConstants(mVSConst2.getAllocation(), 0);
+
+        pfbCustom = new ProgramFragment.Builder(mRS);
+        pfbCustom.setShader(mRes, R.raw.shaderarrayf);
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
+        pfbCustom.addConstant(mFSConst2.getAllocation().getType());
+        mProgFragmentCustom2 = pfbCustom.create();
+        mProgFragmentCustom2.bindConstants(mFSConst2.getAllocation(), 0);
+
+        // Cubemap test shaders
+        pvbCustom = new ProgramVertex.Builder(mRS);
+        pvbCustom.setShader(mRes, R.raw.shadercubev);
+        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
+        pvbCustom.addConstant(mVSConst.getAllocation().getType());
+        mProgVertexCube = pvbCustom.create();
+        mProgVertexCube.bindConstants(mVSConst.getAllocation(), 0);
+
+        pfbCustom = new ProgramFragment.Builder(mRS);
+        pfbCustom.setShader(mRes, R.raw.shadercubef);
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_CUBE);
+        mProgFragmentCube = pfbCustom.create();
+
+        pfbCustom = new ProgramFragment.Builder(mRS);
+        pfbCustom.setShader(mRes, R.raw.multitexf);
+        for (int texCount = 0; texCount < 3; texCount ++) {
+            pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
+        }
+        mProgFragmentMultitex = pfbCustom.create();
+
+        mScript.set_gProgVertexCustom(mProgVertexCustom);
+        mScript.set_gProgFragmentCustom(mProgFragmentCustom);
+        mScript.set_gProgVertexCustom2(mProgVertexCustom2);
+        mScript.set_gProgFragmentCustom2(mProgFragmentCustom2);
+        mScript.set_gProgVertexCube(mProgVertexCube);
+        mScript.set_gProgFragmentCube(mProgFragmentCube);
+        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
+    }
+
+    private Allocation loadTextureRGB(int id) {
+        return Allocation.createFromBitmapResource(mRS, mRes, id,
+                                                   Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                   Allocation.USAGE_GRAPHICS_TEXTURE);
+    }
+
+    private Allocation loadTextureARGB(int id) {
+        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
+        return Allocation.createFromBitmap(mRS, b,
+                                           Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                           Allocation.USAGE_GRAPHICS_TEXTURE);
+    }
+
+    private void loadImages() {
+        mTexTorus = loadTextureRGB(R.drawable.torusmap);
+        mTexOpaque = loadTextureRGB(R.drawable.data);
+        mTexTransparent = loadTextureARGB(R.drawable.leaf);
+        mTexChecker = loadTextureRGB(R.drawable.checker);
+        Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test);
+        mTexCube = Allocation.createCubemapFromBitmap(mRS, b);
+
+        mScript.set_gTexTorus(mTexTorus);
+        mScript.set_gTexOpaque(mTexOpaque);
+        mScript.set_gTexTransparent(mTexTransparent);
+        mScript.set_gTexChecker(mTexChecker);
+        mScript.set_gTexCube(mTexCube);
+    }
+
+    private void initFonts() {
+        // Sans font by family name
+        mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+        mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8);
+        // Create fonts by family and style
+        mFontSerifBold = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8);
+        mFontSerifItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
+        mFontSerifBoldItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+        mFontMono = Font.create(mRS, mRes, "mono", Font.Style.NORMAL, 8);
+
+        mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT);
+
+        mScript.set_gFontSans(mFontSans);
+        mScript.set_gFontSerif(mFontSerif);
+        mScript.set_gFontSerifBold(mFontSerifBold);
+        mScript.set_gFontSerifItalic(mFontSerifItalic);
+        mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic);
+        mScript.set_gFontMono(mFontMono);
+        mScript.set_gTextAlloc(mTextAlloc);
+    }
+
+    private void initMesh() {
+        mMbyNMesh = getMbyNMesh(256, 256, 10, 10);
+        mScript.set_gMbyNMesh(mMbyNMesh);
+
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
+        FileA3D.IndexEntry entry = model.getIndexEntry(0);
+        if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
+            Log.e("rs", "could not load model");
+        } else {
+            mTorus = (Mesh)entry.getObject();
+            mScript.set_gTorusMesh(mTorus);
+        }
+    }
+
+    private void initSamplers() {
+        Sampler.Builder bs = new Sampler.Builder(mRS);
+        bs.setMinification(Sampler.Value.LINEAR);
+        bs.setMagnification(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.WRAP);
+        mLinearWrap = bs.create();
+
+        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
+        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
+        mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
+
+        bs = new Sampler.Builder(mRS);
+        bs.setMinification(Sampler.Value.LINEAR_MIP_LINEAR);
+        bs.setMagnification(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.WRAP);
+        bs.setAnisotropy(8.0f);
+        mMipLinearAniso8 = bs.create();
+        bs.setAnisotropy(15.0f);
+        mMipLinearAniso15 = bs.create();
+
+        mScript.set_gLinearClamp(mLinearClamp);
+        mScript.set_gLinearWrap(mLinearWrap);
+        mScript.set_gMipLinearWrap(mMipLinearWrap);
+        mScript.set_gMipLinearAniso8(mMipLinearAniso8);
+        mScript.set_gMipLinearAniso15(mMipLinearAniso15);
+        mScript.set_gNearestClamp(mNearestClamp);
+    }
+
+    private void initProgramRaster() {
+        mCullBack = ProgramRaster.CULL_BACK(mRS);
+        mCullFront = ProgramRaster.CULL_FRONT(mRS);
+        mCullNone = ProgramRaster.CULL_NONE(mRS);
+
+        mScript.set_gCullBack(mCullBack);
+        mScript.set_gCullFront(mCullFront);
+        mScript.set_gCullNone(mCullNone);
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_rsrenderstates(mRS, mRes, R.raw.rsrenderstates);
+
+        mMaxModes = mScript.get_gMaxModes();
+
+        initSamplers();
+        initProgramStore();
+        initProgramFragment();
+        initProgramVertex();
+        initFonts();
+        loadImages();
+        initMesh();
+        initProgramRaster();
+        initCustomShaders();
+
+        mRS.bindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java
new file mode 100644
index 0000000..a15e38f
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.miscsamples;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+
+public class RsRenderStatesView extends RSSurfaceView {
+
+    public RsRenderStatesView(Context context) {
+        super(context);
+        ensureRenderScript();
+    }
+
+    private RenderScriptGL mRS;
+    private RsRenderStatesRS mRender;
+
+    private void ensureRenderScript() {
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScriptGL(sc);
+            mRender = new RsRenderStatesRS();
+            mRender.init(mRS, getResources());
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ensureRenderScript();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        mRender.surfaceChanged();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mRender = null;
+        if (mRS != null) {
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            return true;
+        }
+
+        return false;
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs
new file mode 100644
index 0000000..d9d450d
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs
@@ -0,0 +1,70 @@
+// Copyright (C) 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
+//
+//      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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.example.android.rs.miscsamples)
+
+#include "rs_graphics.rsh"
+
+float gDY;
+
+rs_font gItalic;
+
+typedef struct ListAllocs_s {
+    rs_allocation text;
+} ListAllocs;
+
+ListAllocs *gList;
+
+void init() {
+    gDY = 0.0f;
+}
+
+int textPos = 0;
+
+int root(void) {
+
+    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+    textPos -= (int)gDY*2;
+    gDY *= 0.95;
+
+    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+    rsgBindFont(gItalic);
+
+    rs_allocation listAlloc;
+    listAlloc = rsGetAllocation(gList);
+    int allocSize = rsAllocationGetDimX(listAlloc);
+
+    int width = rsgGetWidth();
+    int height = rsgGetHeight();
+
+    int itemHeight = 80;
+    int currentYPos = itemHeight + textPos;
+
+    for (int i = 0; i < allocSize; i ++) {
+        if (currentYPos - itemHeight > height) {
+            break;
+        }
+
+        if (currentYPos > 0) {
+            rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0);
+            rsgDrawText(gList[i].text, 30, currentYPos - 32);
+        }
+        currentYPos += itemHeight;
+    }
+
+    return 10;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs
new file mode 100644
index 0000000..5dabd00
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs
@@ -0,0 +1,680 @@
+// Copyright (C) 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
+//
+//      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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.example.android.rs.miscsamples)
+
+#include "rs_graphics.rsh"
+#include "shader_def.rsh"
+
+const int gMaxModes = 11;
+
+rs_program_vertex gProgVertex;
+rs_program_fragment gProgFragmentColor;
+rs_program_fragment gProgFragmentTexture;
+
+rs_program_store gProgStoreBlendNoneDepth;
+rs_program_store gProgStoreBlendNone;
+rs_program_store gProgStoreBlendAlpha;
+rs_program_store gProgStoreBlendAdd;
+
+rs_allocation gTexOpaque;
+rs_allocation gTexTorus;
+rs_allocation gTexTransparent;
+rs_allocation gTexChecker;
+rs_allocation gTexCube;
+
+rs_mesh gMbyNMesh;
+rs_mesh gTorusMesh;
+
+rs_font gFontSans;
+rs_font gFontSerif;
+rs_font gFontSerifBold;
+rs_font gFontSerifItalic;
+rs_font gFontSerifBoldItalic;
+rs_font gFontMono;
+rs_allocation gTextAlloc;
+
+int gDisplayMode;
+
+rs_sampler gLinearClamp;
+rs_sampler gLinearWrap;
+rs_sampler gMipLinearWrap;
+rs_sampler gMipLinearAniso8;
+rs_sampler gMipLinearAniso15;
+rs_sampler gNearestClamp;
+
+rs_program_raster gCullBack;
+rs_program_raster gCullFront;
+rs_program_raster gCullNone;
+
+// Custom vertex shader compunents
+VertexShaderConstants *gVSConstants;
+VertexShaderConstants2 *gVSConstants2;
+FragentShaderConstants *gFSConstants;
+FragentShaderConstants2 *gFSConstants2;
+// Export these out to easily set the inputs to shader
+VertexShaderInputs *gVSInputs;
+// Custom shaders we use for lighting
+rs_program_vertex gProgVertexCustom;
+rs_program_fragment gProgFragmentCustom;
+rs_program_vertex gProgVertexCustom2;
+rs_program_fragment gProgFragmentCustom2;
+rs_program_vertex gProgVertexCube;
+rs_program_fragment gProgFragmentCube;
+rs_program_fragment gProgFragmentMultitex;
+
+float gDt = 0;
+
+void init() {
+}
+
+static void displayFontSamples() {
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    int yPos = 100;
+    rsgBindFont(gFontSans);
+    rsgDrawText("Sans font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
+    rsgBindFont(gFontSerif);
+    rsgDrawText("Serif font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.7f, 0.7f, 0.7f, 1.0f);
+    rsgBindFont(gFontSerifBold);
+    rsgDrawText("Serif Bold font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.5f, 0.9f, 1.0f);
+    rsgBindFont(gFontSerifItalic);
+    rsgDrawText("Serif Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+    rsgDrawText("Serif Bold Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgBindFont(gFontMono);
+    rsgDrawText("Monospace font sample", 30, yPos);
+    yPos += 50;
+
+    // Now use text metrics to center the text
+    uint width = rsgGetWidth();
+    uint height = rsgGetHeight();
+    int left = 0, right = 0, top = 0, bottom = 0;
+
+    rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+
+    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
+    int centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(gTextAlloc, centeredPos, yPos);
+    yPos += 30;
+
+    const char* text = "Centered Text Sample";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    rsgBindFont(gFontSans);
+    text = "More Centered Text Samples";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    // Now draw bottom and top right aligned text
+    text = "Top-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, top);
+
+    text = "Top-left";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, -left, top);
+
+    text = "Bottom-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, height + bottom);
+
+}
+
+static void bindProgramVertexOrtho() {
+    // Default vertex sahder
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix
+    rs_matrix4x4 proj;
+    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+}
+
+static void displayShaderSamples() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    startX = 200; startY = 0;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTransparent);
+    startX = 0; startY = 200;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    // Fragment program with simple color
+    rsgBindProgramFragment(gProgFragmentColor);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.9, 0.3, 0.3, 1);
+    rsgDrawRect(200, 300, 350, 450, 0);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.3, 0.9, 0.3, 1);
+    rsgDrawRect(50, 400, 400, 600, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Texture shader", 10, 50);
+    rsgDrawText("Alpha-blended texture shader", 10, 280);
+    rsgDrawText("Flat color shader", 100, 450);
+}
+
+static void displayBlendingSamples() {
+    int i;
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgBindProgramFragment(gProgFragmentColor);
+
+    rsgBindProgramStore(gProgStoreBlendNone);
+    for (i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.1f*iPlusOne, 0.2f*iPlusOne, 0.3f*iPlusOne, 1);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(0, yPos, 200, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    for (i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.2f*iPlusOne, 0.3f*iPlusOne, 0.1f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(150, yPos, 350, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAdd);
+    for (i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.3f*iPlusOne, 0.1f*iPlusOne, 0.2f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(300, yPos, 500, yPos + 200, 0);
+    }
+
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("No Blending", 10, 50);
+    rsgDrawText("Alpha Blending", 160, 150);
+    rsgDrawText("Additive Blending", 320, 250);
+
+}
+
+static void displayMeshSamples() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadTranslate(&matrix, 128, 128, 0);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    rsgDrawMesh(gMbyNMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("User gen 10 by 10 grid mesh", 10, 250);
+}
+
+static void displayTextureSamplers() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    // Linear clamp
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    float startX = 0, startY = 0;
+    float width = 300, height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Linear Wrap
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearWrap);
+    startX = 0; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Nearest
+    rsgBindSampler(gProgFragmentTexture, 0, gNearestClamp);
+    startX = 300; startY = 0;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+    startX = 300; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.5,
+                         startX + width, startY + height, 0, 1.5, 1.5,
+                         startX + width, startY, 0, 1.5, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Filtering: linear clamp", 10, 290);
+    rsgDrawText("Filtering: linear wrap", 10, 590);
+    rsgDrawText("Filtering: nearest clamp", 310, 290);
+    rsgDrawText("Filtering: miplinear wrap", 310, 590);
+}
+
+static float gTorusRotation = 0;
+
+static void displayCullingSamples() {
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix with 60 degree field of view
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
+
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    rs_matrix4x4 matrix;
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&matrix, -2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use front face culling
+    rsgBindProgramRaster(gCullFront);
+    rsgDrawMesh(gTorusMesh);
+
+    rsMatrixLoadTranslate(&matrix, 2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Displaying mesh front/back face culling", 10, rsgGetHeight() - 10);
+}
+
+static float gLight0Rotation = 0;
+static float gLight1Rotation = 0;
+
+static void setupCustomShaderLights() {
+    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
+    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
+    float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
+    float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f};
+    float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f};
+    float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f};
+
+    gLight0Rotation += 50.0f * gDt;
+    if (gLight0Rotation > 360.0f) {
+        gLight0Rotation -= 360.0f;
+    }
+    gLight1Rotation -= 50.0f * gDt;
+    if (gLight1Rotation > 360.0f) {
+        gLight1Rotation -= 360.0f;
+    }
+
+    rs_matrix4x4 l0Mat;
+    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
+    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
+    rs_matrix4x4 l1Mat;
+    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
+    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
+
+    // Set light 0 properties
+    gVSConstants->light0_Posision = light0Pos;
+    gVSConstants->light0_Diffuse = 1.0f;
+    gVSConstants->light0_Specular = 0.5f;
+    gVSConstants->light0_CosinePower = 10.0f;
+    // Set light 1 properties
+    gVSConstants->light1_Posision = light1Pos;
+    gVSConstants->light1_Diffuse = 1.0f;
+    gVSConstants->light1_Specular = 0.7f;
+    gVSConstants->light1_CosinePower = 25.0f;
+    rsgAllocationSyncAll(rsGetAllocation(gVSConstants));
+
+    gVSConstants2->light_Posision[0] = light0Pos;
+    gVSConstants2->light_Diffuse[0] = 1.0f;
+    gVSConstants2->light_Specular[0] = 0.5f;
+    gVSConstants2->light_CosinePower[0] = 10.0f;
+    gVSConstants2->light_Posision[1] = light1Pos;
+    gVSConstants2->light_Diffuse[1] = 1.0f;
+    gVSConstants2->light_Specular[1] = 0.7f;
+    gVSConstants2->light_CosinePower[1] = 25.0f;
+    rsgAllocationSyncAll(rsGetAllocation(gVSConstants2));
+
+    // Update fragmetn shader constants
+    // Set light 0 colors
+    gFSConstants->light0_DiffuseColor = light0DiffCol;
+    gFSConstants->light0_SpecularColor = light0SpecCol;
+    // Set light 1 colors
+    gFSConstants->light1_DiffuseColor = light1DiffCol;
+    gFSConstants->light1_SpecularColor = light1SpecCol;
+    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
+
+    gFSConstants2->light_DiffuseColor[0] = light0DiffCol;
+    gFSConstants2->light_SpecularColor[0] = light0SpecCol;
+    // Set light 1 colors
+    gFSConstants2->light_DiffuseColor[1] = light1DiffCol;
+    gFSConstants2->light_SpecularColor[1] = light1SpecCol;
+    rsgAllocationSyncAll(rsGetAllocation(gFSConstants2));
+}
+
+static void displayCustomShaderSamples() {
+
+    // Update vertex shader constants
+    // Load model matrix
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
+    setupCustomShaderLights();
+
+    rsgBindProgramVertex(gProgVertexCustom);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCustom);
+    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
+}
+
+static void displayCustomShaderSamples2() {
+
+    // Update vertex shader constants
+    // Load model matrix
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&gVSConstants2->model[1], 0.0f, 0.0f, -10.0f);
+    rsMatrixLoadIdentity(&gVSConstants2->model[0]);
+    rsMatrixRotate(&gVSConstants2->model[0], gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&gVSConstants2->model[0], gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants2->proj, 30.0f, aspect, 0.1f, 100.0f);
+    setupCustomShaderLights();
+
+    rsgBindProgramVertex(gProgVertexCustom2);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCustom2);
+    rsgBindSampler(gProgFragmentCustom2, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCustom2, 0, gTexTorus);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader sample with array uniforms", 10, rsgGetHeight() - 10);
+}
+
+static void displayCubemapShaderSample() {
+    // Update vertex shader constants
+    // Load model matrix
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Position our model on the screen
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
+
+    rsgBindProgramVertex(gProgVertexCube);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCube);
+    rsgBindSampler(gProgFragmentCube, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCube, 0, gTexCube);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Cubemap shader sample", 10, rsgGetHeight() - 10);
+}
+
+static void displayMultitextureSample() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentMultitex);
+    rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
+    rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
+    rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
+    rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker);
+    rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
+    rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader with multitexturing", 10, 280);
+}
+
+static float gAnisoTime = 0.0f;
+static uint anisoMode = 0;
+static void displayAnisoSample() {
+
+    gAnisoTime += gDt;
+
+    rsgBindProgramVertex(gProgVertex);
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rs_matrix4x4 proj;
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    rs_matrix4x4 matrix;
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, -80, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgBindProgramRaster(gCullNone);
+
+    rsgBindTexture(gProgFragmentTexture, 0, gTexChecker);
+
+    if (gAnisoTime >= 5.0f) {
+        gAnisoTime = 0.0f;
+        anisoMode ++;
+        anisoMode = anisoMode % 3;
+    }
+
+    if (anisoMode == 0) {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso8);
+    } else if (anisoMode == 1) {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso15);
+    } else {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+    }
+
+    float startX = -15;
+    float startY = -15;
+    float width = 30;
+    float height = 30;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 10,
+                         startX + width, startY + height, 0, 10, 10,
+                         startX + width, startY, 0, 10, 0);
+
+    rsgBindProgramRaster(gCullBack);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    if (anisoMode == 0) {
+        rsgDrawText("Anisotropic filtering 8", 10, 40);
+    } else if (anisoMode == 1) {
+        rsgDrawText("Anisotropic filtering 15", 10, 40);
+    } else {
+        rsgDrawText("Miplinear filtering", 10, 40);
+    }
+}
+
+int root(void) {
+
+    gDt = rsGetDt();
+
+    rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    switch (gDisplayMode) {
+    case 0:
+        displayFontSamples();
+        break;
+    case 1:
+        displayShaderSamples();
+        break;
+    case 2:
+        displayBlendingSamples();
+        break;
+    case 3:
+        displayMeshSamples();
+        break;
+    case 4:
+        displayTextureSamplers();
+        break;
+    case 5:
+        displayCullingSamples();
+        break;
+    case 6:
+        displayCustomShaderSamples();
+        break;
+    case 7:
+        displayMultitextureSample();
+        break;
+    case 8:
+        displayAnisoSample();
+        break;
+    case 9:
+        displayCustomShaderSamples2();
+        break;
+    case 10:
+        displayCubemapShaderSample();
+        break;
+    }
+
+    return 10;
+}
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh
new file mode 100644
index 0000000..08cf361
--- /dev/null
+++ b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh
@@ -0,0 +1,83 @@
+// Copyright (C) 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
+//
+//      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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.example.android.rs.miscsamples)
+
+typedef struct VertexShaderConstants_s {
+    rs_matrix4x4 model;
+    rs_matrix4x4 proj;
+    float4 light0_Posision;
+    float light0_Diffuse;
+    float light0_Specular;
+    float light0_CosinePower;
+
+    float4 light1_Posision;
+    float light1_Diffuse;
+    float light1_Specular;
+    float light1_CosinePower;
+} VertexShaderConstants;
+
+typedef struct VertexShaderConstants2_s {
+    rs_matrix4x4 model[2];
+    rs_matrix4x4 proj;
+    float4 light_Posision[2];
+    float light_Diffuse[2];
+    float light_Specular[2];
+    float light_CosinePower[2];
+} VertexShaderConstants2;
+
+typedef struct VertexShaderConstants3_s {
+    rs_matrix4x4 model;
+    rs_matrix4x4 proj;
+    float time;
+} VertexShaderConstants3;
+
+
+typedef struct FragentShaderConstants_s {
+    float4 light0_DiffuseColor;
+    float4 light0_SpecularColor;
+
+    float4 light1_DiffuseColor;
+    float4 light1_SpecularColor;
+} FragentShaderConstants;
+
+typedef struct FragentShaderConstants2_s {
+    float4 light_DiffuseColor[2];
+    float4 light_SpecularColor[2];
+} FragentShaderConstants2;
+
+typedef struct FragentShaderConstants3_s {
+    float4 light0_DiffuseColor;
+    float4 light0_SpecularColor;
+    float4 light0_Posision;
+    float light0_Diffuse;
+    float light0_Specular;
+    float light0_CosinePower;
+
+    float4 light1_DiffuseColor;
+    float4 light1_SpecularColor;
+    float4 light1_Posision;
+    float light1_Diffuse;
+    float light1_Specular;
+    float light1_CosinePower;
+} FragentShaderConstants3;
+
+typedef struct VertexShaderInputs_s {
+    float4 position;
+    float3 normal;
+    float2 texture0;
+} VertexShaderInputs;
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index 9321cb3..d0f3e62 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -72,6 +72,7 @@
         unitTests.add(new UT_clamp(this, mRes, mCtx));
         unitTests.add(new UT_clamp_relaxed(this, mRes, mCtx));
         unitTests.add(new UT_convert(this, mRes, mCtx));
+        unitTests.add(new UT_convert_relaxed(this, mRes, mCtx));
         unitTests.add(new UT_rsdebug(this, mRes, mCtx));
         unitTests.add(new UT_rstime(this, mRes, mCtx));
         unitTests.add(new UT_rstypes(this, mRes, mCtx));
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_convert_relaxed.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_convert_relaxed.java
new file mode 100644
index 0000000..728806c
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_convert_relaxed.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_convert_relaxed extends UnitTest {
+    private Resources mRes;
+
+    protected UT_convert_relaxed(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Convert (Relaxed)", ctx);
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        ScriptC_convert_relaxed s =
+                new ScriptC_convert_relaxed(pRS, mRes, R.raw.convert_relaxed);
+        pRS.setMessageHandler(mRsMessage);
+        s.invoke_convert_test();
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/convert_relaxed.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/convert_relaxed.rs
new file mode 100644
index 0000000..81abb9b
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/convert_relaxed.rs
@@ -0,0 +1,2 @@
+#include "convert.rs"
+#pragma rs_fp_relaxed
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index ec61403..f2fa3f5 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -56,55 +56,93 @@
     return true;
 }
 
+// The default to use if no other ignore pattern is defined.
+const char * const gDefaultIgnoreAssets =
+    "!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~";
+// The ignore pattern that can be passed via --ignore-assets in Main.cpp
+const char * gUserIgnoreAssets = NULL;
+
 static bool isHidden(const char *root, const char *path)
 {
-    const char *ext  = NULL;
-    const char *type = NULL;
+    // Patterns syntax:
+    // - Delimiter is :
+    // - Entry can start with the flag ! to avoid printing a warning
+    //   about the file being ignored.
+    // - Entry can have the flag "<dir>" to match only directories
+    //   or <file> to match only files. Default is to match both.
+    // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+    //   where prefix/suffix must have at least 1 character (so that
+    //   we don't match a '*' catch-all pattern.)
+    // - The special filenames "." and ".." are always ignored.
+    // - Otherwise the full string is matched.
+    // - match is not case-sensitive.
 
-    // Skip all hidden files.
-    if (path[0] == '.') {
-        // Skip ., .. and  .svn but don't chatter about it.
-        if (strcmp(path, ".") == 0
-            || strcmp(path, "..") == 0
-            || strcmp(path, ".svn") == 0) {
-            return true;
-        }
-        type = "hidden";
-    } else if (path[0] == '_') {
-        // skip directories starting with _ (don't chatter about it)
-        String8 subdirName(root);
-        subdirName.appendPath(path);
-        if (getFileType(subdirName.string()) == kFileTypeDirectory) {
-            return true;
-        }
-    } else if (strcmp(path, "CVS") == 0) {
-        // Skip CVS but don't chatter about it.
+    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
         return true;
-    } else if (strcasecmp(path, "thumbs.db") == 0
-               || strcasecmp(path, "picasa.ini") == 0) {
-        // Skip suspected image indexes files.
-        type = "index";
-    } else if (path[strlen(path)-1] == '~') {
-        // Skip suspected emacs backup files.
-        type = "backup";
-    } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) {
-        // Skip VisualSourceSafe files and don't chatter about it
-        return true;
-    } else {
-        // Let everything else through.
-        return false;
     }
 
-    /* If we get this far, "type" should be set and the file
-     * should be skipped.
-     */
-    String8 subdirName(root);
-    subdirName.appendPath(path);
-    fprintf(stderr, "    (skipping %s %s '%s')\n", type,
-            getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
-            subdirName.string());
+    const char *delim = ":";
+    const char *p = gUserIgnoreAssets;
+    if (!p || !p[0]) {
+        p = getenv("ANDROID_AAPT_IGNORE");
+    }
+    if (!p || !p[0]) {
+        p = gDefaultIgnoreAssets;
+    }
+    char *patterns = strdup(p);
 
-    return true;
+    bool ignore = false;
+    bool chatty = true;
+    char *matchedPattern = NULL;
+
+    String8 fullPath(root);
+    fullPath.appendPath(path);
+    FileType type = getFileType(fullPath);
+
+    int plen = strlen(path);
+
+    // Note: we don't have strtok_r under mingw.
+    for(char *token = strtok(patterns, delim);
+            !ignore && token != NULL;
+            token = strtok(NULL, delim)) {
+        chatty = token[0] != '!';
+        if (!chatty) token++; // skip !
+        if (strncasecmp(token, "<dir>" , 5) == 0) {
+            if (type != kFileTypeDirectory) continue;
+            token += 5;
+        }
+        if (strncasecmp(token, "<file>", 6) == 0) {
+            if (type != kFileTypeRegular) continue;
+            token += 6;
+        }
+
+        matchedPattern = token;
+        int n = strlen(token);
+
+        if (token[0] == '*') {
+            // Match *suffix
+            token++;
+            n--;
+            if (n <= plen) {
+                ignore = strncasecmp(token, path + plen - n, n) == 0;
+            }
+        } else if (n > 1 && token[n - 1] == '*') {
+            // Match prefix*
+            ignore = strncasecmp(token, path, n - 1) == 0;
+        } else {
+            ignore = strcasecmp(token, path) == 0;
+        }
+    }
+
+    if (ignore && chatty) {
+        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
+                type == kFileTypeDirectory ? "dir" : "file",
+                path,
+                matchedPattern ? matchedPattern : "");
+    }
+
+    free(patterns);
+    return ignore;
 }
 
 // =========================================================================
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 5924952..d5f296c 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -22,6 +22,10 @@
 
 using namespace android;
 
+
+extern const char * const gDefaultIgnoreAssets;
+extern const char * gUserIgnoreAssets;
+
 bool valid_symbol_name(const String8& str);
 
 class AaptAssets;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 50c828d..9f05c6a 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -178,7 +178,11 @@
         "   --non-constant-id\n"
         "       Make the resources ID non constant. This is required to make an R java class\n"
         "       that does not contain the final value but is used to make reusable compiled\n"
-        "       libraries that need to access resources.\n");
+        "       libraries that need to access resources.\n"
+        "   --ignore-assets\n"
+        "       Assets to be ignored. Default pattern is:\n"
+        "       %s\n",
+        gDefaultIgnoreAssets);
 }
 
 /*
@@ -556,7 +560,16 @@
                     bundle.setNonConstantId(true);
                 } else if (strcmp(cp, "-no-crunch") == 0) {
                     bundle.setUseCrunchCache(true);
-                }else {
+                } else if (strcmp(cp, "-ignore-assets") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--ignore-assets' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    gUserIgnoreAssets = argv[0];
+                } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
                     goto bail;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b9ec30c..a69adc1 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2089,6 +2089,23 @@
     keep->add(rule, location);
 }
 
+void
+addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
+        const char* pkg, const String8& srcName, int line)
+{
+    String8 rule("-keepclassmembers class * { *** ");
+    rule += memberName;
+    rule += "(...); }";
+
+    String8 location("onClick ");
+    location += srcName;
+    char lineno[20];
+    sprintf(lineno, ":%d", line);
+    location += lineno;
+
+    keep->add(rule, location);
+}
+
 status_t
 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
 {
@@ -2251,6 +2268,13 @@
                 }
             }
         }
+        ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
+        if (attrIndex >= 0) {
+            size_t len;
+            addProguardKeepMethodRule(keep,
+                                String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+                                layoutFile->getPrintableSource(), tree.getLineNumber());
+        }
     }
 
     return NO_ERROR;
@@ -2289,6 +2313,9 @@
         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
             startTag = "PreferenceScreen";
             tagAttrPairs = &kXmlTagAttrPairs;
+        } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
+            startTag = "menu";
+            tagAttrPairs = NULL;
         } else {
             continue;
         }
diff --git a/tools/preload/Record.java b/tools/preload/Record.java
index 2f2ffaf..ac99f1c 100644
--- a/tools/preload/Record.java
+++ b/tools/preload/Record.java
@@ -30,8 +30,22 @@
             "com.google.android.apps.maps\\u003Adriveabout",
             "com.google.android.apps.maps:LocationFriendService",
             "com.google.android.apps.maps\\u003ALocationFriendService",
+            "com.google.android.apps.maps:MapsBackgroundService",
+            "com.google.android.apps.maps\\u003AMapsBackgroundService",
             "com.google.android.apps.maps:NetworkLocationService",
             "com.google.android.apps.maps\\u003ANetworkLocationService",
+            "com.android.fakeoemfeatures:background",
+            "com.android.fakeoemfeatures\\u003Abackground",
+            "com.android.fakeoemfeatures:core",
+            "com.android.fakeoemfeatures\\u003Acore",
+            "com.google.android.music:main",
+            "com.google.android.music\\u003Amain",
+            "com.google.android.music:ui",
+            "com.google.android.music\\u003Aui",
+            "com.google.android.setupwarlock:broker",
+            "com.google.android.setupwarlock\\u003Abroker",
+            "android:ui",
+            "android\\u003Aui",
     };
 
     enum Type {
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 0a87a538..63359c1 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -438,6 +438,22 @@
         return doBooleanCommand("SET config_methods " + cfg);
     }
 
+    public boolean setManufacturer(String value) {
+        return doBooleanCommand("SET manufacturer " + value);
+    }
+
+    public boolean setModelName(String value) {
+        return doBooleanCommand("SET model_name " + value);
+    }
+
+    public boolean setModelNumber(String value) {
+        return doBooleanCommand("SET model_number " + value);
+    }
+
+    public boolean setSerialNumber(String value) {
+        return doBooleanCommand("SET serial_number " + value);
+    }
+
     public boolean setP2pSsidPostfix(String postfix) {
         return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 2903faa..a0f3281 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -120,6 +120,7 @@
     private ConnectivityManager mCm;
 
     private final boolean mP2pSupported;
+    private final String mPrimaryDeviceType;
 
     /* Scan results handling */
     private List<ScanResult> mScanResults;
@@ -590,6 +591,9 @@
         mBackgroundScanSupported = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_wifi_background_scan_support);
 
+        mPrimaryDeviceType = mContext.getResources().getString(
+                com.android.internal.R.string.config_wifi_p2p_device_type);
+
         mContext.registerReceiver(
             new BroadcastReceiver() {
                 @Override
@@ -2214,6 +2218,37 @@
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
         }
+
+        private void initializeWpsDetails() {
+            String detail;
+            detail = SystemProperties.get("ro.product.name", "");
+            if (!mWifiNative.setDeviceName(detail)) {
+                loge("Failed to set device name " +  detail);
+            }
+            detail = SystemProperties.get("ro.product.manufacturer", "");
+            if (!mWifiNative.setManufacturer(detail)) {
+                loge("Failed to set manufacturer " + detail);
+            }
+            detail = SystemProperties.get("ro.product.model", "");
+            if (!mWifiNative.setModelName(detail)) {
+                loge("Failed to set model name " + detail);
+            }
+            detail = SystemProperties.get("ro.product.model", "");
+            if (!mWifiNative.setModelNumber(detail)) {
+                loge("Failed to set model number " + detail);
+            }
+            detail = SystemProperties.get("ro.serialno", "");
+            if (!mWifiNative.setSerialNumber(detail)) {
+                loge("Failed to set serial number " + detail);
+            }
+            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button keypad")) {
+                loge("Failed to set WPS config methods");
+            }
+            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
+                loge("Failed to set primary device type " + mPrimaryDeviceType);
+            }
+        }
+
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
@@ -2231,8 +2266,8 @@
                     mLastSignalLevel = -1;
 
                     mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
-
                     mWifiConfigStore.initialize();
+                    initializeWpsDetails();
 
                     sendSupplicantConnectionChangedBroadcast(true);
                     transitionTo(mDriverStartedState);