Merge "Fix the display name of InputMethodSubtype"
diff --git a/Android.mk b/Android.mk
index e987f91..90ffcd3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,11 +127,13 @@
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/IPermissionController.aidl \
 	core/java/android/os/IPowerManager.aidl \
-    core/java/android/os/IRemoteCallback.aidl \
+	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/IVibratorService.aidl \
-    core/java/android/service/wallpaper/IWallpaperConnection.aidl \
-    core/java/android/service/wallpaper/IWallpaperEngine.aidl \
-    core/java/android/service/wallpaper/IWallpaperService.aidl \
+	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
+	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
+	core/java/android/service/wallpaper/IWallpaperService.aidl \
+	core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
+	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
diff --git a/api/14.txt b/api/14.txt
index 58d49d9..ac93afb 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -22250,10 +22250,8 @@
 
   public class AccessibilityRecord {
     ctor protected AccessibilityRecord();
-    method protected void clear();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
-    method public boolean getBooleanProperty(int);
     method public java.lang.CharSequence getClassName();
     method public java.lang.CharSequence getContentDescription();
     method public int getCurrentItemIndex();
diff --git a/api/current.txt b/api/current.txt
index afaf385..80d0687 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -90,6 +90,7 @@
     field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+    field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -485,7 +486,6 @@
     field public static final int hint = 16843088; // 0x1010150
     field public static final int homeAsUpIndicator = 16843531; // 0x101030b
     field public static final int homeLayout = 16843549; // 0x101031d
-    field public static final int horizontalDirection = 16843631; // 0x101036f
     field public static final int horizontalDivider = 16843053; // 0x101012d
     field public static final int horizontalGap = 16843327; // 0x101023f
     field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
@@ -570,6 +570,7 @@
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
+    field public static final int layoutDirection = 16843631; // 0x101036f
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
     field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -1515,12 +1516,13 @@
     field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075
     field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076
     field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0
+    field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1
     field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c
-    field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3
+    field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974068; // 0x10300f4
     field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c
     field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d
     field public static final int Theme_Holo_Panel = 16973947; // 0x103007b
-    field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2
+    field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974067; // 0x10300f3
     field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d
     field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e
     field public static final int Theme_InputMethod = 16973908; // 0x1030054
@@ -4966,6 +4968,7 @@
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
     field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
     field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
+    field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
     field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
@@ -5014,8 +5017,8 @@
     field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
     field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
     field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
-    field public static final java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
-    field public static final java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+    field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
+    field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
     field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
     field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
@@ -16438,6 +16441,7 @@
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
     method public void syncAll(int);
     field public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
+    field public static final int USAGE_GRAPHICS_RENDER_TARGET = 16; // 0x10
     field public static final int USAGE_GRAPHICS_TEXTURE = 2; // 0x2
     field public static final int USAGE_GRAPHICS_VERTEX = 4; // 0x4
     field public static final int USAGE_SCRIPT = 1; // 0x1
@@ -20435,14 +20439,12 @@
     method public static int getAbsoluteGravity(int, boolean);
     method public static boolean isHorizontal(int);
     method public static boolean isVertical(int);
-    field public static final int AFTER = 8388613; // 0x800005
     field public static final int AXIS_CLIP = 8; // 0x8
     field public static final int AXIS_PULL_AFTER = 4; // 0x4
     field public static final int AXIS_PULL_BEFORE = 2; // 0x2
     field public static final int AXIS_SPECIFIED = 1; // 0x1
     field public static final int AXIS_X_SHIFT = 0; // 0x0
     field public static final int AXIS_Y_SHIFT = 4; // 0x4
-    field public static final int BEFORE = 8388611; // 0x800003
     field public static final int BOTTOM = 80; // 0x50
     field public static final int CENTER = 17; // 0x11
     field public static final int CENTER_HORIZONTAL = 1; // 0x1
@@ -20451,15 +20453,17 @@
     field public static final int CLIP_VERTICAL = 128; // 0x80
     field public static final int DISPLAY_CLIP_HORIZONTAL = 16777216; // 0x1000000
     field public static final int DISPLAY_CLIP_VERTICAL = 268435456; // 0x10000000
+    field public static final int END = 8388613; // 0x800005
     field public static final int FILL = 119; // 0x77
     field public static final int FILL_HORIZONTAL = 7; // 0x7
     field public static final int FILL_VERTICAL = 112; // 0x70
     field public static final int HORIZONTAL_GRAVITY_MASK = 7; // 0x7
     field public static final int LEFT = 3; // 0x3
     field public static final int NO_GRAVITY = 0; // 0x0
-    field public static final int RELATIVE_HORIZONTAL_DIRECTION = 8388608; // 0x800000
     field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
     field public static final int RIGHT = 5; // 0x5
+    field public static final int START = 8388611; // 0x800003
     field public static final int TOP = 48; // 0x30
     field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70
   }
@@ -21477,6 +21481,7 @@
     method protected int computeVerticalScrollExtent();
     method protected int computeVerticalScrollOffset();
     method protected int computeVerticalScrollRange();
+    method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -21505,6 +21510,7 @@
     method public android.view.View findFocus();
     method public final android.view.View findViewById(int);
     method public final android.view.View findViewWithTag(java.lang.Object);
+    method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence);
     method protected boolean fitSystemWindows(android.graphics.Rect);
     method public android.view.View focusSearch(int);
     method public void forceLayout();
@@ -21673,6 +21679,7 @@
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
     method public boolean onHoverEvent(android.view.MotionEvent);
     method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -22566,17 +22573,21 @@
     method public void appendRecord(android.view.accessibility.AccessibilityRecord);
     method public int describeContents();
     method public static java.lang.String eventTypeToString(int);
+    method public int getAccessibilityWindowId();
     method public long getEventTime();
     method public int getEventType();
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityRecord getRecord(int);
     method public int getRecordCount();
+    method public android.view.accessibility.AccessibilityNodeInfo getSource();
     method public void initFromParcel(android.os.Parcel);
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
+    method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
     method public static android.view.accessibility.AccessibilityEvent obtain();
     method public void setEventTime(long);
     method public void setEventType(int);
     method public void setPackageName(java.lang.CharSequence);
+    method public void setSource(android.view.View);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int INVALID_POSITION = -1; // 0xffffffff
@@ -22601,20 +22612,75 @@
   }
 
   public final class AccessibilityManager {
+    method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
     method public void interrupt();
     method public boolean isEnabled();
+    method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
   }
 
+  public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
+    method public abstract void onAccessibilityStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfo implements android.os.Parcelable {
+    method public void addAction(int);
+    method public void addChild(android.view.View);
+    method public int describeContents();
+    method public int getAccessibilityWindowId();
+    method public int getActions();
+    method public void getBounds(android.graphics.Rect);
+    method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getPackageName();
+    method public android.view.accessibility.AccessibilityNodeInfo getParent();
+    method public java.lang.CharSequence getText();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isLongClickable();
+    method public boolean isPassword();
+    method public boolean isSelected();
+    method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+    method public static android.view.accessibility.AccessibilityNodeInfo obtain();
+    method public boolean performAction(int);
+    method public void recycle();
+    method public void setBounds(android.graphics.Rect);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setLongClickable(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setPassword(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setText(java.lang.CharSequence);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public class AccessibilityRecord {
     ctor protected AccessibilityRecord();
-    method protected void clear();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
-    method public boolean getBooleanProperty(int);
     method public java.lang.CharSequence getClassName();
     method public java.lang.CharSequence getContentDescription();
     method public int getCurrentItemIndex();
@@ -22627,6 +22693,7 @@
     method public boolean isEnabled();
     method public boolean isFullScreen();
     method public boolean isPassword();
+    method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
     method public static android.view.accessibility.AccessibilityRecord obtain();
     method public void recycle();
     method public void setAddedCount(int);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 38cacdd..424b70a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,6 +98,8 @@
             runStart();
         } else if (op.equals("startservice")) {
             runStartService();
+        } else if (op.equals("force-stop")) {
+            runForceStop();
         } else if (op.equals("instrument")) {
             runInstrument();
         } else if (op.equals("broadcast")) {
@@ -365,6 +367,10 @@
         }
     }
 
+    private void runForceStop() throws Exception {
+        mAm.forceStopPackage(nextArgRequired());
+    }
+
     private void sendBroadcast() throws Exception {
         Intent intent = makeIntent();
         IntentReceiver receiver = new IntentReceiver();
@@ -851,7 +857,7 @@
                 wm.clearForcedDisplaySize();
             }
         } catch (RemoteException e) {
-       }
+        }
     }
 
     private class IntentReceiver extends IIntentReceiver.Stub {
@@ -1013,6 +1019,8 @@
                 "\n" +
                 "    start a Service: am startservice <INTENT>\n" +
                 "\n" +
+                "    force stop everything associated with a package: force-stop <package>\n" +
+                "\n" +
                 "    send a broadcast Intent: am broadcast <INTENT>\n" +
                 "\n" +
                 "    start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 28fc21a..8bb305d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * An accessibility service runs in the background and receives callbacks by the system
@@ -51,7 +52,11 @@
  * enabling or disabling it in the device settings. After the system binds to a service it
  * calls {@link AccessibilityService#onServiceConnected()}. This method can be
  * overriden by clients that want to perform post binding setup.
+ * </p>
  * <p>
+ * An accessibility service can be configured to receive specific types of accessibility events,
+ * listen only to specific packages, get events from each type only once in a given time frame,
+ * retrieve window content, specify a settings activity, etc.
  * </p>
  * There are two approaches for configuring an accessibility service:
  * <ul>
@@ -59,20 +64,23 @@
  *     Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
  *     the service. A service declaration with a meta-data tag is presented below:
  *     <p>
- *     <code>
- *       &lt;service android:name=".MyAccessibilityService"&gt;<br>
- *       &nbsp;&nbsp;&lt;intent-filter&gt;<br>
- *       &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
- *       &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
- *       &nbsp;&nbsp;&lt;meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /&gt;<br>
- *       &lt;/service&gt;<br>
- *     </code>
+ *       <code>
+ *         &lt;service android:name=".MyAccessibilityService"&gt;<br>
+ *         &nbsp;&nbsp;&lt;intent-filter&gt;<br>
+ *         &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
+ *         &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
+ *         &nbsp;&nbsp;&lt;meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /&gt;<br>
+ *         &lt;/service&gt;<br>
+ *       </code>
  *     </p>
  *     <p>
  *     <strong>
  *       This approach enables setting all accessibility service properties.
  *     </strong>
  *     </p>
+ *     <p>
+ *       For more details refer to {@link #SERVICE_META_DATA}.
+ *     </p>
  *   </li>
  *   <li>
  *     Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
@@ -88,6 +96,9 @@
  *       {@link AccessibilityServiceInfo#packageNames}
  *     </strong>
  *     </p>
+ *     <p>
+ *       For more details refer to {@link AccessibilityServiceInfo}.
+ *     </p>
  *   </li>
  * </ul>
  * <p>
@@ -151,16 +162,49 @@
      * <code>
      *   &lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
      *   &lt;accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br>
-     *   &nbsp;&nbsp;android:eventTypes="typeViewClicked|typeViewFocused"<br>
+     *   &nbsp;&nbsp;android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br>
      *   &nbsp;&nbsp;android:packageNames="foo.bar, foo.baz"<br>
-     *   &nbsp;&nbsp;android:feedbackType="feedbackSpoken"<br>
+     *   &nbsp;&nbsp;android:accessibilityFeedbackType="feedbackSpoken"<br>
      *   &nbsp;&nbsp;android:notificationTimeout="100"<br>
-     *   &nbsp;&nbsp;android:flags="flagDefault"<br>
+     *   &nbsp;&nbsp;android:accessibilityFlags="flagDefault"<br>
      *   &nbsp;&nbsp;android:settingsActivity="foo.bar.TestBackActivity"<br>
      *   &nbsp;&nbsp;. . .<br>
      *   /&gt;
      * </code>
      * </p>
+     * <p>
+     *  <strong>Note:</strong> A service can retrieve only the content of the active window.
+     *          An active window is the source of the most recent event of type
+     *          {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START},
+     *          {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END},
+     *          {@link AccessibilityEvent#TYPE_VIEW_CLICKED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_FOCUSED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
+     *          {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
+     *          {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_SELECTED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED},
+     *          {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
+     *          Therefore the service should:
+     *          <ul>
+     *            <li>
+     *              Register for all event types with no notification timeout and keep track
+     *              for the active window by calling
+     *              {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received
+     *              event and compare this with the
+     *              {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling
+     *              retrieval methods on the latter.
+     *            </li>
+     *            <li>
+     *              Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail
+     *              since the active window has changed and the service did not get the 
+     *              accessibility event. Note that it is possible to have a retrieval method
+     *              failing event adopting the strategy specified in the previous bullet
+     *              because the accessibility event dispatch is asynchronous and crosses
+     *              process boundaries. 
+     *            </li>
+     *          <ul>
+     * </p>
      */
     public static final String SERVICE_META_DATA = "android.accessibilityservice";
 
@@ -224,7 +268,7 @@
 
     /**
      * Implement to return the implementation of the internal accessibility
-     * service interface.  Subclasses should not override.
+     * service interface.
      */
     @Override
     public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7157def..19f0bf0 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -17,14 +17,72 @@
 package android.accessibilityservice;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
- * Interface AccessibilityManagerService#Service implements, and passes to an
- * AccessibilityService so it can dynamically configure how the system handles it.
+ * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
  *
  * @hide
  */
-oneway interface IAccessibilityServiceConnection {
+interface IAccessibilityServiceConnection {
 
     void setServiceInfo(in AccessibilityServiceInfo info);
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param accessibilityWindowId A unique window id.
+     * @param accessibilityViewId A unique View accessibility id.
+     * @return The node info.
+     */
+    AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+        int accessibilityViewId);
+
+    /**
+     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+     * insensitive containment.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received infos by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param text The searched text.
+     * @return A list of node info.
+     */
+    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text);
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by View id.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param id The id of the node.
+     * @return The node info.
+     */
+    AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId);
+
+    /**
+     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
+     *
+     * @param accessibilityWindowId The id of the window.
+     * @param accessibilityViewId The of a view in the .
+     * @return Whether the action was performed.
+     */
+    boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
+        int action);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 17f8adb..63f3258 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -278,7 +278,6 @@
                 int token, IBackupManager callbackBinder) throws RemoteException {
             long ident = Binder.clearCallingIdentity();
             try {
-Log.d(TAG, "doRestoreFile() => onRestoreFile()");
                 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
             } catch (IOException e) {
                 throw new RuntimeException(e);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index dfb0dd7..3b70e19 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -46,7 +46,7 @@
     public static final String SHARED_STORAGE_TOKEN = "shared";
 
     public static final String APPS_PREFIX = "apps/";
-    public static final String SHARED_PREFIX = "shared/";
+    public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
 
     public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
     public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
@@ -61,7 +61,8 @@
             String linkdomain, String rootpath, String path, BackupDataOutput output);
 
     static public void restoreToFile(ParcelFileDescriptor data,
-            long size, int type, long mode, long mtime, File outFile) throws IOException {
+            long size, int type, long mode, long mtime, File outFile,
+            boolean doChmod) throws IOException {
         if (type == FullBackup.TYPE_DIRECTORY) {
             // Canonically a directory has no associated content, so we don't need to read
             // anything from the pipe in this case.  Just create the directory here and
@@ -116,7 +117,7 @@
         }
 
         // Now twiddle the state to match the backup, assuming all went well
-        if (outFile != null) {
+        if (doChmod && outFile != null) {
             try {
                 Libcore.os.chmod(outFile.getPath(), (int)mode);
             } catch (ErrnoException e) {
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index 4dca593..df1c363 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -28,8 +28,6 @@
 import libcore.io.StructStat;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -84,9 +82,10 @@
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-            ParcelFileDescriptor newState) {
+            ParcelFileDescriptor newState) throws IOException {
         // Filters, the scan queue, and the set of resulting entities
         HashSet<String> filterSet = new HashSet<String>();
+        String packageName = getPackageName();
 
         // Okay, start with the app's root tree, but exclude all of the canonical subdirs
         if (mLibDir != null) {
@@ -96,25 +95,28 @@
         filterSet.add(mDatabaseDir);
         filterSet.add(mSharedPrefsDir);
         filterSet.add(mFilesDir);
-        processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+        processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
 
         // Now do the same for the files dir, db dir, and shared prefs dir
         filterSet.add(mMainDir);
         filterSet.remove(mFilesDir);
-        processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+        processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
 
         filterSet.add(mFilesDir);
         filterSet.remove(mDatabaseDir);
-        processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+        processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
 
         filterSet.add(mDatabaseDir);
         filterSet.remove(mSharedPrefsDir);
-        processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+        processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
     }
 
-    private void processTree(String domain, String rootPath,
+    // Scan the dir tree (if it actually exists) and process each entry we find.  If the
+    // 'excludes' parameter is non-null, it is consulted each time a new file system entity
+    // is visited to see whether that entity (and its subtree, if appropriate) should be
+    // omitted from the backup process.
+    protected void processTree(String packageName, String domain, String rootPath,
             HashSet<String> excludes, BackupDataOutput data) {
-        // Scan the dir tree (if it actually exists) and process each entry we find
         File rootFile = new File(rootPath);
         if (rootFile.exists()) {
             LinkedList<File> scanQueue = new LinkedList<File>();
@@ -125,7 +127,7 @@
                 String filePath = file.getAbsolutePath();
 
                 // prune this subtree?
-                if (excludes.contains(filePath)) {
+                if (excludes != null && excludes.contains(filePath)) {
                     continue;
                 }
 
@@ -149,7 +151,7 @@
                 }
 
                 // Finally, back this file up before proceeding
-                FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+                FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
             }
         }
     }
@@ -218,6 +220,6 @@
         if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
 
         // Now that we've figured out where the data goes, send it on its way
-        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 26707c9..a8c31f9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -873,10 +873,10 @@
 
     /**
      * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
-     * <p>The link key will be unauthenticated i.e the communication is
+     * <p>The link key is not required to be authenticated, i.e the communication may be
      * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
-     * the link key will be encrypted, as encryption is mandartory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link key will not
+     * the link will be encrypted, as encryption is mandartory.
+     * For legacy devices (pre Bluetooth 2.1 devices) the link will not
      * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
      * encrypted and authenticated communication channel is desired.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
@@ -902,6 +902,44 @@
         return createNewRfcommSocketAndRecord(name, uuid, false, false);
     }
 
+     /**
+     * Create a listening, encrypted,
+     * RFCOMM Bluetooth socket with Service Record.
+     * <p>The link will be encrypted, but the link key is not required to be authenticated
+     * i.e the communication is vulnerable to Man In the Middle attacks. Use
+     * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
+     * <p> Use this socket if authentication of link key is not possible.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not have
+     * an input and output capability or just has the ability to display a numeric key,
+     * a secure socket connection is not possible and this socket can be used.
+     * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
+     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+     * connections from a listening {@link BluetoothServerSocket}.
+     * <p>The system will assign an unused RFCOMM channel to listen on.
+     * <p>The system will also register a Service Discovery
+     * Protocol (SDP) record with the local SDP server containing the specified
+     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
+     * can use the same UUID to query our SDP server and discover which channel
+     * to connect to. This SDP record will be removed when this socket is
+     * closed, or if this application closes unexpectedly.
+     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
+     * connect to this socket from another device using the same {@link UUID}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * @param name service name for SDP record
+     * @param uuid uuid for SDP record
+     * @return a listening RFCOMM BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions, or channel in use.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
+            String name, UUID uuid) throws IOException {
+        return createNewRfcommSocketAndRecord(name, uuid, false, true);
+    }
+
     private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
             boolean auth, boolean encrypt) throws IOException {
         RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
@@ -973,6 +1011,28 @@
         return socket;
     }
 
+     /**
+     * Construct an encrypted, RFCOMM server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
+            throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, false, true, port);
+        int errno = socket.mSocket.bindListen();
+        if (errno != 0) {
+            try {
+                socket.close();
+            } catch (IOException e) {}
+            socket.mSocket.throwErrnoNative(errno);
+        }
+        return socket;
+    }
+
     /**
      * Construct a SCO server socket.
      * Call #accept to retrieve connections to this socket.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3d637e9..2f9627a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1160,6 +1160,15 @@
     public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
 
     /**
+     * Activity Action: Show settings for managing network data usage of a
+     * specific application. Applications should define an activity that offers
+     * options to control data usage.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_NETWORK_USAGE =
+            "android.intent.action.MANAGE_NETWORK_USAGE";
+
+    /**
      * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
      * describing the last run version of the platform that was setup.
      * @hide
@@ -1654,8 +1663,9 @@
      * This is used mainly for the USB Settings panel.
      * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
      * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
 
     /**
@@ -1663,8 +1673,9 @@
      * This is used mainly for the USB Settings panel.
      * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
      * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 09fede0..31ad6e9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,11 +1517,12 @@
             }
         }
 
+        // fullBackupAgent is explicitly handled even if allowBackup is false
         name = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
         if (name != null) {
             ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
-            if (true) {
+            if (false) {
                 Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
                         + " from " + pkgName + "+" + name);
             }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d5c4ace..dea5133 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,22 +16,22 @@
 
 package android.hardware;
 
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.StringTokenizer;
-import java.io.IOException;
-
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
 
 /**
  * The Camera class is used to set image capture settings, start/stop preview,
@@ -834,8 +834,6 @@
      * @param raw       the callback for raw (uncompressed) image data, or null
      * @param postview  callback with postview image data, may be null
      * @param jpeg      the callback for JPEG image data, or null
-     *
-     * @see #addRawImageCallbackBuffer(byte[])
      */
     public final void takePicture(ShutterCallback shutter, PictureCallback raw,
             PictureCallback postview, PictureCallback jpeg) {
@@ -1084,10 +1082,14 @@
     };
 
     /**
-     * Area class for focus.
+     * Area class for focus and metering.
      *
-     * @see #setFocusAreas(List)
-     * @see #getFocusAreas()
+     * @see Parameters#setFocusAreas(List)
+     * @see Parameters#getFocusAreas()
+     * @see Parameters#getMaxNumFocusAreas()
+     * @see Parameters#setMeteringAreas(List)
+     * @see Parameters#getMeteringAreas()
+     * @see Parameters#getMaxNumMeteringAreas()
      */
     public static class Area {
         /**
@@ -1121,12 +1123,22 @@
             return weight == a.weight;
         }
 
-        /** rectangle of the area */
+        /**
+         * Rectangle of the area.
+         *
+         * @see Parameters#getFocusAreas()
+         * @see Parameters#getMeteringAreas()
+         */
         public Rect rect;
 
-        /** weight of the area */
+        /**
+         * Weight of the area.
+         *
+         * @see Parameters#getFocusAreas()
+         * @see Parameters#getMeteringAreas()
+         */
         public int weight;
-    };
+    }
 
     /**
      * Camera service settings.
@@ -2775,7 +2787,7 @@
          * The direction is not affected by the rotation or mirroring of
          * {@link #setDisplayOrientation(int)}. Coordinates of the rectangle
          * range from -1000 to 1000. (-1000, -1000) is the upper left point.
-         * (1000, 1000) is the lower right point. The length and width of focus
+         * (1000, 1000) is the lower right point. The width and height of focus
          * areas cannot be 0 or negative.
          *
          * The weight must range from 1 to 1000. The weight should be
@@ -2842,7 +2854,7 @@
          * sensor sees. The direction is not affected by the rotation or
          * mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the
          * rectangle range from -1000 to 1000. (-1000, -1000) is the upper left
-         * point. (1000, 1000) is the lower right point. The length and width of
+         * point. (1000, 1000) is the lower right point. The width and height of
          * metering areas cannot be 0 or negative.
          *
          * The weight must range from 1 to 1000, and represents a weight for
@@ -3033,7 +3045,7 @@
             if (result.size() == 0) return null;
 
             if (result.size() == 1) {
-                Area area = (Area) result.get(0);
+                Area area = result.get(0);
                 Rect rect = area.rect;
                 if (rect.left == 0 && rect.top == 0 && rect.right == 0
                         && rect.bottom == 0 && area.weight == 0) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c72c4b0..21cce44 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -256,10 +256,61 @@
 
     private final IConnectivityManager mService;
 
-    static public boolean isNetworkTypeValid(int networkType) {
+    public static boolean isNetworkTypeValid(int networkType) {
         return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
     }
 
+    /** {@hide} */
+    public static String getNetworkTypeName(int type) {
+        switch (type) {
+            case TYPE_MOBILE:
+                return "MOBILE";
+            case TYPE_WIFI:
+                return "WIFI";
+            case TYPE_MOBILE_MMS:
+                return "MOBILE_MMS";
+            case TYPE_MOBILE_SUPL:
+                return "MOBILE_SUPL";
+            case TYPE_MOBILE_DUN:
+                return "MOBILE_DUN";
+            case TYPE_MOBILE_HIPRI:
+                return "MOBILE_HIPRI";
+            case TYPE_WIMAX:
+                return "WIMAX";
+            case TYPE_BLUETOOTH:
+                return "BLUETOOTH";
+            case TYPE_DUMMY:
+                return "DUMMY";
+            case TYPE_ETHERNET:
+                return "ETHERNET";
+            case TYPE_MOBILE_FOTA:
+                return "MOBILE_FOTA";
+            case TYPE_MOBILE_IMS:
+                return "MOBILE_IMS";
+            case TYPE_MOBILE_CBS:
+                return "MOBILE_CBS";
+            default:
+                return Integer.toString(type);
+        }
+    }
+
+    /** {@hide} */
+    public static boolean isNetworkTypeMobile(int networkType) {
+        switch (networkType) {
+            case TYPE_MOBILE:
+            case TYPE_MOBILE_MMS:
+            case TYPE_MOBILE_SUPL:
+            case TYPE_MOBILE_DUN:
+            case TYPE_MOBILE_HIPRI:
+            case TYPE_MOBILE_FOTA:
+            case TYPE_MOBILE_IMS:
+            case TYPE_MOBILE_CBS:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     public void setNetworkPreference(int preference) {
         try {
             mService.setNetworkPreference(preference);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 647a60a..07f6cece 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
 
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.NetworkState;
 import android.net.ProxyProperties;
 import android.os.IBinder;
 
@@ -40,6 +41,8 @@
     LinkProperties getActiveLinkProperties();
     LinkProperties getLinkProperties(int networkType);
 
+    NetworkState[] getAllNetworkState();
+
     boolean setRadios(boolean onOff);
 
     boolean setRadio(int networkType, boolean turnOn);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 6d57036..d38d16c 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -16,12 +16,18 @@
 
 package android.net;
 
+import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 
 /** {@hide} */
 interface INetworkStatsService {
 
-    NetworkStatsHistory[] getNetworkStatsSummary(int networkType);
-    NetworkStatsHistory getNetworkStatsUid(int uid);
+    /** Return historical stats for traffic that matches template. */
+    NetworkStatsHistory getHistoryForNetwork(int networkTemplate);
+    /** Return historical stats for specific UID traffic that matches template. */
+    NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+
+    /** Return usage summary per UID for traffic that matches template. */
+    NetworkStats getSummaryPerUid(long start, long end, int networkTemplate);
 
 }
diff --git a/core/java/android/net/NetworkState.aidl b/core/java/android/net/NetworkState.aidl
new file mode 100644
index 0000000..c0b6cdc
--- /dev/null
+++ b/core/java/android/net/NetworkState.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.net;
+
+parcelable NetworkState;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
new file mode 100644
index 0000000..749039a
--- /dev/null
+++ b/core/java/android/net/NetworkState.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Snapshot of network state.
+ *
+ * @hide
+ */
+public class NetworkState implements Parcelable {
+
+    public final NetworkInfo networkInfo;
+    public final LinkProperties linkProperties;
+    public final LinkCapabilities linkCapabilities;
+
+    public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
+            LinkCapabilities linkCapabilities) {
+        this.networkInfo = networkInfo;
+        this.linkProperties = linkProperties;
+        this.linkCapabilities = linkCapabilities;
+    }
+
+    public NetworkState(Parcel in) {
+        networkInfo = in.readParcelable(null);
+        linkProperties = in.readParcelable(null);
+        linkCapabilities = in.readParcelable(null);
+    }
+
+    /** {@inheritDoc} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(networkInfo, flags);
+        out.writeParcelable(linkProperties, flags);
+        out.writeParcelable(linkCapabilities, flags);
+    }
+
+    public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+        public NetworkState createFromParcel(Parcel in) {
+            return new NetworkState(in);
+        }
+
+        public NetworkState[] newArray(int size) {
+            return new NetworkState[size];
+        }
+    };
+
+}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 588bf64..ee415fa 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.SparseBooleanArray;
 
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
@@ -125,7 +126,7 @@
     /**
      * Return list of unique interfaces known by this data structure.
      */
-    public String[] getKnownIfaces() {
+    public String[] getUniqueIfaces() {
         final HashSet<String> ifaces = new HashSet<String>();
         for (String iface : this.iface) {
             if (iface != IFACE_ALL) {
@@ -136,6 +137,23 @@
     }
 
     /**
+     * Return list of unique UIDs known by this data structure.
+     */
+    public int[] getUniqueUids() {
+        final SparseBooleanArray uids = new SparseBooleanArray();
+        for (int uid : this.uid) {
+            uids.put(uid, true);
+        }
+
+        final int size = uids.size();
+        final int[] result = new int[size];
+        for (int i = 0; i < size; i++) {
+            result[i] = uids.keyAt(i);
+        }
+        return result;
+    }
+
+    /**
      * Subtract the given {@link NetworkStats}, effectively leaving the delta
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b16101f..5edbf58 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -24,7 +24,9 @@
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.ProtocolException;
 import java.util.Arrays;
+import java.util.Random;
 
 /**
  * Collection of historical network statistics, recorded into equally-sized
@@ -38,28 +40,19 @@
  * @hide
  */
 public class NetworkStatsHistory implements Parcelable {
-    private static final int VERSION = 1;
-
-    /** {@link #uid} value when UID details unavailable. */
-    public static final int UID_ALL = -1;
+    private static final int VERSION_CURRENT = 1;
 
     // TODO: teach about zigzag encoding to use less disk space
     // TODO: teach how to convert between bucket sizes
 
-    public final int networkType;
-    public final String identity;
-    public final int uid;
     public final long bucketDuration;
 
-    int bucketCount;
-    long[] bucketStart;
-    long[] rx;
-    long[] tx;
+    public int bucketCount;
+    public long[] bucketStart;
+    public long[] rx;
+    public long[] tx;
 
-    public NetworkStatsHistory(int networkType, String identity, int uid, long bucketDuration) {
-        this.networkType = networkType;
-        this.identity = identity;
-        this.uid = uid;
+    public NetworkStatsHistory(long bucketDuration) {
         this.bucketDuration = bucketDuration;
         bucketStart = new long[0];
         rx = new long[0];
@@ -68,9 +61,6 @@
     }
 
     public NetworkStatsHistory(Parcel in) {
-        networkType = in.readInt();
-        identity = in.readString();
-        uid = in.readInt();
         bucketDuration = in.readLong();
         bucketStart = readLongArray(in);
         rx = in.createLongArray();
@@ -80,9 +70,6 @@
 
     /** {@inheritDoc} */
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(networkType);
-        out.writeString(identity);
-        out.writeInt(uid);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rx, bucketCount);
@@ -91,21 +78,23 @@
 
     public NetworkStatsHistory(DataInputStream in) throws IOException {
         final int version = in.readInt();
-        networkType = in.readInt();
-        identity = in.readUTF();
-        uid = in.readInt();
-        bucketDuration = in.readLong();
-        bucketStart = readLongArray(in);
-        rx = readLongArray(in);
-        tx = readLongArray(in);
-        bucketCount = bucketStart.length;
+        switch (version) {
+            case VERSION_CURRENT: {
+                bucketDuration = in.readLong();
+                bucketStart = readLongArray(in);
+                rx = readLongArray(in);
+                tx = readLongArray(in);
+                bucketCount = bucketStart.length;
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION);
-        out.writeInt(networkType);
-        out.writeUTF(identity);
-        out.writeInt(uid);
+        out.writeInt(VERSION_CURRENT);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rx, bucketCount);
@@ -145,6 +134,18 @@
     }
 
     /**
+     * Record an entire {@link NetworkStatsHistory} into this history. Usually
+     * for combining together stats for external reporting.
+     */
+    public void recordEntireHistory(NetworkStatsHistory input) {
+        for (int i = 0; i < input.bucketCount; i++) {
+            final long start = input.bucketStart[i];
+            final long end = start + input.bucketDuration;
+            recordData(start, end, input.rx[i], input.tx[i]);
+        }
+    }
+
+    /**
      * Ensure that buckets exist for given time range, creating as needed.
      */
     private void ensureBuckets(long start, long end) {
@@ -213,15 +214,65 @@
         }
     }
 
+    /**
+     * Return interpolated data usage across the requested range. Interpolates
+     * across buckets, so values may be rounded slightly.
+     */
+    public void getTotalData(long start, long end, long[] outTotal) {
+        long rx = 0;
+        long tx = 0;
+
+        for (int i = bucketCount - 1; i >= 0; i--) {
+            final long curStart = bucketStart[i];
+            final long curEnd = curStart + bucketDuration;
+
+            // bucket is older than record; we're finished
+            if (curEnd < start) break;
+            // bucket is newer than record; keep looking
+            if (curStart > end) continue;
+
+            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
+            if (overlap > 0) {
+                rx += this.rx[i] * overlap / bucketDuration;
+                tx += this.tx[i] * overlap / bucketDuration;
+            }
+        }
+
+        outTotal[0] = rx;
+        outTotal[1] = tx;
+    }
+
+    /**
+     * @deprecated only for temporary testing
+     */
+    @Deprecated
+    public void generateRandom(long start, long end, long rx, long tx) {
+        ensureBuckets(start, end);
+
+        final Random r = new Random();
+        while (rx > 1024 && tx > 1024) {
+            final long curStart = randomLong(r, start, end);
+            final long curEnd = randomLong(r, curStart, end);
+            final long curRx = randomLong(r, 0, rx);
+            final long curTx = randomLong(r, 0, tx);
+
+            recordData(curStart, curEnd, curRx, curTx);
+
+            rx -= curRx;
+            tx -= curTx;
+        }
+    }
+
+    private static long randomLong(Random r, long start, long end) {
+        return (long) (start + (r.nextFloat() * (end - start)));
+    }
+
     public void dump(String prefix, PrintWriter pw) {
-        // TODO: consider stripping identity when dumping
         pw.print(prefix);
-        pw.print("NetworkStatsHistory: networkType="); pw.print(networkType);
-        pw.print(" identity="); pw.print(identity);
-        pw.print(" uid="); pw.println(uid);
+        pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
         for (int i = 0; i < bucketCount; i++) {
             pw.print(prefix);
-            pw.print("  timestamp="); pw.print(bucketStart[i]);
+            pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rx="); pw.print(rx[i]);
             pw.print(" tx="); pw.println(tx[i]);
         }
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 3bf64b2..8e50cd5 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -28,6 +28,7 @@
 import javax.net.SocketFactory;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
@@ -86,6 +87,8 @@
 
     private SSLSocketFactory mInsecureFactory = null;
     private SSLSocketFactory mSecureFactory = null;
+    private TrustManager[] mTrustManagers = null;
+    private KeyManager[] mKeyManagers = null;
 
     private final int mHandshakeTimeoutMillis;
     private final SSLClientSessionCache mSessionCache;
@@ -197,10 +200,11 @@
         }
     }
 
-    private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
+    private SSLSocketFactory makeSocketFactory(
+            KeyManager[] keyManagers, TrustManager[] trustManagers) {
         try {
             OpenSSLContextImpl sslContext = new OpenSSLContextImpl();
-            sslContext.engineInit(null, trustManagers, null);
+            sslContext.engineInit(keyManagers, trustManagers, null);
             sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
             return sslContext.engineGetSocketFactory();
         } catch (KeyManagementException e) {
@@ -223,18 +227,44 @@
                 } else {
                     Log.w(TAG, "Bypassing SSL security checks at caller's request");
                 }
-                mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
+                mInsecureFactory = makeSocketFactory(mKeyManagers, INSECURE_TRUST_MANAGER);
             }
             return mInsecureFactory;
         } else {
             if (mSecureFactory == null) {
-                mSecureFactory = makeSocketFactory(null);
+                mSecureFactory = makeSocketFactory(mKeyManagers, mTrustManagers);
             }
             return mSecureFactory;
         }
     }
 
     /**
+     * Sets the {@link TrustManager}s to be used for connections made by this factory.
+     * @hide
+     */
+    public void setTrustManagers(TrustManager[] trustManager) {
+        mTrustManagers = trustManager;
+
+        // Clear out all cached secure factories since configurations have changed.
+        mSecureFactory = null;
+        // Note - insecure factories only ever use the INSECURE_TRUST_MANAGER so they need not
+        // be cleared out here.
+    }
+
+    /**
+     * Sets the {@link KeyManager}s to be used for connections made by this factory.
+     * @hide
+     */
+    public void setKeyManagers(KeyManager[] keyManagers) {
+        mKeyManagers = keyManagers;
+
+        // Clear out any existing cached factories since configurations have changed.
+        mSecureFactory = null;
+        mInsecureFactory = null;
+    }
+
+
+    /**
      * {@inheritDoc}
      *
      * <p>This method verifies the peer's certificate hostname after connecting
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8ab64fa..a0738c1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -41,6 +41,42 @@
      */
     public final static int UNSUPPORTED = -1;
 
+    // TODO: find better home for these template constants
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together. Only uses statistics for currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_ALL = 1;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together that roughly meet a "3G" definition, or lower. Only
+     * uses statistics for currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_3G_LOWER = 2;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together that meet a "4G" definition. Only uses statistics for
+     * currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_4G = 3;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
+     * networks together.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_WIFI = 4;
+
     /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
      * session started, or {@code null} if no session active.
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 69329df..3adcdc3 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -57,6 +57,7 @@
             case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED";
             case ERROR_SE_CONNECTED: return "SE_CONNECTED";
             case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED";
+            case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED";
             default: return "UNKNOWN ERROR";
         }
     }
@@ -105,4 +106,6 @@
 
     public static final int ERROR_NO_SE_CONNECTED = -20;
 
-}
\ No newline at end of file
+    public static final int ERROR_NOT_SUPPORTED = -21;
+
+}
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index 7ec807a..6557ee0 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -77,6 +77,10 @@
                 // Store this in the tag object
                 mTag.setConnectedTechnology(mSelectedTechnology);
                 mIsConnected = true;
+            } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
+                throw new UnsupportedOperationException("Connecting to " +
+                        "this technology is not supported by the NFC " +
+                        "adapter.");
             } else {
                 throw new IOException();
             }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index bc4208a..792e4c1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -32,6 +32,7 @@
     private final boolean mRemovable;
     private final boolean mEmulated;
     private final int mMtpReserveSpace;
+    private final boolean mAllowMassStorage;
     private int mStorageId;
 
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
@@ -39,23 +40,25 @@
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
     public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
 
-    public StorageVolume(String path, String description,
-            boolean removable, boolean emulated, int mtpReserveSpace) {
+    public StorageVolume(String path, String description, boolean removable,
+            boolean emulated, int mtpReserveSpace, boolean allowMassStorage) {
         mPath = path;
         mDescription = description;
         mRemovable = removable;
         mEmulated = emulated;
         mMtpReserveSpace = mtpReserveSpace;
+        mAllowMassStorage = allowMassStorage;
     }
 
     // for parcelling only
-    private StorageVolume(String path, String description,
-            boolean removable, boolean emulated, int mtpReserveSpace, int storageId) {
+    private StorageVolume(String path, String description, boolean removable,
+            boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) {
         mPath = path;
         mDescription = description;
         mRemovable = removable;
         mEmulated = emulated;
         mMtpReserveSpace = mtpReserveSpace;
+        mAllowMassStorage = allowMassStorage;
         mStorageId = storageId;
     }
 
@@ -130,6 +133,15 @@
         return mMtpReserveSpace;
     }
 
+    /**
+     * Returns true if this volume can be shared via USB mass storage.
+     *
+     * @return whether mass storage is allowed
+     */
+    public boolean allowMassStorage() {
+        return mAllowMassStorage;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
@@ -158,9 +170,10 @@
             int emulated = in.readInt();
             int storageId = in.readInt();
             int mtpReserveSpace = in.readInt();
+            int allowMassStorage = in.readInt();
             return new StorageVolume(path, description,
                     removable == 1, emulated == 1,
-                    mtpReserveSpace, storageId);
+                    mtpReserveSpace, storageId, allowMassStorage == 1);
         }
 
         public StorageVolume[] newArray(int size) {
@@ -179,5 +192,6 @@
         parcel.writeInt(mEmulated ? 1 : 0);
         parcel.writeInt(mStorageId);
         parcel.writeInt(mMtpReserveSpace);
+        parcel.writeInt(mAllowMassStorage ? 1 : 0);
     }
 }
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 0ef38bf..a06d0f6 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+
 import android.accounts.Account;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -32,7 +33,6 @@
 import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.pim.ICalendar;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -44,24 +44,30 @@
  * @hide
  */
 public final class Calendar {
-
-    public static final String TAG = "Calendar";
+    private static final String TAG = "Calendar";
 
     /**
-     * Broadcast Action: An event reminder.
+     * Broadcast Action: This is the intent that gets fired when an alarm
+     * notification needs to be posted for a reminder.
      */
     public static final String EVENT_REMINDER_ACTION = "android.intent.action.EVENT_REMINDER";
 
     /**
-     * These are the symbolic names for the keys used in the extra data
-     * passed in the intent for event reminders.
+     * Intent Extras key: The start time of an event or an instance of a
+     * recurring event. (milliseconds since epoch)
      */
     public static final String EVENT_BEGIN_TIME = "beginTime";
+
+    /**
+     * Intent Extras key: The end time of an event or an instance of a recurring
+     * event. (milliseconds since epoch)
+     */
     public static final String EVENT_END_TIME = "endTime";
 
     /**
-     * This must not be changed or horrible, unspeakable things could happen.
-     * For instance, the Calendar app might break. Also, the db might not work.
+     * This authority is used for writing to or querying from the calendar
+     * provider. Note: This is set at first run and cannot be changed without
+     * breaking apps that access the provider.
      */
     public static final String AUTHORITY = "com.android.calendar";
 
@@ -73,20 +79,37 @@
 
     /**
      * An optional insert, update or delete URI parameter that allows the caller
-     * to specify that it is a sync adapter. The default value is false. If true
-     * the dirty flag is not automatically set and the "syncToNetwork" parameter
-     * is set to false when calling
-     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+     * to specify that it is a sync adapter. The default value is false. If set
+     * to true, the modified row is not marked as "dirty" (needs to be synced)
+     * and when the provider calls
+     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+     * , the third parameter "syncToNetwork" is set to false. Furthermore, if
+     * set to true, the caller must also include
+     * {@link SyncColumns#ACCOUNT_NAME} and {@link SyncColumns#ACCOUNT_TYPE} as
+     * query parameters.
+     *
+     * @See Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)
      */
     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
 
+    /**
+     * A special account type for calendars not associated with any account.
+     * Normally calendars that do not match an account on the device will be
+     * removed. Setting the account_type on a calendar to this will prevent it
+     * from being wiped if it does not match an existing account.
+     *
+     * @see SyncColumns#ACCOUNT_TYPE
+     */
+    public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
 
     /**
-     * Generic columns for use by sync adapters. The specific functions of
-     * these columns are private to the sync adapter. Other clients of the API
-     * should not attempt to either read or write this column.
+     * Generic columns for use by sync adapters. The specific functions of these
+     * columns are private to the sync adapter. Other clients of the API should
+     * not attempt to either read or write this column. These columns are
+     * editable as part of the Calendars Uri, but can only be read if accessed
+     * through any other Uri.
      */
-    protected interface BaseSyncColumns {
+    protected interface CalendarSyncColumns {
 
         /** Generic column for use by sync adapters. */
         public static final String CAL_SYNC1 = "cal_sync1";
@@ -103,29 +126,41 @@
     }
 
     /**
-     * Columns for Sync information used by Calendars and Events tables.
+     * Columns for Sync information used by Calendars and Events tables. These
+     * have specific uses which are expected to be consistent by the app and
+     * sync adapter.
+     *
+     * @hide
      */
-    public interface SyncColumns extends BaseSyncColumns {
+    public interface SyncColumns extends CalendarSyncColumns {
         /**
-         * The account that was used to sync the entry to the device.
+         * The account that was used to sync the entry to the device. If the
+         * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
+         * type must match an account on the device or the calendar will be
+         * deleted.
          * <P>Type: TEXT</P>
          */
         public static final String ACCOUNT_NAME = "account_name";
 
         /**
-         * The type of the account that was used to sync the entry to the device.
+         * The type of the account that was used to sync the entry to the
+         * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event
+         * form being deleted if there are no matching accounts on the device.
          * <P>Type: TEXT</P>
          */
         public static final String ACCOUNT_TYPE = "account_type";
 
         /**
-         * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
+         * The unique ID for a row assigned by the sync source. NULL if the row
+         * has never been synced. This is used as a reference id for exceptions
+         * along with {@link BaseColumns#_ID}.
          * <P>Type: TEXT</P>
          */
         public static final String _SYNC_ID = "_sync_id";
 
         /**
-         * The last time, from the sync source's point of view, that this row has been synchronized.
+         * The last time, from the sync source's point of view, that this row
+         * has been synchronized.
          * <P>Type: INTEGER (long)</P>
          */
         public static final String _SYNC_TIME = "_sync_time";
@@ -148,12 +183,21 @@
          */
         public static final String DIRTY = "dirty";
 
+        /**
+         * If set to 1 this causes events on this calendar to be duplicated with
+         * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event transitions from non-dirty
+         * to dirty. The duplicated event will not be expanded in the instances table and will only
+         * show up in sync adapter queries of the events table. It will also be deleted when the
+         * originating event has its dirty flag cleared by the sync adapter.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
     }
 
     /**
-     * Columns from the Calendars table that other tables join into themselves.
+     * Columns specific to the Calendars Uri that other Uris can query.
      */
-    public interface CalendarsColumns {
+    private interface CalendarsColumns {
         /**
          * The color of the calendar
          * <P>Type: INTEGER (color value)</P>
@@ -172,10 +216,17 @@
         public static final int FREEBUSY_ACCESS = 100;
         /** Can read all event details */
         public static final int READ_ACCESS = 200;
+        /** Can reply yes/no/maybe to an event */
         public static final int RESPOND_ACCESS = 300;
+        /** not used */
         public static final int OVERRIDE_ACCESS = 400;
-        /** Full access to modify the calendar, but not the access control settings */
+        /** Full access to modify the calendar, but not the access control
+         * settings
+         */
         public static final int CONTRIBUTOR_ACCESS = 500;
+        /** Full access to modify the calendar, but not the access control
+         * settings
+         */
         public static final int EDITOR_ACCESS = 600;
         /** Full access to the calendar */
         public static final int OWNER_ACCESS = 700;
@@ -184,31 +235,35 @@
 
         /**
          * Is the calendar selected to be displayed?
+         * 0 - do not show events associated with this calendar.
+         * 1 - show events associated with this calendar
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String VISIBLE = "visible";
 
         /**
-         * The timezone the calendar's events occurs in
+         * The time zone the calendar is associated with.
          * <P>Type: TEXT</P>
          */
         public static final String CALENDAR_TIMEZONE = "calendar_timezone";
 
         /**
-         * If this calendar is in the list of calendars that are selected for
-         * syncing then "sync_events" is 1, otherwise 0.
+         * Is this calendar synced and are its events stored on the device?
+         * 0 - Do not sync this calendar or store events for this calendar.
+         * 1 - Sync down events for this calendar.
          * <p>Type: INTEGER (boolean)</p>
          */
         public static final String SYNC_EVENTS = "sync_events";
 
         /**
-         * Sync state data.
+         * Sync state data. Usable by the sync adapter.
          * <p>Type: String (blob)</p>
          */
         public static final String SYNC_STATE = "sync_state";
 
         /**
-         * Whether the row has been deleted.  A deleted row should be ignored.
+         * Whether the row has been deleted but not synced to the server. A
+         * deleted row should be ignored.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String DELETED = "deleted";
@@ -216,35 +271,32 @@
 
     /**
      * Class that represents a Calendar Entity. There is one entry per calendar.
+     * This is a helper class to make batch operations easier.
      */
     public static class CalendarsEntity implements BaseColumns, SyncColumns, CalendarsColumns {
 
+        /**
+         * The default Uri used when creating a new calendar EntityIterator.
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/calendar_entities");
 
-        public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
-            return new EntityIteratorImpl(cursor, resolver);
-        }
-
-        public static EntityIterator newEntityIterator(Cursor cursor,
-                ContentProviderClient provider) {
-            return new EntityIteratorImpl(cursor, provider);
+        /**
+         * Creates an entity iterator for the given cursor. It assumes the
+         * cursor contains a calendars query.
+         *
+         * @param cursor query on {@link #CONTENT_URI}
+         * @return an EntityIterator of calendars
+         */
+        public static EntityIterator newEntityIterator(Cursor cursor) {
+            return new EntityIteratorImpl(cursor);
         }
 
         private static class EntityIteratorImpl extends CursorEntityIterator {
-            private final ContentResolver mResolver;
-            private final ContentProviderClient mProvider;
 
-            public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
+            public EntityIteratorImpl(Cursor cursor) {
                 super(cursor);
-                mResolver = resolver;
-                mProvider = null;
-            }
-
-            public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
-                super(cursor);
-                mResolver = null;
-                mProvider = provider;
             }
 
             @Override
@@ -274,7 +326,8 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         Calendars.DISPLAY_NAME);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+                        Calendars.CALENDAR_COLOR);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
@@ -289,6 +342,8 @@
                         Calendars.CAN_MODIFY_TIME_ZONE);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
                         Calendars.MAX_REMINDERS);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+                        Calendars.CAN_PARTIALLY_UPDATE);
 
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
 
@@ -305,27 +360,40 @@
      }
 
     /**
-     * Contains a list of available calendars.
+     * Fields and helpers for interacting with Calendars.
      */
-    public static class Calendars implements BaseColumns, SyncColumns,
-            CalendarsColumns
-    {
+    public static class Calendars implements BaseColumns, SyncColumns, CalendarsColumns {
         private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
                 + " AND "
                 + Calendars.ACCOUNT_TYPE + "=?";
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                       String where, String orderBy)
-        {
-            return cr.query(CONTENT_URI, projection, where,
-                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        /**
+         * Helper function for generating a calendars query. This is blocking
+         * and should not be used on the UI thread. See
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         * for more details about using the parameters.
+         *
+         * @param cr The ContentResolver to query with
+         * @param projection A list of columns to return
+         * @param selection A formatted selection string
+         * @param selectionArgs arguments to the selection string
+         * @param orderBy How to order the returned rows
+         * @return
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+                String[] selectionArgs, String orderBy) {
+            return cr.query(CONTENT_URI, projection, selection, selectionArgs,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * Convenience method perform a delete on the Calendar provider
+         * Convenience method perform a delete on the Calendar provider. This is
+         * a blocking call and should not be used on the UI thread.
          *
          * @param cr the ContentResolver
-         * @param selection the rows to delete
+         * @param selection A filter to apply to rows before deleting, formatted
+         *            as an SQL WHERE clause (excluding the WHERE itself).
+         * @param selectionArgs Fill in the '?'s in the selection
          * @return the count of rows that were deleted
          */
         public static int delete(ContentResolver cr, String selection, String[] selectionArgs)
@@ -335,10 +403,12 @@
 
         /**
          * Convenience method to delete all calendars that match the account.
+         * This is a blocking call and should not be used on the UI thread.
          *
          * @param cr the ContentResolver
-         * @param account the account whose rows should be deleted
-         * @return the count of rows that were deleted
+         * @param account the account whose calendars and events should be
+         *            deleted
+         * @return the count of calendar rows that were deleted
          */
         public static int deleteCalendarsForAccount(ContentResolver cr, Account account) {
             // delete all calendars that match this account
@@ -348,8 +418,9 @@
         }
 
         /**
-         * The content:// style URL for this table
+         * The content:// style URL for accessing Calendars
          */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
 
         /**
@@ -358,43 +429,43 @@
         public static final String DEFAULT_SORT_ORDER = "displayName";
 
         /**
-         * The URL to the calendar
+         * The URL to the calendar. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String URL = "url";
 
         /**
-         * The URL for the calendar itself
+         * The URL for the calendar itself. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String SELF_URL = "selfUrl";
 
         /**
-         * The URL for the calendar to be edited
+         * The URL for the calendar to be edited. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String EDIT_URL = "editUrl";
 
         /**
-         * The URL for the calendar events
+         * The URL for the calendar events. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String EVENTS_URL = "eventsUrl";
 
         /**
-         * The name of the calendar
+         * The name of the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String NAME = "name";
 
         /**
-         * The display name of the calendar
+         * The display name of the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DISPLAY_NAME = "displayName";
 
         /**
-         * The location the of the events in the calendar
+         * The default location for the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String CALENDAR_LOCATION = "calendar_location";
@@ -402,41 +473,45 @@
         /**
          * The owner account for this calendar, based on the calendar feed.
          * This will be different from the _SYNC_ACCOUNT for delegated calendars.
+         * Column name.
          * <P>Type: String</P>
          */
         public static final String OWNER_ACCOUNT = "ownerAccount";
 
         /**
          * Can the organizer respond to the event?  If no, the status of the
-         * organizer should not be shown by the UI.  Defaults to 1
+         * organizer should not be shown by the UI.  Defaults to 1. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
 
         /**
-         * Can the organizer modify the time zone of the event?
+         * Can the organizer modify the time zone of the event? Column name.
          * <P>Type: INTEGER (boolean)</P>
         */
         public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
 
         /**
-         * The maximum number of reminders allowed for an event.
+         * The maximum number of reminders allowed for an event. Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String MAX_REMINDERS = "maxReminders";
 
         /**
-         * The maximum number of reminders allowed for an event.
-         * <P>
-         * Type: INTEGER
-         * </P>
+         * A comma separated list of reminder methods supported for this
+         * calendar in the format "#,#,#". Valid types are
+         * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
+         * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column
+         * name.
+         * <P>Type: TEXT</P>
          */
         public static final String ALLOWED_REMINDERS = "allowedReminders";
 
         /**
          * These fields are only writable by a sync adapter. To modify them the
-         * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
-         * _SYNC_ACCOUNT_TYPE in the query parameters.
+         * caller must include {@link #CALLER_IS_SYNCADAPTER},
+         * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the query
+         * parameters.
          */
         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
             ACCOUNT_NAME,
@@ -449,6 +524,7 @@
             MAX_REMINDERS,
             CAN_MODIFY_TIME_ZONE,
             CAN_ORGANIZER_RESPOND,
+            CAN_PARTIALLY_UPDATE,
             CALENDAR_LOCATION,
             CALENDAR_TIMEZONE,
             ACCESS_LEVEL,
@@ -457,7 +533,8 @@
             CAL_SYNC2,
             CAL_SYNC3,
             CAL_SYNC4,
- CAL_SYNC5, CAL_SYNC6,
+            CAL_SYNC5,
+            CAL_SYNC6,
             SYNC_STATE,
         };
     }
@@ -465,29 +542,29 @@
     /**
      * Columns from the Attendees table that other tables join into themselves.
      */
-    public interface AttendeesColumns {
+    private interface AttendeesColumns {
 
         /**
-         * The id of the event.
+         * The id of the event. Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The name of the attendee.
+         * The name of the attendee. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ATTENDEE_NAME = "attendeeName";
 
         /**
-         * The email address of the attendee.
+         * The email address of the attendee. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ATTENDEE_EMAIL = "attendeeEmail";
 
         /**
-         * The relationship of the attendee to the user.
-         * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.
+         * The relationship of the attendee to the user. Column name.
+         * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P>
          */
         public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship";
 
@@ -498,8 +575,8 @@
         public static final int RELATIONSHIP_SPEAKER = 4;
 
         /**
-         * The type of attendee.
-         * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})
+         * The type of attendee. Column name.
+         * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})</P>
          */
         public static final String ATTENDEE_TYPE = "attendeeType";
 
@@ -508,8 +585,8 @@
         public static final int TYPE_OPTIONAL = 2;
 
         /**
-         * The attendance status of the attendee.
-         * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...}.
+         * The attendance status of the attendee. Column name.
+         * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P>
          */
         public static final String ATTENDEE_STATUS = "attendeeStatus";
 
@@ -520,59 +597,84 @@
         public static final int ATTENDEE_STATUS_TENTATIVE = 4;
     }
 
+    /**
+     * Fields and helpers for interacting with Attendees.
+     */
     public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns {
-        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
 
-        // TODO: fill out this class when we actually start utilizing attendees
-        // in the calendar application.
+        /**
+         * The content:// style URL for accessing Attendees data
+         */
+        @SuppressWarnings("hiding")
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
+        /**
+         * the projection used by the attendees query
+         */
+        public static final String[] PROJECTION = new String[] {
+                _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
+        private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
+
+        /**
+         * Queries all attendees associated with the given event. This is a
+         * blocking call and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param eventId The id of the event to retrieve attendees for
+         * @return A Cursor containing all attendees for the event
+         */
+        public static final Cursor query(ContentResolver cr, long eventId) {
+            String[] attArgs = {Long.toString(eventId)};
+            return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */,
+                    null /* sort order */);
+        }
     }
 
     /**
      * Columns from the Events table that other tables join into themselves.
      */
-    public interface EventsColumns {
+    private interface EventsColumns {
         /**
-         * For use by sync adapter at its discretion; not modified by CalendarProvider
-         * Note that this column was formerly named _SYNC_LOCAL_ID.  We are using it to avoid a
-         * schema change.
-         * TODO Replace this with something more general in the future.
+         * For use by sync adapter at its discretion. Column name.
+         * TODO change to sync_data2
          * <P>Type: INTEGER (long)</P>
          */
         public static final String _SYNC_DATA = "_sync_local_id";
 
         /**
-         * The calendar the event belongs to
-         * <P>Type: INTEGER (foreign key to the Calendars table)</P>
+         * The {@link Calendars#_ID} of the calendar the event belongs to.
+         * Column name.
+         * <P>Type: INTEGER</P>
          */
         public static final String CALENDAR_ID = "calendar_id";
 
         /**
-         * The URI for an HTML version of this event.
+         * The URI for an HTML version of this event. Column name.
+         * TODO change to sync_data3
          * <P>Type: TEXT</P>
          */
         public static final String HTML_URI = "htmlUri";
 
         /**
-         * The title of the event
+         * The title of the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String TITLE = "title";
 
         /**
-         * The description of the event
+         * The description of the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DESCRIPTION = "description";
 
         /**
-         * Where the event takes place.
+         * Where the event takes place. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EVENT_LOCATION = "eventLocation";
 
         /**
-         * The event status
-         * <P>Type: INTEGER (int)</P>
+         * The event status. Column name.
+         * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
          */
         public static final String STATUS = "eventStatus";
 
@@ -584,64 +686,82 @@
          * This is a copy of the attendee status for the owner of this event.
          * This field is copied here so that we can efficiently filter out
          * events that are declined without having to look in the Attendees
-         * table.
+         * table. Column name.
          *
          * <P>Type: INTEGER (int)</P>
          */
         public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus";
 
         /**
-         * This column is available for use by sync adapters
+         * This column is available for use by sync adapters. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String SYNC_DATA1 = "sync_data1";
 
         /**
-         * The comments feed uri.
+         * This column is available for use by sync adapters
+         * <P>Type: TEXT</P>
+         */
+        public static final String SYNC_DATA7 = "sync_data7";
+
+        /**
+         * Used to indicate that a row is not a real event but an original copy of a locally
+         * modified event. A copy is made when an event changes from non-dirty to dirty and the
+         * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
+         * does not get expanded in the instances table and is only visible in queries made by a
+         * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
+         * sync adapter.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LAST_SYNCED = "lastSynced";
+
+        /**
+         * The comments feed uri. Column name.
+         * TODO change to sync_data6
          * <P>Type: TEXT</P>
          */
         public static final String COMMENTS_URI = "commentsUri";
 
         /**
-         * The time the event starts
+         * The time the event starts in UTC millis since epoch. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String DTSTART = "dtstart";
 
         /**
-         * The time the event ends
+         * The time the event ends in UTC millis since epoch. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String DTEND = "dtend";
 
         /**
-         * The duration of the event
+         * The duration of the event in RFC2445 format. Column name.
          * <P>Type: TEXT (duration in RFC2445 format)</P>
          */
         public static final String DURATION = "duration";
 
         /**
-         * The timezone for the event.
-         * <P>Type: TEXT
+         * The timezone for the event. Column name.
+         * <P>Type: TEXT</P>
          */
         public static final String EVENT_TIMEZONE = "eventTimezone";
 
         /**
-         * The timezone for the event, allDay events will have a local tz instead of UTC
-         * <P>Type: TEXT
+         * The timezone for the end time of the event. Column name.
+         * <P>Type: TEXT</P>
          */
         public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
 
         /**
-         * Whether the event lasts all day or not
+         * Is the event all day (time zone independent). Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String ALL_DAY = "allDay";
 
         /**
          * Defines how the event shows up for others when the calendar is
-         * shared.
-         * <P>Type: INTEGER</P>
+         * shared. Column name.
+         * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P>
          */
         public static final String ACCESS_LEVEL = "accessLevel";
 
@@ -655,20 +775,20 @@
          */
         public static final int ACCESS_CONFIDENTIAL = 1;
         /**
-         * Private assumes the event appears as a free/busy slot with no
-         * details.
+         * Private shares the event as a free/busy slot with no details.
          */
         public static final int ACCESS_PRIVATE = 2;
         /**
-         * Public assumes the contents are visible to anyone with access to the
+         * Public makes the contents visible to anyone with access to the
          * calendar.
          */
         public static final int ACCESS_PUBLIC = 3;
 
         /**
          * If this event counts as busy time or is still free time that can be
-         * scheduled over.
-         * <P>Type: INTEGER</P>
+         * scheduled over. Column name.
+         * <P>Type: INTEGER (One of {@link #AVAILABILITY_BUSY},
+         * {@link #AVAILABILITY_FREE})</P>
          */
         public static final String AVAILABILITY = "availability";
 
@@ -684,45 +804,44 @@
         public static final int AVAILABILITY_FREE = 1;
 
         /**
-         * Whether the event has an alarm or not
+         * Whether the event has an alarm or not. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_ALARM = "hasAlarm";
 
         /**
-         * Whether the event has extended properties or not
+         * Whether the event has extended properties or not. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties";
 
         /**
-         * The recurrence rule for the event.
-         * than one.
+         * The recurrence rule for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String RRULE = "rrule";
 
         /**
-         * The recurrence dates for the event.
+         * The recurrence dates for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String RDATE = "rdate";
 
         /**
-         * The recurrence exception rule for the event.
+         * The recurrence exception rule for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EXRULE = "exrule";
 
         /**
-         * The recurrence exception dates for the event.
+         * The recurrence exception dates for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EXDATE = "exdate";
 
         /**
-         * The _id of the original recurring event for which this event is an
-         * exception.
+         * The {@link Events#_ID} of the original recurring event for which this
+         * event is an exception. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_ID = "original_id";
@@ -730,27 +849,28 @@
         /**
          * The _sync_id of the original recurring event for which this event is
          * an exception. The provider should keep the original_id in sync when
-         * this is updated.
+         * this is updated. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_SYNC_ID = "original_sync_id";
 
         /**
          * The original instance time of the recurring event for which this
-         * event is an exception.
+         * event is an exception. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime";
 
         /**
          * The allDay status (true or false) of the original recurring event
-         * for which this event is an exception.
+         * for which this event is an exception. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String ORIGINAL_ALL_DAY = "originalAllDay";
 
         /**
-         * The last date this event repeats on, or NULL if it never ends
+         * The last date this event repeats on, or NULL if it never ends. Column
+         * name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String LAST_DATE = "lastDate";
@@ -758,61 +878,66 @@
         /**
          * Whether the event has attendee information.  True if the event
          * has full attendee data, false if the event has information about
-         * self only.
+         * self only. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
 
         /**
-         * Whether guests can modify the event.
+         * Whether guests can modify the event. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
 
         /**
-         * Whether guests can invite other guests.
+         * Whether guests can invite other guests. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
 
         /**
-         * Whether guests can see the list of attendees.
+         * Whether guests can see the list of attendees. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
 
         /**
-         * Email of the organizer (owner) of the event.
+         * Email of the organizer (owner) of the event. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ORGANIZER = "organizer";
 
         /**
-         * Whether the user can invite others to the event.
-         * The GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary guest,
-         * while CAN_INVITE_OTHERS indicates if the user can invite others (either through
-         * GUESTS_CAN_INVITE_OTHERS or because the user has modify access to the event).
+         * Whether the user can invite others to the event. The
+         * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary
+         * guest, while CAN_INVITE_OTHERS indicates if the user can invite
+         * others (either through GUESTS_CAN_INVITE_OTHERS or because the user
+         * has modify access to the event). Column name.
          * <P>Type: INTEGER (boolean, readonly)</P>
          */
         public static final String CAN_INVITE_OTHERS = "canInviteOthers";
 
         /**
          * The owner account for this calendar, based on the calendar (foreign
-         * key into the calendars table).
+         * key into the calendars table). Column name.
          * <P>Type: String</P>
          */
         public static final String OWNER_ACCOUNT = "ownerAccount";
 
         /**
-         * Whether the row has been deleted.  A deleted row should be ignored.
+         * Whether the row has been deleted. A deleted row should be ignored.
+         * Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String DELETED = "deleted";
     }
 
     /**
-     * Contains one entry per calendar event. Recurring events show up as a
-     * single entry.
+     * Class that represents an Event Entity. There is one entry per event.
+     * Recurring events show up as a single entry. This is a helper class to
+     * make batch operations easier. A {@link ContentResolver} or
+     * {@link ContentProviderClient} is required as the helper does additional
+     * queries to add reminders and attendees to each entry.
      */
     public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
         /**
@@ -821,10 +946,26 @@
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/event_entities");
 
+        /**
+         * Creates a new iterator for events
+         *
+         * @param cursor An event query
+         * @param resolver For performing additional queries
+         * @return an EntityIterator containing one entity per event in the
+         *         cursor
+         */
         public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
             return new EntityIteratorImpl(cursor, resolver);
         }
 
+        /**
+         * Creates a new iterator for events
+         *
+         * @param cursor An event query
+         * @param provider For performing additional queries
+         * @return an EntityIterator containing one entity per event in the
+         *         cursor
+         */
         public static EntityIterator newEntityIterator(Cursor cursor,
                 ContentProviderClient provider) {
             return new EntityIteratorImpl(cursor, provider);
@@ -919,9 +1060,11 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
@@ -1018,53 +1161,54 @@
     }
 
     /**
-     * Contains one entry per calendar event. Recurring events show up as a single entry.
+     * Fields and helpers for interacting with Events.
      */
     public static final class Events implements BaseColumns, SyncColumns, EventsColumns {
 
-        private static final String[] FETCH_ENTRY_COLUMNS =
-                new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID };
-
-        private static final String[] ATTENDEES_COLUMNS =
-                new String[] { AttendeesColumns.ATTENDEE_NAME,
-                               AttendeesColumns.ATTENDEE_EMAIL,
-                               AttendeesColumns.ATTENDEE_RELATIONSHIP,
-                               AttendeesColumns.ATTENDEE_TYPE,
-                               AttendeesColumns.ATTENDEE_STATUS };
-
+        /**
+         * Queries all events with the given projection. This is a blocking call
+         * and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @return A Cursor containing all events in the db
+         */
         public static final Cursor query(ContentResolver cr, String[] projection) {
             return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
         }
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                       String where, String orderBy) {
-            return cr.query(CONTENT_URI, projection, where,
-                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        private static String extractValue(ICalendar.Component component,
-                                           String propertyName) {
-            ICalendar.Property property =
-                    component.getFirstProperty(propertyName);
-            if (property != null) {
-                return property.getValue();
-            }
-            return null;
+        /**
+         * Queries events using the given projection, selection filter, and
+         * ordering. This is a blocking call and should not be done on the UI
+         * thread. For selection and selectionArgs usage see
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor containing the matching events
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+                String[] selectionArgs, String orderBy) {
+            return cr.query(CONTENT_URI, projection, selection, null,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * The content:// style URL for this table
+         * The content:// style URL for interacting with events. Appending an
+         * event id using {@link ContentUris#withAppendedId(Uri, long)} will
+         * specify a single event.
          */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI =
                 Uri.parse("content://" + AUTHORITY + "/events");
 
-        public static final Uri DELETED_CONTENT_URI =
-                Uri.parse("content://" + AUTHORITY + "/deleted_events");
-
         /**
          * The default sort order for this table
          */
-        public static final String DEFAULT_SORT_ORDER = "";
+        private static final String DEFAULT_SORT_ORDER = "";
 
         /**
          * These are columns that should only ever be updated by the provider,
@@ -1073,7 +1217,14 @@
          */
         public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
                 ACCOUNT_NAME,
-                ACCOUNT_TYPE
+                ACCOUNT_TYPE,
+                CAL_SYNC1,
+                CAL_SYNC2,
+                CAL_SYNC3,
+                CAL_SYNC4,
+                CAL_SYNC5,
+                CAL_SYNC6,
+                CAN_PARTIALLY_UPDATE,
         };
 
         /**
@@ -1091,13 +1242,29 @@
     }
 
     /**
-     * Contains one entry per calendar event instance. Recurring events show up every time
-     * they occur.
+     * Fields and helpers for interacting with Instances. An instance is a
+     * single occurrence of an event including time zone specific start and end
+     * days and minutes.
      */
     public static final class Instances implements BaseColumns, EventsColumns, CalendarsColumns {
 
         private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=1";
 
+        /**
+         * Performs a query to return all visible instances in the given range.
+         * This is a blocking function and should not be done on the UI thread.
+         * This will cause an expansion of recurring events to fill this time
+         * range if they are not already expanded and will slow down for larger
+         * time ranges with many recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @return A Cursor containing all instances in the given range
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                                          long begin, long end) {
             Uri.Builder builder = CONTENT_URI.buildUpon();
@@ -1107,111 +1274,184 @@
                          null, DEFAULT_SORT_ORDER);
         }
 
+        /**
+         * Performs a query to return all visible instances in the given range
+         * that match the given query. This is a blocking function and should
+         * not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param searchQuery A string of space separated search terms. Segments
+         *            enclosed by double quotes will be treated as a single
+         *            term.
+         * @return A Cursor of instances matching the search terms in the given
+         *         time range
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                                          long begin, long end, String searchQuery) {
             Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
             ContentUris.appendId(builder, begin);
             ContentUris.appendId(builder, end);
-            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
-                         new String[] { searchQuery }, DEFAULT_SORT_ORDER);
+            builder = builder.appendPath(searchQuery);
+            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED, null,
+                    DEFAULT_SORT_ORDER);
         }
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                         long begin, long end, String where, String orderBy) {
+        /**
+         * Performs a query to return all visible instances in the given range
+         * that match the given selection. This is a blocking function and
+         * should not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor of instances matching the selection
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+                long end, String selection, String[] selectionArgs, String orderBy) {
             Uri.Builder builder = CONTENT_URI.buildUpon();
             ContentUris.appendId(builder, begin);
             ContentUris.appendId(builder, end);
-            if (TextUtils.isEmpty(where)) {
-                where = WHERE_CALENDARS_SELECTED;
+            if (TextUtils.isEmpty(selection)) {
+                selection = WHERE_CALENDARS_SELECTED;
             } else {
-                where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
+                selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
             }
-            return cr.query(builder.build(), projection, where,
-                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
-                long end, String searchQuery, String where, String orderBy) {
-            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
-            ContentUris.appendId(builder, begin);
-            ContentUris.appendId(builder, end);
-            builder = builder.appendPath(searchQuery);
-            if (TextUtils.isEmpty(where)) {
-                where = WHERE_CALENDARS_SELECTED;
-            } else {
-                where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
-            }
-            return cr.query(builder.build(), projection, where, null,
+            return cr.query(builder.build(), projection, selection, selectionArgs,
                     orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * The content:// style URL for this table
+         * Performs a query to return all visible instances in the given range
+         * that match the given selection. This is a blocking function and
+         * should not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param searchQuery A string of space separated search terms. Segments
+         *            enclosed by double quotes will be treated as a single
+         *            term.
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor of instances matching the selection
          */
+        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+                long end, String searchQuery, String selection, String[] selectionArgs,
+                String orderBy) {
+            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+            ContentUris.appendId(builder, begin);
+            ContentUris.appendId(builder, end);
+            builder = builder.appendPath(searchQuery);
+            if (TextUtils.isEmpty(selection)) {
+                selection = WHERE_CALENDARS_SELECTED;
+            } else {
+                selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
+            }
+            return cr.query(builder.build(), projection, selection, selectionArgs,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        /**
+         * The content:// style URL for querying an instance range. The begin
+         * and end of the range to query should be added as path segments if
+         * this is used directly.
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/instances/when");
+        /**
+         * The content:// style URL for querying an instance range by Julian
+         * Day. The start and end day should be added as path segments if this
+         * is used directly.
+         */
         public static final Uri CONTENT_BY_DAY_URI =
             Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term. The begin, end, and search string should be appended as path
+         * segments if this is used directly.
+         */
         public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
                 "/instances/search");
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term. The start day, end day, and search string should be appended as
+         * path segments if this is used directly.
+         */
         public static final Uri CONTENT_SEARCH_BY_DAY_URI =
             Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
 
         /**
          * The default sort order for this table.
          */
-        public static final String DEFAULT_SORT_ORDER = "begin ASC";
+        private static final String DEFAULT_SORT_ORDER = "begin ASC";
 
         /**
-         * The sort order is: events with an earlier start time occur
-         * first and if the start times are the same, then events with
-         * a later end time occur first. The later end time is ordered
-         * first so that long-running events in the calendar views appear
-         * first.  If the start and end times of two events are
-         * the same then we sort alphabetically on the title.  This isn't
-         * required for correctness, it just adds a nice touch.
-         */
-        public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC";
-        /**
-         * The beginning time of the instance, in UTC milliseconds
+         * The beginning time of the instance, in UTC milliseconds. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String BEGIN = "begin";
 
         /**
-         * The ending time of the instance, in UTC milliseconds
+         * The ending time of the instance, in UTC milliseconds. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String END = "end";
 
         /**
-         * The event for this instance
+         * The _id of the event for this instance. Column name.
          * <P>Type: INTEGER (long, foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The Julian start day of the instance, relative to the local timezone
+         * The Julian start day of the instance, relative to the local time
+         * zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String START_DAY = "startDay";
 
         /**
-         * The Julian end day of the instance, relative to the local timezone
+         * The Julian end day of the instance, relative to the local time
+         * zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String END_DAY = "endDay";
 
         /**
          * The start minute of the instance measured from midnight in the
-         * local timezone.
+         * local time zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String START_MINUTE = "startMinute";
 
         /**
          * The end minute of the instance measured from midnight in the
-         * local timezone.
+         * local time zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String END_MINUTE = "endMinute";
@@ -1219,14 +1459,12 @@
 
     /**
      * CalendarCache stores some settings for calendar including the current
-     * time zone for the app. These settings are stored using a key/value
+     * time zone for the instaces. These settings are stored using a key/value
      * scheme.
      */
-    public interface CalendarCacheColumns {
+    private interface CalendarCacheColumns {
         /**
-         * The key for the setting. Keys are defined in CalendarChache in the
-         * Calendar provider.
-         * TODO Add keys to this file
+         * The key for the setting. Keys are defined in {@link CalendarCache}.
          */
         public static final String KEY = "key";
 
@@ -1296,7 +1534,7 @@
      * the Instances table and these are all stored in the first (and only)
      * row of the CalendarMetaData table.
      */
-    public interface CalendarMetaDataColumns {
+    private interface CalendarMetaDataColumns {
         /**
          * The local timezone that was used for precomputing the fields
          * in the Instances table.
@@ -1330,34 +1568,51 @@
         public static final String MAX_EVENTDAYS = "maxEventDays";
     }
 
+    /**
+     * @hide
+     */
     public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
     }
 
-    public interface EventDaysColumns {
+    private interface EventDaysColumns {
         /**
-         * The Julian starting day number.
+         * The Julian starting day number. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String STARTDAY = "startDay";
+        /**
+         * The Julian ending day number. Column name.
+         * <P>Type: INTEGER (int)</P>
+         */
         public static final String ENDDAY = "endDay";
 
     }
 
+    /**
+     * Fields and helpers for querying for a list of days that contain events.
+     */
     public static final class EventDays implements EventDaysColumns {
-        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
-                "/instances/groupbyday");
-
-        public static final String[] PROJECTION = { STARTDAY, ENDDAY };
-        public static final String SELECTION = "selected=1";
+        private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+                + "/instances/groupbyday");
 
         /**
-         * Retrieves the days with events for the Julian days starting at "startDay"
-         * for "numDays".
+         * The projection used by the EventDays query.
+         */
+        public static final String[] PROJECTION = { STARTDAY, ENDDAY };
+        private static final String SELECTION = "selected=1";
+
+        /**
+         * Retrieves the days with events for the Julian days starting at
+         * "startDay" for "numDays". It returns a cursor containing startday and
+         * endday representing the max range of days for all events beginning on
+         * each startday.This is a blocking function and should not be done on
+         * the UI thread.
          *
          * @param cr the ContentResolver
          * @param startDay the first Julian day in the range
          * @param numDays the number of days to load (must be at least 1)
-         * @return a database cursor
+         * @return a database cursor containing a list of start and end days for
+         *         events
          */
         public static final Cursor query(ContentResolver cr, int startDay, int numDays) {
             if (numDays < 1) {
@@ -1372,9 +1627,9 @@
         }
     }
 
-    public interface RemindersColumns {
+    private interface RemindersColumns {
         /**
-         * The event the reminder belongs to
+         * The event the reminder belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
@@ -1382,17 +1637,24 @@
         /**
          * The minutes prior to the event that the alarm should ring.  -1
          * specifies that we should use the default value for the system.
+         * Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String MINUTES = "minutes";
 
+        /**
+         * Passing this as a minutes value will use the default reminder
+         * minutes.
+         */
         public static final int MINUTES_DEFAULT = -1;
 
         /**
-         * The alarm method, as set on the server.  DEFAULT, ALERT, EMAIL, and
-         * SMS are possible values; the device will only process DEFAULT and
-         * ALERT reminders (the other types are simply stored so we can send the
-         * same reminder info back to the server when we make changes).
+         * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
+         * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS}
+         * are possible values; the device will only process
+         * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the
+         * other types are simply stored so we can send the same reminder info
+         * back to the server when we make changes).
          */
         public static final String METHOD = "method";
 
@@ -1402,61 +1664,85 @@
         public static final int METHOD_SMS = 3;
     }
 
+    /**
+     * Fields and helpers for accessing reminders for an event.
+     */
     public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
-        public static final String TABLE_NAME = "Reminders";
+        private static final String REMINDERS_WHERE = Calendar.Reminders.EVENT_ID + "=?";
+        /**
+         * The projection used by the reminders query.
+         */
+        public static final String[] PROJECTION = new String[] {
+                _ID, MINUTES, METHOD,};
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
+
+        /**
+         * Queries all reminders associated with the given event. This is a
+         * blocking call and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param eventId The id of the event to retrieve reminders for
+         * @return A Cursor containing all reminders for the event
+         */
+        public static final Cursor query(ContentResolver cr, long eventId) {
+            String[] remArgs = {Long.toString(eventId)};
+            return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */,
+                    null /* sort order */);
+        }
     }
 
-    public interface CalendarAlertsColumns {
+    private interface CalendarAlertsColumns {
         /**
-         * The event that the alert belongs to
+         * The event that the alert belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The start time of the event, in UTC
+         * The start time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String BEGIN = "begin";
 
         /**
-         * The end time of the event, in UTC
+         * The end time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String END = "end";
 
         /**
-         * The alarm time of the event, in UTC
+         * The alarm time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String ALARM_TIME = "alarmTime";
 
         /**
          * The creation time of this database entry, in UTC.
-         * (Useful for debugging missed reminders.)
+         * Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String CREATION_TIME = "creationTime";
 
         /**
          * The time that the alarm broadcast was received by the Calendar app,
-         * in UTC. (Useful for debugging missed reminders.)
+         * in UTC. Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String RECEIVED_TIME = "receivedTime";
 
         /**
          * The time that the notification was created by the Calendar app,
-         * in UTC. (Useful for debugging missed reminders.)
+         * in UTC. Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String NOTIFY_TIME = "notifyTime";
 
         /**
-         * The state of this alert.  It starts out as SCHEDULED, then when
-         * the alarm goes off, it changes to FIRED, and then when the user
-         * dismisses the alarm it changes to DISMISSED.
+         * The state of this alert. It starts out as {@link #SCHEDULED}, then
+         * when the alarm goes off, it changes to {@link #FIRED}, and then when
+         * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
+         * name.
          * <P>Type: INTEGER</P>
          */
         public static final String STATE = "state";
@@ -1466,21 +1752,33 @@
         public static final int DISMISSED = 2;
 
         /**
-         * The number of minutes that this alarm precedes the start time
-         * <P>Type: INTEGER </P>
+         * The number of minutes that this alarm precedes the start time. Column
+         * name.
+         * <P>Type: INTEGER</P>
          */
         public static final String MINUTES = "minutes";
 
         /**
-         * The default sort order for this table
+         * The default sort order for this alerts queries
          */
         public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
     }
 
+    /**
+     * Fields and helpers for accessing calendar alerts information. These
+     * fields are for tracking which alerts have been fired.
+     */
     public static final class CalendarAlerts implements BaseColumns,
             CalendarAlertsColumns, EventsColumns, CalendarsColumns {
 
+        /**
+         * @hide
+         */
         public static final String TABLE_NAME = "CalendarAlerts";
+        /**
+         * The Uri for querying calendar alert information
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/calendar_alerts");
 
@@ -1507,6 +1805,11 @@
 
         private static final boolean DEBUG = true;
 
+        /**
+         * Helper for inserting an alarm time associated with an event
+         *
+         * @hide
+         */
         public static final Uri insert(ContentResolver cr, long eventId,
                 long begin, long end, long alarmTime, int minutes) {
             ContentValues values = new ContentValues();
@@ -1523,6 +1826,19 @@
             return cr.insert(CONTENT_URI, values);
         }
 
+        /**
+         * Queries alerts info using the given projection, selection filter, and
+         * ordering. This is a blocking call and should not be done on the UI
+         * thread. For selection and selectionArgs usage see
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param sortOrder How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor containing the matching alerts
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder) {
             return cr.query(CONTENT_URI, projection, selection, selectionArgs,
@@ -1531,12 +1847,13 @@
 
         /**
          * Finds the next alarm after (or equal to) the given time and returns
-         * the time of that alarm or -1 if no such alarm exists.
+         * the time of that alarm or -1 if no such alarm exists. This is a
+         * blocking call and should not be done on the UI thread.
          *
          * @param cr the ContentResolver
          * @param millis the time in UTC milliseconds
          * @return the next alarm time greater than or equal to "millis", or -1
-         *     if no such alarm exists.
+         *         if no such alarm exists.
          */
         public static final long findNextAlarmTime(ContentResolver cr, long millis) {
             String selection = ALARM_TIME + ">=" + millis;
@@ -1619,6 +1936,17 @@
             }
         }
 
+        /**
+         * Schedules an alarm intent with the system AlarmManager that will
+         * cause the Calendar provider to recheck alarms. This is used to wake
+         * the Calendar alarm handler when an alarm is expected or to do a
+         * periodic refresh of alarm data.
+         *
+         * @param context A context for referencing system resources
+         * @param manager The AlarmManager to use or null
+         * @param alarmTime The time to fire the intent in UTC millis since
+         *            epoch
+         */
         public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
             if (DEBUG) {
                 Time time = new Time();
@@ -1678,27 +2006,32 @@
         }
     }
 
-    public interface ExtendedPropertiesColumns {
+    private interface ExtendedPropertiesColumns {
         /**
-         * The event the extended property belongs to
+         * The event the extended property belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
          * The name of the extended property.  This is a uri of the form
-         * {scheme}#{local-name} convention.
+         * {scheme}#{local-name} convention. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String NAME = "name";
 
         /**
-         * The value of the extended property.
+         * The value of the extended property. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String VALUE = "value";
     }
 
+    /**
+     * Fields for accessing the Extended Properties. This is a generic set of
+     * name/value pairs for use by sync adapters or apps to add extra
+     * information to events.
+     */
    public static final class ExtendedProperties implements BaseColumns,
             ExtendedPropertiesColumns, EventsColumns {
         public static final Uri CONTENT_URI =
@@ -1719,7 +2052,7 @@
          */
         private SyncState() {}
 
-        public static final String CONTENT_DIRECTORY =
+        private static final String CONTENT_DIRECTORY =
                 SyncStateContract.Constants.CONTENT_DIRECTORY;
 
         /**
@@ -1732,39 +2065,43 @@
     /**
      * Columns from the EventsRawTimes table
      */
-    public interface EventsRawTimesColumns {
+    private interface EventsRawTimesColumns {
         /**
-         * The corresponding event id
+         * The corresponding event id. Column name.
          * <P>Type: INTEGER (long)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The RFC2445 compliant time the event starts
+         * The RFC2445 compliant time the event starts. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DTSTART_2445 = "dtstart2445";
 
         /**
-         * The RFC2445 compliant time the event ends
+         * The RFC2445 compliant time the event ends. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DTEND_2445 = "dtend2445";
 
         /**
-         * The RFC2445 compliant original instance time of the recurring event for which this
-         * event is an exception.
+         * The RFC2445 compliant original instance time of the recurring event
+         * for which this event is an exception. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
 
         /**
-         * The RFC2445 compliant last date this event repeats on, or NULL if it never ends
+         * The RFC2445 compliant last date this event repeats on, or NULL if it
+         * never ends. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String LAST_DATE_2445 = "lastDate2445";
     }
 
+    /**
+     * @hide
+     */
     public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 893947d..6ab7738 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2924,6 +2924,30 @@
         public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
 
         /**
+         * Setting to turn off walled garden test on Wi-Fi. Feature is enabled by default and
+         * the setting needs to be set to 0 to disable it.
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED =
+                "wifi_watchdog_walled_garden_test_enabled";
+
+        /**
+         * The URL used for walled garden check upon a new conection. WifiWatchdogService
+         * fetches the URL and checks to see if {@link #WIFI_WATCHDOG_WALLED_GARDEN_PATTERN}
+         * is not part of the title string to notify the user on the presence of a walled garden.
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_URL =
+                "wifi_watchdog_walled_garden_url";
+
+        /**
+         * The pattern string in the fetched URL used to detect a walled garden
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_PATTERN =
+                "wifi_watchdog_walled_garden_pattern";
+
+        /**
          * The maximum number of times we will retry a connection to an access
          * point for which we have failed in acquiring an IP address from DHCP.
          * A value of N means that we will make N+1 connection attempts in all.
@@ -3771,6 +3795,19 @@
         public static final String DREAM_TIMEOUT =
                 "dream_timeout";
 
+        /** {@hide} */
+        public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+        /** {@hide} */
+        public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold";
+        /** {@hide} */
+        public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration";
+        /** {@hide} */
+        public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history";
+        /** {@hide} */
+        public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration";
+        /** {@hide} */
+        public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history";
+
         /**
          * @hide
          */
diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java
new file mode 100644
index 0000000..db4d622
--- /dev/null
+++ b/core/java/android/speech/tts/AudioMessageParams.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class AudioMessageParams extends MessageParams {
+    private final BlockingMediaPlayer mPlayer;
+
+    AudioMessageParams(UtteranceCompletedDispatcher dispatcher, BlockingMediaPlayer player) {
+        super(dispatcher);
+        mPlayer = player;
+    }
+
+    BlockingMediaPlayer getPlayer() {
+        return mPlayer;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_AUDIO;
+    }
+
+}
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
new file mode 100644
index 0000000..924bbbc
--- /dev/null
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -0,0 +1,358 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.speech.tts.SynthesisMessageParams.ListEntry;
+import android.util.Log;
+
+class AudioPlaybackHandler extends Handler {
+    private static final String TAG = "TTS.AudioPlaybackHandler";
+    private static final boolean DBG = false;
+
+    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+    private static final int SYNTHESIS_START = 1;
+    private static final int SYNTHESIS_DATA_AVAILABLE = 2;
+    private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3;
+    private static final int SYNTHESIS_DONE = 4;
+
+    private static final int PLAY_AUDIO = 5;
+    private static final int PLAY_SILENCE = 6;
+
+    // Accessed by multiple threads, synchronized by "this".
+    private MessageParams mCurrentParams;
+    // Used only for book keeping and error detection.
+    private SynthesisMessageParams mLastSynthesisRequest;
+
+    AudioPlaybackHandler(Looper looper) {
+        super(looper);
+    }
+
+    @Override
+    public synchronized void handleMessage(Message msg) {
+        if (msg.what == SYNTHESIS_START) {
+            mCurrentParams = (SynthesisMessageParams) msg.obj;
+            handleSynthesisStart(msg);
+        } else if (msg.what == SYNTHESIS_DATA_AVAILABLE) {
+            handleSynthesisDataAvailable(msg);
+        } else if (msg.what == SYNTHESIS_DONE) {
+            handleSynthesisDone(msg);
+        } else if (msg.what == SYNTHESIS_COMPLETE_DATA_AVAILABLE) {
+            handleSynthesisCompleteDataAvailable(msg);
+        } else if (msg.what == PLAY_AUDIO) {
+            handleAudio(msg);
+        } else if (msg.what == PLAY_SILENCE) {
+            handleSilence(msg);
+        }
+
+        mCurrentParams = null;
+    }
+
+    /**
+     * Stops all synthesis for a given {@code token}. If the current token
+     * is currently being processed, an effort will be made to stop it but
+     * that is not guaranteed.
+     */
+    synchronized public void stop(MessageParams token) {
+        removeCallbacksAndMessages(token);
+
+        if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
+            sendMessageAtFrontOfQueue(obtainMessage(SYNTHESIS_DONE, token));
+        } else if (token == mCurrentParams) {
+            if (token.getType() == MessageParams.TYPE_AUDIO) {
+                ((AudioMessageParams) mCurrentParams).getPlayer().stop();
+            } else if (token.getType() == MessageParams.TYPE_SILENCE) {
+                ((SilenceMessageParams) mCurrentParams).getConditionVariable().open();
+            }
+        }
+    }
+
+    /**
+     * Shut down the audio playback thread.
+     */
+    synchronized public void quit() {
+        if (mCurrentParams != null) {
+            stop(mCurrentParams);
+        }
+        getLooper().quit();
+    }
+
+    void enqueueSynthesisStart(SynthesisMessageParams token) {
+        sendMessage(obtainMessage(SYNTHESIS_START, token));
+    }
+
+    void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
+        sendMessage(obtainMessage(SYNTHESIS_DATA_AVAILABLE, token));
+    }
+
+    void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) {
+        sendMessage(obtainMessage(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token));
+    }
+
+    void enqueueSynthesisDone(SynthesisMessageParams token) {
+        sendMessage(obtainMessage(SYNTHESIS_DONE, token));
+    }
+
+    void enqueueAudio(AudioMessageParams token) {
+        sendMessage(obtainMessage(PLAY_AUDIO, token));
+    }
+
+    void enqueueSilence(SilenceMessageParams token) {
+        sendMessage(obtainMessage(PLAY_SILENCE, token));
+    }
+
+    // -----------------------------------------
+    // End of public API methods.
+    // -----------------------------------------
+
+    // Currently implemented as blocking the audio playback thread for the
+    // specified duration. If a call to stop() is made, the thread
+    // unblocks.
+    private void handleSilence(Message msg) {
+        if (DBG) Log.d(TAG, "handleSilence()");
+        SilenceMessageParams params = (SilenceMessageParams) msg.obj;
+        if (params.getSilenceDurationMs() > 0) {
+            params.getConditionVariable().block(params.getSilenceDurationMs());
+        }
+        params.getDispatcher().dispatchUtteranceCompleted();
+        if (DBG) Log.d(TAG, "handleSilence() done.");
+    }
+
+    // Plays back audio from a given URI. No TTS engine involvement here.
+    private void handleAudio(Message msg) {
+        if (DBG) Log.d(TAG, "handleAudio()");
+        AudioMessageParams params = (AudioMessageParams) msg.obj;
+        // Note that the BlockingMediaPlayer spawns a separate thread.
+        //
+        // TODO: This can be avoided.
+        params.getPlayer().startAndWait();
+        params.getDispatcher().dispatchUtteranceCompleted();
+        if (DBG) Log.d(TAG, "handleAudio() done.");
+    }
+
+    // Denotes the start of a new synthesis request. We create a new
+    // audio track, and prepare it for incoming data.
+    //
+    // Note that since all TTS synthesis happens on a single thread, we
+    // should ALWAYS see the following order :
+    //
+    // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone
+    // OR
+    // handleSynthesisCompleteDataAvailable.
+    private void handleSynthesisStart(Message msg) {
+        if (DBG) Log.d(TAG, "handleSynthesisStart()");
+        final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj;
+
+        // Oops, looks like the engine forgot to call done(). We go through
+        // extra trouble to clean the data to prevent the AudioTrack resources
+        // from being leaked.
+        if (mLastSynthesisRequest != null) {
+            Log.w(TAG, "Error : Missing call to done() for request : " +
+                    mLastSynthesisRequest);
+            handleSynthesisDone(mLastSynthesisRequest);
+        }
+
+        mLastSynthesisRequest = param;
+
+        // Create the audio track.
+        final AudioTrack audioTrack = createStreamingAudioTrack(
+                param.mStreamType, param.mSampleRateInHz, param.mAudioFormat,
+                param.mChannelCount, param.mVolume, param.mPan);
+
+        param.setAudioTrack(audioTrack);
+    }
+
+    // More data available to be flushed to the audio track.
+    private void handleSynthesisDataAvailable(Message msg) {
+        final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj;
+        if (param.getAudioTrack() == null) {
+            Log.w(TAG, "Error : null audio track in handleDataAvailable.");
+            return;
+        }
+
+        if (param != mLastSynthesisRequest) {
+            Log.e(TAG, "Call to dataAvailable without done() / start()");
+            return;
+        }
+
+        final AudioTrack audioTrack = param.getAudioTrack();
+        final ListEntry bufferCopy = param.getNextBuffer();
+
+        if (bufferCopy == null) {
+            Log.e(TAG, "No buffers available to play.");
+            return;
+        }
+
+        int playState = audioTrack.getPlayState();
+        if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+            if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode());
+            audioTrack.play();
+        }
+        int count = 0;
+        while (count < bufferCopy.mLength) {
+            // Note that we don't take bufferCopy.mOffset into account because
+            // it is guaranteed to be 0.
+            int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength);
+            if (written <= 0) {
+                break;
+            }
+            count += written;
+        }
+    }
+
+    private void handleSynthesisDone(Message msg) {
+        final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj;
+        handleSynthesisDone(params);
+    }
+
+    // Flush all remaining data to the audio track, stop it and release
+    // all it's resources.
+    private void handleSynthesisDone(SynthesisMessageParams params) {
+        if (DBG) Log.d(TAG, "handleSynthesisDone()");
+        final AudioTrack audioTrack = params.getAudioTrack();
+
+        try {
+            if (audioTrack != null) {
+                audioTrack.flush();
+                audioTrack.stop();
+                audioTrack.release();
+            }
+        } finally {
+            params.setAudioTrack(null);
+            params.getDispatcher().dispatchUtteranceCompleted();
+            mLastSynthesisRequest = null;
+        }
+    }
+
+    private void handleSynthesisCompleteDataAvailable(Message msg) {
+        final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj;
+        if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
+
+        // Channel config and bytes per frame are checked before
+        // this message is sent.
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
+        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat);
+
+        ListEntry entry = params.getNextBuffer();
+
+        if (entry == null) {
+            Log.w(TAG, "completeDataAvailable : No buffers available to play.");
+            return;
+        }
+
+        final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz,
+                channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC);
+
+        // So that handleDone can access this correctly.
+        params.mAudioTrack = audioTrack;
+
+        try {
+            audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength);
+            setupVolume(audioTrack, params.mVolume, params.mPan);
+            audioTrack.play();
+            blockUntilDone(audioTrack, bytesPerFrame, entry.mLength);
+            if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength);
+        } catch (IllegalStateException ex) {
+            Log.e(TAG, "Playback error", ex);
+        } finally {
+            handleSynthesisDone(msg);
+        }
+    }
+
+
+    private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
+        int lengthInFrames = length / bytesPerFrame;
+        int currentPosition = 0;
+        while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+            long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
+                    audioTrack.getSampleRate();
+            audioTrack.getPlayState();
+            if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
+                    " Playback position : " + currentPosition);
+            try {
+                Thread.sleep(estimatedTimeMs);
+            } catch (InterruptedException ie) {
+                break;
+            }
+        }
+    }
+
+    private static AudioTrack createStreamingAudioTrack(int streamType, int sampleRateInHz,
+            int audioFormat, int channelCount, float volume, float pan) {
+        int channelConfig = getChannelConfig(channelCount);
+
+        int minBufferSizeInBytes
+                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+
+        AudioTrack audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig,
+                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+            Log.w(TAG, "Unable to create audio track.");
+            audioTrack.release();
+            return null;
+        }
+
+        setupVolume(audioTrack, volume, pan);
+        return audioTrack;
+    }
+
+    static int getChannelConfig(int channelCount) {
+        if (channelCount == 1) {
+            return AudioFormat.CHANNEL_OUT_MONO;
+        } else if (channelCount == 2){
+            return AudioFormat.CHANNEL_OUT_STEREO;
+        }
+
+        return 0;
+    }
+
+    static int getBytesPerFrame(int audioFormat) {
+        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+            return 1;
+        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+            return 2;
+        }
+
+        return -1;
+    }
+
+    private static void setupVolume(AudioTrack audioTrack, float volume, float pan) {
+        float vol = clip(volume, 0.0f, 1.0f);
+        float panning = clip(pan, -1.0f, 1.0f);
+        float volLeft = vol;
+        float volRight = vol;
+        if (panning > 0.0f) {
+            volLeft *= (1.0f - panning);
+        } else if (panning < 0.0f) {
+            volRight *= (1.0f + panning);
+        }
+        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+            Log.e(TAG, "Failed to set volume");
+        }
+    }
+
+    private static float clip(float value, float min, float max) {
+        return value > max ? max : (value < min ? min : value);
+    }
+
+}
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
new file mode 100644
index 0000000..2d96df4
--- /dev/null
+++ b/core/java/android/speech/tts/MessageParams.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+abstract class MessageParams {
+    private final UtteranceCompletedDispatcher mDispatcher;
+
+    static final int TYPE_SYNTHESIS = 1;
+    static final int TYPE_AUDIO = 2;
+    static final int TYPE_SILENCE = 3;
+
+    MessageParams(UtteranceCompletedDispatcher dispatcher) {
+        mDispatcher = dispatcher;
+    }
+
+    UtteranceCompletedDispatcher getDispatcher() {
+        return mDispatcher;
+    }
+
+    abstract int getType();
+}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index d698b54..34b263c 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -15,10 +15,8 @@
  */
 package android.speech.tts;
 
-import android.media.AudioFormat;
-import android.media.AudioTrack;
 import android.os.Bundle;
-import android.os.Handler;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
 import android.util.Log;
 
 /**
@@ -49,54 +47,48 @@
      */
     private final float mPan;
 
+    /**
+     * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}.
+     */
     private final Object mStateLock = new Object();
-    private final Handler mAudioTrackHandler;
-    private volatile AudioTrack mAudioTrack = null;
-    private boolean mStopped = false;
-    private boolean mDone = false;
-    private volatile boolean mWriteErrorOccured;
 
-    PlaybackSynthesisRequest(String text, Bundle params,
-            int streamType, float volume, float pan, Handler audioTrackHandler) {
+    // Handler associated with a thread that plays back audio requests.
+    private final AudioPlaybackHandler mAudioTrackHandler;
+    // A request "token", which will be non null after start() or
+    // completeAudioAvailable() have been called.
+    private SynthesisMessageParams mToken = null;
+    // Whether this request has been stopped. This is useful for keeping
+    // track whether stop() has been called before start(). In all other cases,
+    // a non-null value of mToken will provide the same information.
+    private boolean mStopped = false;
+
+    private volatile boolean mDone = false;
+
+    private final UtteranceCompletedDispatcher mDispatcher;
+
+    PlaybackSynthesisRequest(String text, Bundle params, int streamType, float volume, float pan,
+            AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher) {
         super(text, params);
         mStreamType = streamType;
         mVolume = volume;
         mPan = pan;
         mAudioTrackHandler = audioTrackHandler;
-        mWriteErrorOccured = false;
+        mDispatcher = dispatcher;
     }
 
     @Override
     void stop() {
         if (DBG) Log.d(TAG, "stop()");
+
         synchronized (mStateLock) {
-            mStopped = true;
-            cleanUp();
-        }
-    }
-
-    // Always guarded by mStateLock.
-    private void cleanUp() {
-        if (DBG) Log.d(TAG, "cleanUp()");
-        if (mAudioTrack == null) {
-            return;
-        }
-
-        final AudioTrack audioTrack = mAudioTrack;
-        mAudioTrack = null;
-
-        // Clean up on the audiotrack handler thread.
-        //
-        // NOTE: It isn't very clear whether AudioTrack is thread safe.
-        // If it is we can clean up on the current (synthesis) thread.
-        mAudioTrackHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                audioTrack.flush();
-                audioTrack.stop();
-                audioTrack.release();
+            if (mToken == null || mStopped) {
+                Log.w(TAG, "stop() called twice, before start(), or after done()");
+                return;
             }
-        });
+            mAudioTrackHandler.stop(mToken);
+            mToken = null;
+            mStopped = true;
+        }
     }
 
     @Override
@@ -111,7 +103,6 @@
         return mDone;
     }
 
-    // TODO: add a thread that writes to the AudioTrack?
     @Override
     public int start(int sampleRateInHz, int audioFormat, int channelCount) {
         if (DBG) {
@@ -119,45 +110,28 @@
                     + "," + channelCount + ")");
         }
 
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+        if (channelConfig == 0) {
+            Log.e(TAG, "Unsupported number of channels :" + channelCount);
+            return TextToSpeech.ERROR;
+        }
+
         synchronized (mStateLock) {
             if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
+                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
                 return TextToSpeech.ERROR;
             }
-            if (mAudioTrack != null) {
-                Log.e(TAG, "start() called twice");
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
+            SynthesisMessageParams params = new SynthesisMessageParams(
+                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+                    mDispatcher);
+            mAudioTrackHandler.enqueueSynthesisStart(params);
 
-            mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount);
-            if (mAudioTrack == null) {
-                return TextToSpeech.ERROR;
-            }
+            mToken = params;
         }
 
         return TextToSpeech.SUCCESS;
     }
 
-    private void setupVolume(AudioTrack audioTrack, float volume, float pan) {
-        float vol = clip(volume, 0.0f, 1.0f);
-        float panning = clip(pan, -1.0f, 1.0f);
-        float volLeft = vol;
-        float volRight = vol;
-        if (panning > 0.0f) {
-            volLeft *= (1.0f - panning);
-        } else if (panning < 0.0f) {
-            volRight *= (1.0f + panning);
-        }
-        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
-        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
-            Log.e(TAG, "Failed to set volume");
-        }
-    }
-
-    private float clip(float value, float min, float max) {
-        return value > max ? max : (value < min ? min : value);
-    }
 
     @Override
     public int audioAvailable(byte[] buffer, int offset, int length) {
@@ -169,195 +143,78 @@
             throw new IllegalArgumentException("buffer is too large or of zero length (" +
                     + length + " bytes)");
         }
+
         synchronized (mStateLock) {
-            if (mWriteErrorOccured) {
-                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+            if (mToken == null) {
                 return TextToSpeech.ERROR;
             }
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack == null) {
-                Log.e(TAG, "audioAvailable(): Not started");
-                return TextToSpeech.ERROR;
-            }
-            final AudioTrack audioTrack = mAudioTrack;
+
             // Sigh, another copy.
             final byte[] bufferCopy = new byte[length];
             System.arraycopy(buffer, offset, bufferCopy, 0, length);
-
-            mAudioTrackHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    int playState = audioTrack.getPlayState();
-                    if (playState == AudioTrack.PLAYSTATE_STOPPED) {
-                        if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
-                        audioTrack.play();
-                    }
-                    // TODO: loop until all data is written?
-                    if (DBG) Log.d(TAG, "AudioTrack.write()");
-                    int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
-                    // The semantics of this change very slightly. Earlier, we would
-                    // report an error immediately, Now we will return an error on
-                    // the next API call, usually done( ) or another audioAvailable( )
-                    // call.
-                    if (count < 0) {
-                        mWriteErrorOccured = true;
-                    }
-                }
-            });
-
-            return TextToSpeech.SUCCESS;
+            mToken.addBuffer(bufferCopy);
+            mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
         }
+
+        return TextToSpeech.SUCCESS;
     }
 
     @Override
     public int done() {
         if (DBG) Log.d(TAG, "done()");
+
         synchronized (mStateLock) {
-            if (mWriteErrorOccured) {
-                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+            if (mDone) {
+                Log.w(TAG, "Duplicate call to done()");
                 return TextToSpeech.ERROR;
             }
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack == null) {
-                Log.e(TAG, "done(): Not started");
-                return TextToSpeech.ERROR;
-            }
+
             mDone = true;
-            cleanUp();
+
+            if (mToken == null) {
+                return TextToSpeech.ERROR;
+            }
+
+            mAudioTrackHandler.enqueueSynthesisDone(mToken);
         }
         return TextToSpeech.SUCCESS;
     }
 
     @Override
     public void error() {
-        if (DBG) Log.d(TAG, "error()");
-        synchronized (mStateLock) {
-            cleanUp();
-        }
+        if (DBG) Log.d(TAG, "error() [will call stop]");
+        stop();
     }
 
     @Override
     public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
             byte[] buffer, int offset, int length) {
-        if (DBG) {
-            Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat
-                    + "," + channelCount + "byte[" + buffer.length + "],"
-                    + offset + "," + length + ")");
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+        if (channelConfig == 0) {
+            Log.e(TAG, "Unsupported number of channels :" + channelCount);
+            return TextToSpeech.ERROR;
+        }
+
+        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat);
+        if (bytesPerFrame < 0) {
+            Log.e(TAG, "Unsupported audio format :" + audioFormat);
+            return TextToSpeech.ERROR;
         }
 
         synchronized (mStateLock) {
             if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
             }
-            if (mAudioTrack != null) {
-                Log.e(TAG, "start() called before completeAudioAvailable()");
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
+            SynthesisMessageParams params = new SynthesisMessageParams(
+                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+                    mDispatcher);
+            params.addBuffer(buffer, offset, length);
 
-            int channelConfig = getChannelConfig(channelCount);
-            if (channelConfig < 0) {
-                Log.e(TAG, "Unsupported number of channels :" + channelCount);
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-            int bytesPerFrame = getBytesPerFrame(audioFormat);
-            if (bytesPerFrame < 0) {
-                Log.e(TAG, "Unsupported audio format :" + audioFormat);
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-
-            mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
-                    audioFormat, buffer.length, AudioTrack.MODE_STATIC);
-            if (mAudioTrack == null) {
-                return TextToSpeech.ERROR;
-            }
-
-            try {
-                mAudioTrack.write(buffer, offset, length);
-                setupVolume(mAudioTrack, mVolume, mPan);
-                mAudioTrack.play();
-                blockUntilDone(mAudioTrack, bytesPerFrame, length);
-                mDone = true;
-                if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length);
-            } catch (IllegalStateException ex) {
-                Log.e(TAG, "Playback error", ex);
-                return TextToSpeech.ERROR;
-            } finally {
-                cleanUp();
-            }
+            mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params);
+            mToken = params;
         }
 
         return TextToSpeech.SUCCESS;
     }
 
-    private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
-        int lengthInFrames = length / bytesPerFrame;
-        int currentPosition = 0;
-        while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
-            long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
-                    audioTrack.getSampleRate();
-            if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
-                    " Playback position : " + currentPosition);
-            try {
-                Thread.sleep(estimatedTimeMs);
-            } catch (InterruptedException ie) {
-                break;
-            }
-        }
-    }
-
-    private int getBytesPerFrame(int audioFormat) {
-        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
-            return 1;
-        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
-            return 2;
-        }
-
-        return -1;
-    }
-
-    private int getChannelConfig(int channelCount) {
-        if (channelCount == 1) {
-            return AudioFormat.CHANNEL_OUT_MONO;
-        } else if (channelCount == 2){
-            return AudioFormat.CHANNEL_OUT_STEREO;
-        }
-
-        return -1;
-    }
-
-    private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat,
-            int channelCount) {
-        int channelConfig = getChannelConfig(channelCount);
-
-        if (channelConfig < 0) {
-            Log.e(TAG, "Unsupported number of channels : " + channelCount);
-            return null;
-        }
-
-        int minBufferSizeInBytes
-                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
-        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
-        AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
-                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
-        if (audioTrack == null) {
-            return null;
-        }
-
-        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
-            audioTrack.release();
-            return null;
-        }
-        setupVolume(audioTrack, mVolume, mPan);
-        return audioTrack;
-    }
 }
diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java
new file mode 100644
index 0000000..eee8b68
--- /dev/null
+++ b/core/java/android/speech/tts/SilenceMessageParams.java
@@ -0,0 +1,43 @@
+/*
+ * 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 android.speech.tts;
+
+import android.os.ConditionVariable;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class SilenceMessageParams extends MessageParams {
+    private final ConditionVariable mCondVar = new ConditionVariable();
+    private final long mSilenceDurationMs;
+
+    SilenceMessageParams(UtteranceCompletedDispatcher dispatcher, long silenceDurationMs) {
+        super(dispatcher);
+        mSilenceDurationMs = silenceDurationMs;
+    }
+
+    long getSilenceDurationMs() {
+        return mSilenceDurationMs;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_SILENCE;
+    }
+
+    ConditionVariable getConditionVariable() {
+        return mCondVar;
+    }
+
+}
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
new file mode 100644
index 0000000..aabaa5a
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioTrack;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+import java.util.LinkedList;
+
+/**
+ * Params required to play back a synthesis request.
+ */
+final class SynthesisMessageParams extends MessageParams {
+    final int mStreamType;
+    final int mSampleRateInHz;
+    final int mAudioFormat;
+    final int mChannelCount;
+    final float mVolume;
+    final float mPan;
+
+    public volatile AudioTrack mAudioTrack;
+
+    private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
+
+    SynthesisMessageParams(int streamType, int sampleRate,
+            int audioFormat, int channelCount,
+            float volume, float pan, UtteranceCompletedDispatcher dispatcher) {
+        super(dispatcher);
+
+        mStreamType = streamType;
+        mSampleRateInHz = sampleRate;
+        mAudioFormat = audioFormat;
+        mChannelCount = channelCount;
+        mVolume = volume;
+        mPan = pan;
+
+        // initially null.
+        mAudioTrack = null;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_SYNTHESIS;
+    }
+
+    synchronized void addBuffer(byte[] buffer, int offset, int length) {
+        mDataBufferList.add(new ListEntry(buffer, offset, length));
+    }
+
+    synchronized void addBuffer(byte[] buffer) {
+        mDataBufferList.add(new ListEntry(buffer, 0, buffer.length));
+    }
+
+    synchronized ListEntry getNextBuffer() {
+        return mDataBufferList.poll();
+    }
+
+
+    void setAudioTrack(AudioTrack audioTrack) {
+        mAudioTrack = audioTrack;
+    }
+
+    AudioTrack getAudioTrack() {
+        return mAudioTrack;
+    }
+
+    static final class ListEntry {
+        final byte[] mBytes;
+        final int mOffset;
+        final int mLength;
+
+        ListEntry(byte[] bytes, int offset, int length) {
+            mBytes = bytes;
+            mOffset = offset;
+            mLength = length;
+        }
+    }
+}
+
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ddd3252..e553f77 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,7 +51,10 @@
     private static final String SYNTH_THREAD_NAME = "SynthThread";
 
     private SynthHandler mSynthHandler;
-    private Handler mAudioTrackHandler;
+    // A thread and it's associated handler for playing back any audio
+    // associated with this TTS engine. Will handle all requests except synthesis
+    // to file requests, which occur on the synthesis thread.
+    private AudioPlaybackHandler mAudioPlaybackHandler;
 
     private CallbackMap mCallbacks;
 
@@ -68,7 +71,7 @@
 
         HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
         audioTrackThread.start();
-        mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+        mAudioPlaybackHandler = new AudioPlaybackHandler(audioTrackThread.getLooper());
 
         mCallbacks = new CallbackMap();
 
@@ -83,8 +86,8 @@
 
         // Tell the synthesizer to stop
         mSynthHandler.quit();
-        mAudioTrackHandler.getLooper().quit();
-
+        // Tell the audio playback thread to stop.
+        mAudioPlaybackHandler.quit();
         // Unregister all callbacks.
         mCallbacks.kill();
 
@@ -236,13 +239,6 @@
             super(looper);
         }
 
-        private void dispatchUtteranceCompleted(SpeechItem item) {
-            String utteranceId = item.getUtteranceId();
-            if (!TextUtils.isEmpty(utteranceId)) {
-                mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
-            }
-        }
-
         private synchronized SpeechItem getCurrentSpeechItem() {
             return mCurrentSpeechItem;
         }
@@ -286,9 +282,7 @@
                 @Override
                 public void run() {
                     setCurrentSpeechItem(speechItem);
-                    if (speechItem.play() == TextToSpeech.SUCCESS) {
-                        dispatchUtteranceCompleted(speechItem);
-                    }
+                    speechItem.play();
                     setCurrentSpeechItem(null);
                 }
             };
@@ -318,14 +312,19 @@
             if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
                 current.stop();
             }
+
             return TextToSpeech.SUCCESS;
         }
     }
 
+    interface UtteranceCompletedDispatcher {
+        public void dispatchUtteranceCompleted();
+    }
+
     /**
      * An item in the synth thread queue.
      */
-    private static abstract class SpeechItem {
+    private abstract class SpeechItem implements UtteranceCompletedDispatcher {
         private final String mCallingApp;
         protected final Bundle mParams;
         private boolean mStarted = false;
@@ -380,6 +379,13 @@
             stopImpl();
         }
 
+        public void dispatchUtteranceCompleted() {
+            final String utteranceId = getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId);
+            }
+        }
+
         protected abstract int playImpl();
 
         protected abstract void stopImpl();
@@ -413,7 +419,7 @@
         }
     }
 
-    private class SynthesisSpeechItem extends SpeechItem {
+    class SynthesisSpeechItem extends SpeechItem {
         private final String mText;
         private SynthesisRequest mSynthesisRequest;
 
@@ -453,7 +459,8 @@
 
         protected SynthesisRequest createSynthesisRequest() {
             return new PlaybackSynthesisRequest(mText, mParams,
-                    getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
+                    getStreamType(), getVolume(), getPan(), mAudioPlaybackHandler,
+                    this);
         }
 
         private void setRequestParams(SynthesisRequest request) {
@@ -526,6 +533,15 @@
             return new FileSynthesisRequest(getText(), mParams, mFile);
         }
 
+        @Override
+        protected int playImpl() {
+            int status = super.playImpl();
+            if (status == TextToSpeech.SUCCESS) {
+                dispatchUtteranceCompleted();
+            }
+            return status;
+        }
+
         /**
          * Checks that the given file can be used for synthesis output.
          */
@@ -557,6 +573,7 @@
     private class AudioSpeechItem extends SpeechItem {
 
         private final BlockingMediaPlayer mPlayer;
+        private AudioMessageParams mToken;
 
         public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
             super(callingApp, params);
@@ -570,23 +587,26 @@
 
         @Override
         protected int playImpl() {
-            return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+            mToken = new AudioMessageParams(this, mPlayer);
+            mAudioPlaybackHandler.enqueueAudio(mToken);
+            return TextToSpeech.SUCCESS;
         }
 
         @Override
         protected void stopImpl() {
-            mPlayer.stop();
+            if (mToken != null) {
+                mAudioPlaybackHandler.stop(mToken);
+            }
         }
     }
 
     private class SilenceSpeechItem extends SpeechItem {
         private final long mDuration;
-        private final ConditionVariable mDone;
+        private SilenceMessageParams mToken;
 
         public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
             super(callingApp, params);
             mDuration = duration;
-            mDone = new ConditionVariable();
         }
 
         @Override
@@ -596,13 +616,16 @@
 
         @Override
         protected int playImpl() {
-            boolean aborted = mDone.block(mDuration);
-            return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+            mToken = new SilenceMessageParams(this, mDuration);
+            mAudioPlaybackHandler.enqueueSilence(mToken);
+            return TextToSpeech.SUCCESS;
         }
 
         @Override
         protected void stopImpl() {
-            mDone.open();
+            if (mToken != null) {
+                mAudioPlaybackHandler.stop(mToken);
+            }
         }
     }
 
diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java
index 3ef8293..4ae21ad 100644
--- a/core/java/android/util/FinitePool.java
+++ b/core/java/android/util/FinitePool.java
@@ -69,6 +69,7 @@
 
         if (element != null) {
             element.setNextPoolable(null);
+            element.setPooled(false);
             mManager.onAcquired(element);            
         }
 
@@ -76,9 +77,13 @@
     }
 
     public void release(T element) {
+        if (element.isPooled()) {
+            throw new IllegalArgumentException("Element already in the pool.");
+        }
         if (mInfinite || mPoolCount < mLimit) {
             mPoolCount++;
             element.setNextPoolable(mRoot);
+            element.setPooled(true);
             mRoot = element;
         }
         mManager.onReleased(element);
diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java
index fd9bd9b..87e0529 100644
--- a/core/java/android/util/Poolable.java
+++ b/core/java/android/util/Poolable.java
@@ -22,4 +22,6 @@
 public interface Poolable<T> {
     void setNextPoolable(T element);
     T getNextPoolable();
+    boolean isPooled();
+    void setPooled(boolean isPooled);
 }
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index b2a35d3..69e6489 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -81,8 +81,10 @@
      *  horizontal axis. */
     public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
 
-    /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */
-    public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000;
+    /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
+     * absolute LEFT/RIGHT).
+     */
+    public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
 
     /**
      * Binary mask to get the absolute horizontal gravity of a gravity.
@@ -109,16 +111,16 @@
      */
     public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
     
-    /** Push object to x-axis position before its container, not changing its size. */
-    public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT;
+    /** Push object to x-axis position at the start of its container, not changing its size. */
+    public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
 
-    /** Push object to x-axis position after its container, not changing its size. */
-    public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT;
+    /** Push object to x-axis position at the end of its container, not changing its size. */
+    public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
 
     /**
      * Binary mask for the horizontal gravity and script specific direction bit.
      */
-    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER;
+    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
 
     /**
      * Apply a gravity constant to an object. This suppose that the layout direction is LTR.
@@ -342,8 +344,8 @@
     /**
      * <p>Convert script specific gravity to absolute horizontal value.</p>
      *
-     * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT.
-     * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT.
+     * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
+     * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
      *
      * @param gravity The gravity to convert to absolute (horizontal) values.
      * @param isRtl Whether the layout is right-to-left.
@@ -351,11 +353,11 @@
      */
     public static int getAbsoluteGravity(int gravity, boolean isRtl) {
         int result = gravity;
-        // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER)
-        if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) {
-            if ((result & Gravity.BEFORE) == Gravity.BEFORE) {
-                // Remove the BEFORE bit
-                result &= ~BEFORE;
+        // If layout is script specific and gravity is horizontal relative (START or END)
+        if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
+            if ((result & Gravity.START) == Gravity.START) {
+                // Remove the START bit
+                result &= ~START;
                 if (isRtl) {
                     // Set the RIGHT bit
                     result |= RIGHT;
@@ -363,9 +365,9 @@
                     // Set the LEFT bit
                     result |= LEFT;
                 }
-            } else if ((result & Gravity.AFTER) == Gravity.AFTER) {
-                // Remove the AFTER bit
-                result &= ~AFTER;
+            } else if ((result & Gravity.END) == Gravity.END) {
+                // Remove the END bit
+                result &= ~END;
                 if (isRtl) {
                     // Set the LEFT bit
                     result |= LEFT;
@@ -376,7 +378,7 @@
             }
             // Don't need the script specific bit any more, so remove it as we are converting to
             // absolute values (LEFT or RIGHT)
-            result &= ~RELATIVE_HORIZONTAL_DIRECTION;
+            result &= ~RELATIVE_LAYOUT_DIRECTION;
         }
         return result;
     }
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index fccef2b..5a91d31 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -50,6 +50,7 @@
 
     private int mPtr;
     private VelocityTracker mNext;
+    private boolean mIsPooled;
 
     private static native int nativeInitialize();
     private static native void nativeDispose(int ptr);
@@ -93,6 +94,20 @@
         return mNext;
     }
 
+    /**
+     * @hide
+     */
+    public boolean isPooled() {
+        return mIsPooled;
+    }
+
+    /**
+     * @hide
+     */
+    public void setPooled(boolean isPooled) {
+        mIsPooled = isPooled;
+    }
+
     private VelocityTracker() {
         mPtr = nativeInitialize();
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 014f19f..70218ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -950,51 +951,51 @@
 
     /**
      * Horizontal direction of this view is from Left to Right.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_LTR = 0x00000000;
+    public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
 
     /**
      * Horizontal direction of this view is from Right to Left.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_RTL = 0x40000000;
+    public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
 
     /**
      * Horizontal direction of this view is inherited from its parent.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_INHERIT = 0x80000000;
+    public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
 
     /**
      * Horizontal direction of this view is from deduced from the default language
-     * script for the locale. Use with {@link #setHorizontalDirection}.
+     * script for the locale. Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_LOCALE = 0xC0000000;
+    public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
 
     /**
      * Mask for use with setFlags indicating bits used for horizontalDirection.
      * {@hide}
      */
-    static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000;
+    static final int LAYOUT_DIRECTION_MASK = 0xC0000000;
 
     /*
      * Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct
      * flag value.
      * {@hide}
      */
-    private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR,
-            HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE};
+    private static final int[] LAYOUT_DIRECTION_FLAGS = {LAYOUT_DIRECTION_LTR,
+        LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE};
 
     /**
      * Default horizontalDirection.
      * {@hide}
      */
-    private static final int HORIZONTAL_DIRECTION_DEFAULT = HORIZONTAL_DIRECTION_INHERIT;
+    private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
 
     /**
      * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
@@ -1492,6 +1493,11 @@
     private static final Object sTagsLock = new Object();
 
     /**
+     * The next available accessiiblity id.
+     */
+    private static int sNextAccessibilityViewId;
+
+    /**
      * The animation currently associated with this view.
      * @hide
      */
@@ -1533,6 +1539,11 @@
     int mID = NO_ID;
 
     /**
+     * The stable ID of this view for accessibility porposes.
+     */
+    int mAccessibilityViewId = NO_ID;
+
+    /**
      * The view's tag.
      * {@hide}
      *
@@ -2453,7 +2464,7 @@
     public View(Context context) {
         mContext = context;
         mResources = context != null ? context.getResources() : null;
-        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | HORIZONTAL_DIRECTION_INHERIT;
+        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
     }
@@ -2651,18 +2662,18 @@
                         viewFlagMasks |= VISIBILITY_MASK;
                     }
                     break;
-                case com.android.internal.R.styleable.View_horizontalDirection:
+                case com.android.internal.R.styleable.View_layoutDirection:
                     // Clear any HORIZONTAL_DIRECTION flag already set
-                    viewFlagValues &= ~HORIZONTAL_DIRECTION_MASK;
+                    viewFlagValues &= ~LAYOUT_DIRECTION_MASK;
                     // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute
-                    final int horizontalDirection = a.getInt(attr, -1);
-                    if (horizontalDirection != -1) {
-                        viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[horizontalDirection];
+                    final int layoutDirection = a.getInt(attr, -1);
+                    if (layoutDirection != -1) {
+                        viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection];
                     } else {
-                        // Set to default (HORIZONTAL_DIRECTION_INHERIT)
-                        viewFlagValues |= HORIZONTAL_DIRECTION_DEFAULT;
+                        // Set to default (LAYOUT_DIRECTION_INHERIT)
+                        viewFlagValues |= LAYOUT_DIRECTION_DEFAULT;
                     }
-                    viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
+                    viewFlagMasks |= LAYOUT_DIRECTION_MASK;
                     break;
                 case com.android.internal.R.styleable.View_drawingCacheQuality:
                     final int cacheQuality = a.getInt(attr, 0);
@@ -3649,6 +3660,7 @@
      * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
      */
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        event.setSource(this);
         event.setClassName(getClass().getName());
         event.setPackageName(getContext().getPackageName());
         event.setEnabled(isEnabled());
@@ -3664,6 +3676,112 @@
     }
 
     /**
+     * Returns an {@link AccessibilityNodeInfo} representing this view from the
+     * point of view of an {@link android.accessibilityservice.AccessibilityService}.
+     * This method is responsible for obtaining an accessibility node info from a
+     * pool of reusable instances and calling
+     * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on this view to
+     * initialize the former.
+     * <p>
+     * Note: The client is responsible for recycling the obtained instance by calling
+     *       {@link AccessibilityNodeInfo#recycle()} to minimize object creation.
+     * </p>
+     * @return A populated {@link AccessibilityNodeInfo}.
+     *
+     * @see AccessibilityNodeInfo
+     */
+    public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this);
+        onInitializeAccessibilityNodeInfo(info);
+        return info;
+    }
+
+    /**
+     * Initializes an {@link AccessibilityNodeInfo} with information about this view.
+     * The base implementation sets:
+     * <ul>
+     *   <li>{@link AccessibilityNodeInfo#setParent(View)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setClickable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li>
+     * </ul>
+     * <p>
+     * Subclasses should override this method, call the super implementation,
+     * and set additional attributes.
+     * </p>
+     * @param info The instance to initialize.
+     */
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        Rect bounds = mAttachInfo.mTmpInvalRect;
+        getDrawingRect(bounds);
+        info.setBounds(bounds);
+
+        ViewParent parent = getParent();
+        if (parent instanceof View) {
+            View parentView = (View) parent;
+            info.setParent(parentView);
+        }
+
+        info.setPackageName(mContext.getPackageName());
+        info.setClassName(getClass().getName());
+        info.setContentDescription(getContentDescription());
+
+        info.setEnabled(isEnabled());
+        info.setClickable(isClickable());
+        info.setFocusable(isFocusable());
+        info.setFocused(isFocused());
+        info.setSelected(isSelected());
+        info.setLongClickable(isLongClickable());
+
+        // TODO: These make sense only if we are in an AdapterView but all
+        // views can be selected. Maybe from accessiiblity perspective
+        // we should report as selectable view in an AdapterView.
+        info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+        info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+
+        if (isFocusable()) {
+            if (isFocused()) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
+            } else {
+                info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+            }
+        }
+    }
+
+    /**
+     * Gets the unique identifier of this view on the screen for accessibility purposes.
+     * If this {@link View} is not attached to any window, {@value #NO_ID} is returned.
+     *
+     * @return The view accessibility id.
+     *
+     * @hide
+     */
+    public int getAccessibilityViewId() {
+        if (mAccessibilityViewId == NO_ID) {
+            mAccessibilityViewId = sNextAccessibilityViewId++;
+        }
+        return mAccessibilityViewId;
+    }
+
+    /**
+     * Gets the unique identifier of the window in which this View reseides.
+     *
+     * @return The window accessibility id.
+     *
+     * @hide
+     */
+    public int getAccessibilityWindowId() {
+        return mAttachInfo != null ? mAttachInfo.mAccessibilityWindowId : NO_ID;
+    }
+
+    /**
      * Gets the {@link View} description. It briefly describes the view and is
      * primarily used for accessibility support. Set this property to enable
      * better accessibility support for your application. This is especially
@@ -4138,38 +4256,38 @@
     }
 
     /**
-     * Returns the horizontal direction for this view.
+     * Returns the layout direction for this view.
      *
-     * @return One of {@link #HORIZONTAL_DIRECTION_LTR},
-     *   {@link #HORIZONTAL_DIRECTION_RTL},
-     *   {@link #HORIZONTAL_DIRECTION_INHERIT} or
-     *   {@link #HORIZONTAL_DIRECTION_LOCALE}.
-     * @attr ref android.R.styleable#View_horizontalDirection
+     * @return One of {@link #LAYOUT_DIRECTION_LTR},
+     *   {@link #LAYOUT_DIRECTION_RTL},
+     *   {@link #LAYOUT_DIRECTION_INHERIT} or
+     *   {@link #LAYOUT_DIRECTION_LOCALE}.
+     * @attr ref android.R.styleable#View_layoutDirection
      * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LTR,     to = "LTR"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_RTL,     to = "RTL"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_INHERIT, to = "INHERIT"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LOCALE,  to = "LOCALE")
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "LTR"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL,     to = "RTL"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE,  to = "LOCALE")
     })
-    public int getHorizontalDirection() {
-        return mViewFlags & HORIZONTAL_DIRECTION_MASK;
+    public int getLayoutDirection() {
+        return mViewFlags & LAYOUT_DIRECTION_MASK;
     }
 
     /**
-     * Set the horizontal direction for this view.
+     * Set the layout direction for this view.
      *
-     * @param horizontalDirection One of {@link #HORIZONTAL_DIRECTION_LTR},
-     *   {@link #HORIZONTAL_DIRECTION_RTL},
-     *   {@link #HORIZONTAL_DIRECTION_INHERIT} or
-     *   {@link #HORIZONTAL_DIRECTION_LOCALE}.
-     * @attr ref android.R.styleable#View_horizontalDirection
+     * @param layoutDirection One of {@link #LAYOUT_DIRECTION_LTR},
+     *   {@link #LAYOUT_DIRECTION_RTL},
+     *   {@link #LAYOUT_DIRECTION_INHERIT} or
+     *   {@link #LAYOUT_DIRECTION_LOCALE}.
+     * @attr ref android.R.styleable#View_layoutDirection
      * @hide
      */
     @RemotableViewMethod
-    public void setHorizontalDirection(int horizontalDirection) {
-        setFlags(horizontalDirection, HORIZONTAL_DIRECTION_MASK);
+    public void setLayoutDirection(int layoutDirection) {
+        setFlags(layoutDirection, LAYOUT_DIRECTION_MASK);
     }
 
     /**
@@ -4571,6 +4689,16 @@
     }
 
     /**
+     * Finds the Views that contain given text. The containment is case insensitive.
+     * As View's text is considered any text content that View renders.
+     *
+     * @param outViews The output list of matching Views.
+     * @param text The text to match against.
+     */
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+    }
+
+    /**
      * Find and return all touchable views that are descendants of this view,
      * possibly including this view if it is touchable itself.
      *
@@ -4677,8 +4805,8 @@
 
         // need to be focusable in touch mode if in touch mode
         if (isInTouchMode() &&
-                (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
-            return false;
+            (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
+               return false;
         }
 
         // need to not have any parents blocking us
@@ -5975,7 +6103,7 @@
             }
         }
 
-        if ((changed & HORIZONTAL_DIRECTION_MASK) != 0) {
+        if ((changed & LAYOUT_DIRECTION_MASK) != 0) {
             requestLayout();
         }
     }
@@ -8530,24 +8658,24 @@
             mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
         }
         jumpDrawablesToCurrentState();
-        resolveHorizontalDirection();
+        resolveLayoutDirection();
     }
 
     /**
      * Resolving the layout direction. LTR is set initially.
      * We are supposing here that the parent directionality will be resolved before its children
      */
-    private void resolveHorizontalDirection() {
+    private void resolveLayoutDirection() {
         mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
-        switch (getHorizontalDirection()) {
-            case HORIZONTAL_DIRECTION_INHERIT:
+        switch (getLayoutDirection()) {
+            case LAYOUT_DIRECTION_INHERIT:
                 // If this is root view, no need to look at parent's layout dir.
                 if (mParent != null && mParent instanceof ViewGroup &&
                         ((ViewGroup) mParent).isLayoutRtl()) {
                     mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
                 }
                 break;
-            case HORIZONTAL_DIRECTION_RTL:
+            case LAYOUT_DIRECTION_RTL:
                 mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
                 break;
         }
@@ -12719,6 +12847,7 @@
             );
 
             private InvalidateInfo mNext;
+            private boolean mIsPooled;
 
             View target;
 
@@ -12742,6 +12871,14 @@
             void release() {
                 sPool.release(this);
             }
+
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
         }
 
         final IWindowSession mSession;
@@ -12952,6 +13089,11 @@
         final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
 
         /**
+         * The id of the window for accessibility purposes.
+         */
+        int mAccessibilityWindowId = View.NO_ID;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 1e72529..9ab4c82 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -50,18 +50,28 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
+
 import com.android.internal.policy.PolicyManager;
+import com.android.internal.util.Predicate;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
@@ -71,6 +81,7 @@
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -248,6 +259,12 @@
      */
     AudioManager mAudioManager;
 
+    final AccessibilityManager mAccessibilityManager;
+
+    AccessibilityInteractionController mAccessibilityInteractionContrtoller;
+
+    AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
+
     private final int mDensity;
 
     /**
@@ -285,7 +302,7 @@
         // done here instead of in the static block because Zygote does not
         // allow the spawning of threads.
         getWindowSession(context.getMainLooper());
-        
+
         mThread = Thread.currentThread();
         mLocation = new WindowLeaked(null);
         mLocation.fillInStackTrace();
@@ -302,6 +319,11 @@
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
         mAdded = false;
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+        mAccessibilityInteractionConnectionManager =
+            new AccessibilityInteractionConnectionManager();
+        mAccessibilityManager.addAccessibilityStateChangeListener(
+                mAccessibilityInteractionConnectionManager);
         mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
         mViewConfiguration = ViewConfiguration.get(context);
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
@@ -490,10 +512,14 @@
                     InputQueue.registerInputChannel(mInputChannel, mInputHandler,
                             Looper.myQueue());
                 }
-                
+
                 view.assignParent(this);
                 mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
                 mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
+
+                if (mAccessibilityManager.isEnabled()) {
+                    mAccessibilityInteractionConnectionManager.ensureConnection();
+                }
             }
         }
     }
@@ -2004,6 +2030,10 @@
             mView.dispatchDetachedFromWindow();
         }
 
+        mAccessibilityInteractionConnectionManager.ensureNoConnection();
+        mAccessibilityManager.removeAccessibilityStateChangeListener(
+                mAccessibilityInteractionConnectionManager);
+
         mView = null;
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
@@ -2020,7 +2050,6 @@
                 InputQueue.unregisterInputChannel(mInputChannel);
             }
         }
-        
         try {
             sWindowSession.remove(mWindow);
         } catch (RemoteException e) {
@@ -2098,6 +2127,10 @@
     public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
     public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
     public final static int DISPATCH_GENERIC_MOTION = 1018;
+    public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1019;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1020;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1021;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1022;
 
     @Override
     public void handleMessage(Message msg) {
@@ -2298,9 +2331,25 @@
         case DISPATCH_SYSTEM_UI_VISIBILITY: {
             handleDispatchSystemUiVisibilityChanged(msg.arg1);
         } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
+        } break;
+        case DO_PERFORM_ACCESSIBILITY_ACTION: {
+            getAccessibilityInteractionController()
+                .perfromAccessibilityActionUiThread(msg);
+        } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByViewIdUiThread(msg);
+        } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfosByViewTextUiThread(msg);
+        } break;
         }
     }
-    
+
     private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
         if (mFinishedCallback != null) {
             Slog.w(TAG, "Received a new input event from the input queue but there is "
@@ -3220,6 +3269,17 @@
         return mAudioManager;
     }
 
+    public AccessibilityInteractionController getAccessibilityInteractionController() {
+        if (mView == null) {
+            throw new IllegalStateException("getAccessibilityInteractionController"
+                    + " called when there is no mView");
+        }
+        if (mAccessibilityInteractionContrtoller == null) {
+            mAccessibilityInteractionContrtoller = new AccessibilityInteractionController();
+        }
+        return mAccessibilityInteractionContrtoller;
+    }
+
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
             boolean insetsPending) throws RemoteException {
 
@@ -3517,7 +3577,7 @@
      * send an {@link AccessibilityEvent} to announce that.
      */
     private void sendAccessibilityEvents() {
-        if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -3545,7 +3605,7 @@
         if (mView == null) {
             return false;
         }
-        AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event);
+        mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
 
@@ -3608,18 +3668,18 @@
     static class InputMethodCallback extends IInputMethodCallback.Stub {
         private WeakReference<ViewAncestor> mViewAncestor;
 
-        public InputMethodCallback(ViewAncestor viewRoot) {
-            mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+        public InputMethodCallback(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
         }
 
         public void finishedEvent(int seq, boolean handled) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchFinishedEvent(seq, handled);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchFinishedEvent(seq, handled);
             }
         }
 
-        public void sessionCreated(IInputMethodSession session) throws RemoteException {
+        public void sessionCreated(IInputMethodSession session) {
             // Stub -- not for use in the client.
         }
     }
@@ -3627,36 +3687,37 @@
     static class W extends IWindow.Stub {
         private final WeakReference<ViewAncestor> mViewAncestor;
 
-        W(ViewAncestor viewRoot) {
-            mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+        W(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
         }
 
         public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
                 boolean reportDraw, Configuration newConfig) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw,
+                        newConfig);
             }
         }
 
         public void dispatchAppVisibility(boolean visible) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchAppVisibility(visible);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchAppVisibility(visible);
             }
         }
 
         public void dispatchGetNewSurface() {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchGetNewSurface();
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchGetNewSurface();
             }
         }
 
         public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
             }
         }
 
@@ -3674,9 +3735,9 @@
         }
 
         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                final View view = viewRoot.mView;
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                final View view = viewAncestor.mView;
                 if (view != null) {
                     if (checkCallingPermission(Manifest.permission.DUMP) !=
                             PackageManager.PERMISSION_GRANTED) {
@@ -3705,9 +3766,9 @@
         }
         
         public void closeSystemDialogs(String reason) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchCloseSystemDialogs(reason);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchCloseSystemDialogs(reason);
             }
         }
         
@@ -3720,7 +3781,7 @@
                 }
             }
         }
-        
+
         public void dispatchWallpaperCommand(String action, int x, int y,
                 int z, Bundle extras, boolean sync) {
             if (sync) {
@@ -3733,17 +3794,16 @@
 
         /* Drag/drop */
         public void dispatchDragEvent(DragEvent event) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchDragEvent(event);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchDragEvent(event);
             }
         }
 
-        @Override
         public void dispatchSystemUiVisibilityChanged(int visibility) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchSystemUiVisibilityChanged(visibility);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchSystemUiVisibilityChanged(visibility);
             }
         }
     }
@@ -4053,5 +4113,395 @@
         }
     }
 
+    /**
+     * Class for managing the accessibility interaction connection
+     * based on the global accessibility state.
+     */
+    final class AccessibilityInteractionConnectionManager
+            implements AccessibilityStateChangeListener {
+        public void onAccessibilityStateChanged(boolean enabled) {
+            if (enabled) {
+                ensureConnection();
+            } else {
+                ensureNoConnection();
+            }
+        }
+
+        public void ensureConnection() {
+            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+            if (!registered) {
+                mAttachInfo.mAccessibilityWindowId =
+                    mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+                            new AccessibilityInteractionConnection(ViewAncestor.this));
+            }
+        }
+
+        public void ensureNoConnection() {
+            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+            if (registered) {
+                mAttachInfo.mAccessibilityWindowId = View.NO_ID;
+                mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
+            }
+        }
+    }
+
+    /**
+     * This class is an interface this ViewAncestor provides to the
+     * AccessibilityManagerService to the latter can interact with
+     * the view hierarchy in this ViewAncestor.
+     */
+    final class AccessibilityInteractionConnection
+            extends IAccessibilityInteractionConnection.Stub {
+        private final WeakReference<ViewAncestor> mViewAncestor;
+
+        AccessibilityInteractionConnection(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
+                        interactionId, callback);
+        }
+
+        public void performAccessibilityAction(int accessibilityId, int action,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
+                        callback);
+        }
+
+        public void findAccessibilityNodeInfoByViewId(int viewId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
+        }
+
+        public void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId, callback);
+        }
+    }
+
+    /**
+     * Class for managing accessibility interactions initiated from the system
+     * and targeting the view hierarchy. A *ClientThread method is to be
+     * called from the interaction connection this ViewAncestor gives the
+     * system to talk to it and a corresponding *UiThread method that is executed
+     * on the UI thread.
+     */
+    final class AccessibilityInteractionController {
+        private static final int POOL_SIZE = 5;
+
+        private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate =
+            new FindByAccessibilitytIdPredicate();
+
+        private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+            new ArrayList<AccessibilityNodeInfo>();
+
+        // Reusable poolable arguments for interacting with the view hierarchy
+        // to fit more arguments than Message and to avoid sharing objects between
+        // two messages since several threads can send messages concurrently.
+        private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
+                new PoolableManager<SomeArgs>() {
+                    public SomeArgs newInstance() {
+                        return new SomeArgs();
+                    }
+
+                    public void onAcquired(SomeArgs info) {
+                        /* do nothing */
+                    }
+
+                    public void onReleased(SomeArgs info) {
+                        info.clear();
+                    }
+                }, POOL_SIZE)
+        );
+
+        public class SomeArgs implements Poolable<SomeArgs> {
+            private SomeArgs mNext;
+            private boolean mIsPooled;
+
+            public Object arg1;
+            public Object arg2;
+            public int argi1;
+            public int argi2;
+            public int argi3;
+
+            public SomeArgs getNextPoolable() {
+                return mNext;
+            }
+
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setNextPoolable(SomeArgs args) {
+                mNext = args;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
+
+            private void clear() {
+                arg1 = null;
+                arg2 = null;
+                argi1 = 0;
+                argi2 = 0;
+                argi3 = 0;
+            }
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+            message.arg1 = accessibilityId;
+            message.arg2 = interactionId;
+            message.obj = callback;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+            final int accessibilityId = message.arg1;
+            final int interactionId = message.arg2;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) message.obj;
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+
+            FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate;
+            predicate.init(accessibilityId);
+
+            View target = root.findViewByPredicate(predicate);
+            if (target != null) {
+                AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+                try {
+                    callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+                } catch (RemoteException re) {
+                    /* ignore - the other side will time out */
+                }
+            }
+        }
+
+        public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+            message.arg1 = viewId;
+            message.arg2 = interactionId;
+            message.obj = callback;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+            final int viewId = message.arg1;
+            final int interactionId = message.arg2;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) message.obj;
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+            View target = root.findViewById(viewId);
+            if (target != null) {
+                AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+                try {
+                    callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+                } catch (RemoteException re) {
+                    /* ignore - the other side will time out */
+                }
+            }
+        }
+
+        public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
+            SomeArgs args = mPool.acquire();
+            args.arg1 = text;
+            args.argi1 = interactionId;
+            args.arg2 = callback;
+            message.obj = args;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
+            SomeArgs args = (SomeArgs) message.obj;
+            final String text = (String) args.arg1;
+            final int interactionId = args.argi1;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) args.arg2;
+            mPool.release(args);
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+
+            ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
+            foundViews.clear();
+
+            root.findViewsWithText(foundViews, text);
+            if (foundViews.isEmpty()) {
+                return;
+            }
+
+            List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+            infos.clear();
+
+            final int viewCount = foundViews.size();
+            for (int i = 0; i < viewCount; i++) {
+                View foundView = foundViews.get(i);
+                infos.add(foundView.createAccessibilityNodeInfo());
+            }
+
+            try {
+                callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+
+        public void performAccessibilityActionClientThread(int accessibilityId, int action,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
+            SomeArgs args = mPool.acquire();
+            args.argi1 = accessibilityId;
+            args.argi2 = action;
+            args.argi3 = interactionId;
+            args.arg1 = callback;
+            message.obj = args;
+            sendMessage(message);
+        }
+
+        public void perfromAccessibilityActionUiThread(Message message) {
+            SomeArgs args = (SomeArgs) message.obj;
+            final int accessibilityId = args.argi1;
+            final int action = args.argi2;
+            final int interactionId = args.argi3;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) args.arg1;
+            mPool.release(args);
+
+            if (ViewAncestor.this.mView == null) {
+                return;
+            }
+
+            boolean succeeded = false;
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_FOCUS: {
+                    succeeded = performActionFocus(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+                    succeeded = performActionClearFocus(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_SELECT: {
+                    succeeded = performActionSelect(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+                    succeeded = performActionClearSelection(accessibilityId);
+                } break;
+            }
+
+            try {
+                callback.setPerformAccessibilityActionResult(succeeded, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+
+        private boolean performActionFocus(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            // Get out of touch mode since accessibility wants to move focus around.
+            ensureTouchMode(false);
+            return target.requestFocus();
+        }
+
+        private boolean performActionClearFocus(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (!target.isFocused()) {
+                return false;
+            }
+            target.clearFocus();
+            return !target.isFocused();
+        }
+
+        private boolean performActionSelect(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (target.isSelected()) {
+                return false;
+            }
+            target.setSelected(true);
+            return target.isSelected();
+        }
+
+        private boolean performActionClearSelection(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (!target.isSelected()) {
+                return false;
+            }
+            target.setSelected(false);
+            return !target.isSelected();
+        }
+
+        private View findViewByAccessibilityId(int accessibilityId) {
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return null;
+            }
+            mFindByAccessibilityIdPredicate.init(accessibilityId);
+            return root.findViewByPredicate(mFindByAccessibilityIdPredicate);
+        }
+
+        private final class FindByAccessibilitytIdPredicate implements Predicate<View> {
+            public int mSerchedId;
+
+            public void init(int searchedId) {
+                mSerchedId = searchedId;
+            }
+
+            public boolean apply(View view) {
+                return (view.getAccessibilityViewId() == mSerchedId);
+            }
+        }
+    }
+
     private static native void nativeShowFPS(Canvas canvas, int durationMillis);
 }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 36bb046..5919150 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -110,7 +110,21 @@
      * double-tap.
      */
     private static final int DOUBLE_TAP_TIMEOUT = 300;
-    
+
+    /**
+     * Defines the maximum duration in milliseconds between a touch pad
+     * touch and release for a given touch to be considered a tap (click) as
+     * opposed to a hover movement gesture.
+     */
+    private static final int HOVER_TAP_TIMEOUT = 150;
+
+    /**
+     * Defines the maximum distance in pixels that a touch pad touch can move
+     * before being released for it to be considered a tap (click) as opposed
+     * to a hover movement gesture.
+     */
+    private static final int HOVER_TAP_SLOP = 20;
+
     /**
      * Defines the duration in milliseconds we want to display zoom controls in response 
      * to a user panning within an application.
@@ -369,7 +383,7 @@
     public static int getTapTimeout() {
         return TAP_TIMEOUT;
     }
-    
+
     /**
      * @return the duration in milliseconds we will wait to see if a touch event
      * is a jump tap. If the user does not move within this interval, it is
@@ -387,7 +401,27 @@
     public static int getDoubleTapTimeout() {
         return DOUBLE_TAP_TIMEOUT;
     }
-    
+
+    /**
+     * @return the maximum duration in milliseconds between a touch pad
+     * touch and release for a given touch to be considered a tap (click) as
+     * opposed to a hover movement gesture.
+     * @hide
+     */
+    public static int getHoverTapTimeout() {
+        return HOVER_TAP_TIMEOUT;
+    }
+
+    /**
+     * @return the maximum distance in pixels that a touch pad touch can move
+     * before being released for it to be considered a tap (click) as opposed
+     * to a hover movement gesture.
+     * @hide
+     */
+    public static int getHoverTapSlop() {
+        return HOVER_TAP_SLOP;
+    }
+
     /**
      * @return Inset in pixels to look for touchable content when the user touches the edge of the
      *         screen
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f504b90..57ee8a0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -772,6 +773,18 @@
         }
     }
 
+    @Override
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+        final int childrenCount = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < childrenCount; i++) {
+            View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                child.findViewsWithText(outViews, text);
+            }
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -2007,6 +2020,16 @@
         return false;
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        for (int i = 0, count = mChildrenCount; i < count; i++) {
+            View child = mChildren[i];
+            info.addChild(child);
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a1ddd08..b0181bb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -77,8 +77,8 @@
             implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
-         * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or
-         * {@link Gravity#AFTER} it provides an offset from the given edge.
+         * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or
+         * {@link Gravity#END} it provides an offset from the given edge.
          */
         @ViewDebug.ExportedProperty
         public int x;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 7b80797b..32bfa2f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,9 +16,12 @@
 
 package android.view.accessibility;
 
+import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
 import android.text.TextUtils;
+import android.view.View;
 
 import java.util.ArrayList;
 
@@ -159,6 +162,7 @@
  * @see android.accessibilityservice.AccessibilityService
  */
 public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
+    private static final boolean DEBUG = false;
 
     /**
      * Invalid selection/focus position.
@@ -256,21 +260,38 @@
     private static final Object sPoolLock = new Object();
     private static AccessibilityEvent sPool;
     private static int sPoolSize;
-
     private AccessibilityEvent mNext;
     private boolean mIsInPool;
 
     private int mEventType;
+    private int mSourceAccessibilityViewId;
+    private int mSourceAccessibilityWindowId;
     private CharSequence mPackageName;
     private long mEventTime;
 
     private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
 
+    private IAccessibilityServiceConnection mConnection;
+
     /*
      * Hide constructor from clients.
      */
     private AccessibilityEvent() {
+    }
 
+    /**
+     * Initialize an event from another one.
+     *
+     * @param event The event to initialize from.
+     */
+    void init(AccessibilityEvent event) {
+        super.init(event);
+        mEventType = event.mEventType;
+        mEventTime = event.mEventTime;
+        mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId;
+        mSourceAccessibilityViewId = event.mSourceAccessibilityViewId;
+        mPackageName = event.mPackageName;
+        mConnection = event.mConnection;
     }
 
     /**
@@ -286,8 +307,11 @@
      * Appends an {@link AccessibilityRecord} to the end of event records.
      *
      * @param record The record to append.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void appendRecord(AccessibilityRecord record) {
+        enforceNotSealed();
         mRecords.add(record);
     }
 
@@ -311,11 +335,89 @@
     }
 
     /**
+     * Sets the event source.
+     *
+     * @param source The source.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setSource(View source) {
+        enforceNotSealed();
+        if (source != null) {
+            mSourceAccessibilityWindowId = source.getAccessibilityWindowId();
+            mSourceAccessibilityViewId = source.getAccessibilityViewId();
+        } else {
+            mSourceAccessibilityWindowId = View.NO_ID;
+            mSourceAccessibilityViewId = View.NO_ID;
+        }
+    }
+
+    /**
+     * Gets the {@link AccessibilityNodeInfo} of the event source.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @return The info.
+     */
+    public AccessibilityNodeInfo getSource() {
+        enforceSealed();
+        if (mSourceAccessibilityWindowId == View.NO_ID
+                || mSourceAccessibilityViewId == View.NO_ID) {
+            return null;
+        }
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(
+                    mSourceAccessibilityWindowId, mSourceAccessibilityViewId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the id of the window from which the event comes from.
+     *
+     * @return The window id.
+     */
+    public int getAccessibilityWindowId() {
+        return mSourceAccessibilityWindowId;
+    }
+
+    /**
+     * Sets the client token for the accessibility service that
+     * provided this node info.
+     *
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    public final void setConnection(IAccessibilityServiceConnection connection) {
+        mConnection = connection;
+    }
+
+    /**
+     * Gets the accessibility window id of the source window.
+     *
+     * @return The id.
+     *
+     * @hide
+     */
+    public int getSourceAccessibilityWindowId() {
+        return mSourceAccessibilityWindowId;
+    }
+
+    /**
      * Sets the event type.
      *
      * @param eventType The event type.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEventType(int eventType) {
+        enforceNotSealed();
         mEventType = eventType;
     }
 
@@ -332,8 +434,11 @@
      * Sets the time in which this event was sent.
      *
      * @param eventTime The event time.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEventTime(long eventTime) {
+        enforceNotSealed();
         mEventTime = eventTime;
     }
 
@@ -350,8 +455,11 @@
      * Sets the package name of the source.
      *
      * @param packageName The package name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPackageName(CharSequence packageName) {
+        enforceNotSealed();
         mPackageName = packageName;
     }
 
@@ -370,6 +478,27 @@
 
     /**
      * Returns a cached instance if such is available or a new one is
+     * instantiated with type property set.
+     *
+     * @param event The other event.
+     * @return An instance.
+     */
+    public static AccessibilityEvent obtain(AccessibilityEvent event) {
+        AccessibilityEvent eventClone = AccessibilityEvent.obtain();
+        eventClone.init(event);
+
+        final int recordCount = event.mRecords.size();
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = event.mRecords.get(i);
+            AccessibilityRecord recordClone = AccessibilityRecord.obtain(record);
+            eventClone.mRecords.add(recordClone);
+        }
+
+        return eventClone;
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
      * instantiated.
      *
      * @return An instance.
@@ -413,11 +542,16 @@
 
     /**
      * Clears the state of this instance.
+     *
+     * @hide
      */
     @Override
     protected void clear() {
         super.clear();
+        mConnection = null;
         mEventType = 0;
+        mSourceAccessibilityViewId = View.NO_ID;
+        mSourceAccessibilityWindowId = View.NO_ID;
         mPackageName = null;
         mEventTime = 0;
         while (!mRecords.isEmpty()) {
@@ -432,7 +566,14 @@
      * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
      */
     public void initFromParcel(Parcel parcel) {
+        if (parcel.readInt() == 1) {
+            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+                    parcel.readStrongBinder());
+        }
+        setSealed(parcel.readInt() == 1);
         mEventType = parcel.readInt();
+        mSourceAccessibilityWindowId = parcel.readInt();
+        mSourceAccessibilityViewId = parcel.readInt();
         mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         mEventTime = parcel.readLong();
         readAccessibilityRecordFromParcel(this, parcel);
@@ -471,7 +612,16 @@
      * {@inheritDoc}
      */
     public void writeToParcel(Parcel parcel, int flags) {
+        if (mConnection == null) {
+            parcel.writeInt(0);
+        } else {
+            parcel.writeInt(1);
+            parcel.writeStrongBinder(mConnection.asBinder());
+        }
+        parcel.writeInt(isSealed() ? 1 : 0);
         parcel.writeInt(mEventType);
+        parcel.writeInt(mSourceAccessibilityWindowId);
+        parcel.writeInt(mSourceAccessibilityViewId);
         TextUtils.writeToParcel(mPackageName, parcel, 0);
         parcel.writeLong(mEventTime);
         writeAccessibilityRecordToParcel(this, parcel, flags);
@@ -519,18 +669,36 @@
         builder.append("; EventType: ").append(eventTypeToString(mEventType));
         builder.append("; EventTime: ").append(mEventTime);
         builder.append("; PackageName: ").append(mPackageName);
-        builder.append(" \n{\n");
         builder.append(super.toString());
-        builder.append("\n");
-        for (int i = 0; i < mRecords.size(); i++) {
-            AccessibilityRecord record = mRecords.get(i);
-            builder.append("  Record ");
-            builder.append(i);
-            builder.append(":");
-            builder.append(record.toString());
+        if (DEBUG) {
             builder.append("\n");
+            builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId);
+            builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId);
+            for (int i = 0; i < mRecords.size(); i++) {
+                AccessibilityRecord record = mRecords.get(i);
+                builder.append("  Record ");
+                builder.append(i);
+                builder.append(":");
+                builder.append(" [ ClassName: " + record.mClassName);
+                builder.append("; Text: " + record.mText);
+                builder.append("; ContentDescription: " + record.mContentDescription);
+                builder.append("; ItemCount: " + record.mItemCount);
+                builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
+                builder.append("; IsEnabled: " + record.isEnabled());
+                builder.append("; IsPassword: " + record.isPassword());
+                builder.append("; IsChecked: " + record.isChecked());
+                builder.append("; IsFullScreen: " + record.isFullScreen());
+                builder.append("; BeforeText: " + record.mBeforeText);
+                builder.append("; FromIndex: " + record.mFromIndex);
+                builder.append("; AddedCount: " + record.mAddedCount);
+                builder.append("; RemovedCount: " + record.mRemovedCount);
+                builder.append("; ParcelableData: " + record.mParcelableData);
+                builder.append(" ]");
+                builder.append("\n");
+            }
+        } else {
+            builder.append("; recordCount: ").append(getAddedCount());
         }
-        builder.append("}\n");
         return builder.toString();
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 88f8878..eece64a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -28,10 +28,13 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.util.Log;
+import android.view.IWindow;
+import android.view.View;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
@@ -62,6 +65,21 @@
 
     boolean mIsEnabled;
 
+    final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners =
+        new CopyOnWriteArrayList<AccessibilityStateChangeListener>();
+
+    /**
+     * Listener for the accessibility state.
+     */
+    public interface AccessibilityStateChangeListener {
+        /**
+         * Called back on change in the accessibility state.
+         *
+         * @param enabled
+         */
+        public void onAccessibilityStateChanged(boolean enabled);
+    }
+
     final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
         public void setEnabled(boolean enabled) {
             mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
@@ -78,9 +96,8 @@
         public void handleMessage(Message message) {
             switch (message.what) {
                 case DO_SET_ENABLED :
-                    synchronized (mHandler) {
-                        mIsEnabled = (message.arg1 == 1);
-                    }
+                    final boolean isEnabled = (message.arg1 == 1);
+                    setAccessibilityState(isEnabled);
                     return;
                 default :
                     Log.w(LOG_TAG, "Unknown message type: " + message.what);
@@ -117,7 +134,8 @@
         mService = service;
 
         try {
-            mIsEnabled = mService.addClient(mClient);
+            final boolean isEnabled = mService.addClient(mClient);
+            setAccessibilityState(isEnabled);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
         }
@@ -253,4 +271,81 @@
         }
         return Collections.unmodifiableList(services);
     }
+
+    /**
+     * Registers an {@link AccessibilityStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if successfully registered.
+     */
+    public boolean addAccessibilityStateChangeListener(
+            AccessibilityStateChangeListener listener) {
+        return mAccessibilityStateChangeListeners.add(listener);
+    }
+
+    /**
+     * Unregisters an {@link AccessibilityStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if successfully unregistered.
+     */
+    public boolean removeAccessibilityStateChangeListener(
+            AccessibilityStateChangeListener listener) {
+        return mAccessibilityStateChangeListeners.remove(listener);
+    }
+
+    /**
+     * Sets the enabled state.
+     *
+     * @param isEnabled The accessibility state.
+     */
+    private void setAccessibilityState(boolean isEnabled) {
+        synchronized (mHandler) {
+            if (isEnabled != mIsEnabled) {
+                mIsEnabled = isEnabled;
+                notifyAccessibilityStateChanged();
+            }
+        }
+    }
+
+    /**
+     * Notifies the registered {@link AccessibilityStateChangeListener}s.
+     */
+    private void notifyAccessibilityStateChanged() {
+        final int listenerCount = mAccessibilityStateChangeListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
+        }
+    }
+
+    /**
+     * Adds an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is added.
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    public int addAccessibilityInteractionConnection(IWindow windowToken,
+            IAccessibilityInteractionConnection connection) {
+        try {
+            return mService.addAccessibilityInteractionConnection(windowToken, connection);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+        }
+        return View.NO_ID;
+    }
+
+    /**
+     * Removed an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is removed.
+     *
+     * @hide
+     */
+    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+        try {
+            mService.removeAccessibilityInteractionConnection(windowToken);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
new file mode 100644
index 0000000..59175ce
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.view.accessibility;
+
+parcelable AccessibilityNodeInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
new file mode 100644
index 0000000..752f864
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -0,0 +1,947 @@
+/*
+ * 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 android.view.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.View;
+
+/**
+ * This class represents a node of the screen content. From the point of
+ * view of an accessibility service the screen content is presented as tree
+ * of accessibility nodes.
+ *
+ * TODO(svertoslavganov): Update the documentation, add sample, and describe
+ *                        the security policy.
+ */
+public class AccessibilityNodeInfo implements Parcelable {
+
+    private static final boolean DEBUG = false;
+
+    // Actions.
+
+    /**
+     * Action that focuses the node.
+     */
+    public static final int ACTION_FOCUS =  0x00000001;
+
+    /**
+     * Action that unfocuses the node.
+     */
+    public static final int ACTION_CLEAR_FOCUS =  0x00000002;
+
+    /**
+     * Action that selects the node.
+     */
+    public static final int ACTION_SELECT =  0x00000004;
+
+    /**
+     * Action that unselects the node.
+     */
+    public static final int ACTION_CLEAR_SELECTION =  0x00000008;
+
+    // Boolean attributes.
+
+    private static final int PROPERTY_CHECKABLE = 0x00000001;
+
+    private static final int PROPERTY_CHECKED = 0x00000002;
+
+    private static final int PROPERTY_FOCUSABLE = 0x00000004;
+
+    private static final int PROPERTY_FOCUSED = 0x00000008;
+
+    private static final int PROPERTY_SELECTED = 0x00000010;
+
+    private static final int PROPERTY_CLICKABLE = 0x00000020;
+
+    private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
+
+    private static final int PROPERTY_ENABLED = 0x00000080;
+
+    private static final int PROPERTY_PASSWORD = 0x00000100;
+
+    // Readable representations - lazily initialized.
+    private static SparseArray<String> sActionSymbolicNames;
+
+    // Housekeeping.
+    private static final int MAX_POOL_SIZE = 50;
+    private static final Object sPoolLock = new Object();
+    private static AccessibilityNodeInfo sPool;
+    private static int sPoolSize;
+    private AccessibilityNodeInfo mNext;
+    private boolean mIsInPool;
+    private boolean mSealed;
+
+    // Data.
+    private int mAccessibilityViewId;
+    private int mAccessibilityWindowId;
+    private int mParentAccessibilityViewId;
+    private int mBooleanProperties;
+    private final Rect mBounds = new Rect();
+
+    private CharSequence mPackageName;
+    private CharSequence mClassName;
+    private CharSequence mText;
+    private CharSequence mContentDescription;
+
+    private final SparseIntArray mChildAccessibilityIds = new SparseIntArray();
+    private int mActions;
+
+    private IAccessibilityServiceConnection mConnection;
+
+    /**
+     * Hide constructor from clients.
+     */
+    private AccessibilityNodeInfo() {
+        /* do nothing */
+    }
+
+    /**
+     * Sets the source.
+     *
+     * @param source The info source.
+     */
+    public void setSource(View source) {
+        enforceNotSealed();
+        mAccessibilityViewId = source.getAccessibilityViewId();
+        mAccessibilityWindowId = source.getAccessibilityWindowId();
+    }
+
+    /**
+     * Gets the id of the window from which the info comes from.
+     *
+     * @return The window id.
+     */
+    public int getAccessibilityWindowId() {
+        return mAccessibilityWindowId;
+    }
+
+    /**
+     * Gets the number of children.
+     *
+     * @return The child count.
+     */
+    public int getChildCount() {
+        return mChildAccessibilityIds.size();
+    }
+
+    /**
+     * Get the child at given index.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @param index The child index.
+     * @return The child node.
+     *
+     * @throws IllegalStateException If called outside of an AccessibilityService.
+     *
+     */
+    public AccessibilityNodeInfo getChild(int index) {
+        enforceSealed();
+        final int childAccessibilityViewId = mChildAccessibilityIds.get(index);
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+                    childAccessibilityViewId);
+        } catch (RemoteException e) {
+             return null;
+        }
+    }
+
+    /**
+     * Adds a child.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param child The child.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void addChild(View child) {
+        enforceNotSealed();
+        final int childAccessibilityViewId = child.getAccessibilityViewId();
+        final int index = mChildAccessibilityIds.size();
+        mChildAccessibilityIds.put(index, childAccessibilityViewId);
+    }
+
+    /**
+     * Gets the actions that can be performed on the node.
+     *
+     * @return The bit mask of with actions.
+     *
+     * @see AccessibilityNodeInfo#ACTION_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_SELECT
+     * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
+     */
+    public int getActions() {
+        return mActions;
+    }
+
+    /**
+     * Adds an action that can be performed on the node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param action The action.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void addAction(int action) {
+        enforceNotSealed();
+        mActions |= action;
+    }
+
+    /**
+     * Performs an action on the node.
+     * <p>
+     *   Note: An action can be performed only if the request is made
+     *   from an {@link android.accessibilityservice.AccessibilityService}.
+     * </p>
+     * @param action The action to perform.
+     * @return True if the action was performed.
+     *
+     * @throws IllegalStateException If called outside of an AccessibilityService.
+     */
+    public boolean performAction(int action) {
+        enforceSealed();
+        try {
+            return mConnection.performAccessibilityAction(mAccessibilityWindowId,
+                    mAccessibilityViewId, action);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Gets the unique id identifying this node's parent.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @return The node's patent id.
+     */
+    public AccessibilityNodeInfo getParent() {
+        enforceSealed();
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+                    mParentAccessibilityViewId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the parent.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param parent The parent.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setParent(View parent) {
+        enforceNotSealed();
+        mParentAccessibilityViewId = parent.getAccessibilityViewId();
+    }
+
+    /**
+     * Gets the node bounds in parent coordinates.
+     *
+     * @param outBounds The output node bounds.
+     */
+    public void getBounds(Rect outBounds) {
+        outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
+    }
+
+    /**
+     * Sets the node bounds in parent coordinates.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param bounds The node bounds.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setBounds(Rect bounds) {
+        enforceNotSealed();
+        mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    /**
+     * Gets whether this node is checkable.
+     *
+     * @return True if the node is checkable.
+     */
+    public boolean isCheckable() {
+        return getBooleanProperty(PROPERTY_CHECKABLE);
+    }
+
+    /**
+     * Sets whether this node is checkable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param checkable True if the node is checkable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setCheckable(boolean checkable) {
+        setBooleanProperty(PROPERTY_CHECKABLE, checkable);
+    }
+
+    /**
+     * Gets whether this node is checked.
+     *
+     * @return True if the node is checked.
+     */
+    public boolean isChecked() {
+        return getBooleanProperty(PROPERTY_CHECKED);
+    }
+
+    /**
+     * Sets whether this node is checked.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param checked True if the node is checked.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setChecked(boolean checked) {
+        setBooleanProperty(PROPERTY_CHECKED, checked);
+    }
+
+    /**
+     * Gets whether this node is focusable.
+     *
+     * @return True if the node is focusable.
+     */
+    public boolean isFocusable() {
+        return getBooleanProperty(PROPERTY_FOCUSABLE);
+    }
+
+    /**
+     * Sets whether this node is focusable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param focusable True if the node is focusable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFocusable(boolean focusable) {
+        setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
+    }
+
+    /**
+     * Gets whether this node is focused.
+     *
+     * @return True if the node is focused.
+     */
+    public boolean isFocused() {
+        return getBooleanProperty(PROPERTY_FOCUSED);
+    }
+
+    /**
+     * Sets whether this node is focused.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param focused True if the node is focused.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFocused(boolean focused) {
+        setBooleanProperty(PROPERTY_FOCUSED, focused);
+    }
+
+    /**
+     * Gets whether this node is selected.
+     *
+     * @return True if the node is selected.
+     */
+    public boolean isSelected() {
+        return getBooleanProperty(PROPERTY_SELECTED);
+    }
+
+    /**
+     * Sets whether this node is selected.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param selected True if the node is selected.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setSelected(boolean selected) {
+        setBooleanProperty(PROPERTY_SELECTED, selected);
+    }
+
+    /**
+     * Gets whether this node is clickable.
+     *
+     * @return True if the node is clickable.
+     */
+    public boolean isClickable() {
+        return getBooleanProperty(PROPERTY_CLICKABLE);
+    }
+
+    /**
+     * Sets whether this node is clickable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param clickable True if the node is clickable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setClickable(boolean clickable) {
+        setBooleanProperty(PROPERTY_CLICKABLE, clickable);
+    }
+
+    /**
+     * Gets whether this node is long clickable.
+     *
+     * @return True if the node is long clickable.
+     */
+    public boolean isLongClickable() {
+        return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
+    }
+
+    /**
+     * Sets whether this node is long clickable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param longClickable True if the node is long clickable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setLongClickable(boolean longClickable) {
+        setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
+    }
+
+    /**
+     * Gets whether this node is enabled.
+     *
+     * @return True if the node is enabled.
+     */
+    public boolean isEnabled() {
+        return getBooleanProperty(PROPERTY_ENABLED);
+    }
+
+    /**
+     * Sets whether this node is enabled.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param enabled True if the node is enabled.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setEnabled(boolean enabled) {
+        setBooleanProperty(PROPERTY_ENABLED, enabled);
+    }
+
+    /**
+     * Gets whether this node is a password.
+     *
+     * @return True if the node is a password.
+     */
+    public boolean isPassword() {
+        return getBooleanProperty(PROPERTY_PASSWORD);
+    }
+
+    /**
+     * Sets whether this node is a password.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param password True if the node is a password.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setPassword(boolean password) {
+        setBooleanProperty(PROPERTY_PASSWORD, password);
+    }
+
+    /**
+     * Gets the package this node comes from.
+     *
+     * @return The package name.
+     */
+    public CharSequence getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Sets the package this node comes from.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param packageName The package name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setPackageName(CharSequence packageName) {
+        enforceNotSealed();
+        mPackageName = packageName;
+    }
+
+    /**
+     * Gets the class this node comes from.
+     *
+     * @return The class name.
+     */
+    public CharSequence getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Sets the class this node comes from.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param className The class name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setClassName(CharSequence className) {
+        enforceNotSealed();
+        mClassName = className;
+    }
+
+    /**
+     * Gets the text of this node.
+     *
+     * @return The text.
+     */
+    public CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Sets the text of this node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param text The text.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setText(CharSequence text) {
+        enforceNotSealed();
+        mText = text;
+    }
+
+    /**
+     * Gets the content description of this node.
+     *
+     * @return The content description.
+     */
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * Sets the content description of this node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param contentDescription The content description.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setContentDescription(CharSequence contentDescription) {
+        enforceNotSealed();
+        mContentDescription = contentDescription;
+    }
+
+    /**
+     * Gets the value of a boolean property.
+     *
+     * @param property The property.
+     * @return The value.
+     */
+    private boolean getBooleanProperty(int property) {
+        return (mBooleanProperties & property) != 0;
+    }
+
+    /**
+     * Sets a boolean property.
+     *
+     * @param property The property.
+     * @param value The value.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    private void setBooleanProperty(int property, boolean value) {
+        enforceNotSealed();
+        if (value) {
+            mBooleanProperties |= property;
+        } else {
+            mBooleanProperties &= ~property;
+        }
+    }
+
+    /**
+     * Sets the connection for interacting with the system.
+     *
+     * @param connection The client token.
+     *
+     * @hide
+     */
+    public final void setConnection(IAccessibilityServiceConnection connection) {
+        mConnection = connection;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Sets if this instance is sealed.
+     *
+     * @param sealed Whether is sealed.
+     *
+     * @hide
+     */
+    public void setSealed(boolean sealed) {
+        mSealed = sealed;
+    }
+
+    /**
+     * Gets if this instance is sealed.
+     *
+     * @return Whether is sealed.
+     *
+     * @hide
+     */
+    public boolean isSealed() {
+        return mSealed;
+    }
+
+    /**
+     * Enforces that this instance is sealed.
+     *
+     * @throws IllegalStateException If this instance is not sealed.
+     *
+     * @hide
+     */
+    protected void enforceSealed() {
+        if (!isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on a not sealed instance.");
+        }
+    }
+
+    /**
+     * Enforces that this instance is not sealed.
+     *
+     * @throws IllegalStateException If this instance is sealed.
+     *
+     * @hide
+     */
+    protected void enforceNotSealed() {
+        if (isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on an sealed instance.");
+        }
+    }
+
+    /**
+     * Returns a cached instance if such is available otherwise a new one
+     * and sets the source.
+     *
+     * @return An instance.
+     *
+     * @see #setSource(View)
+     */
+    public static AccessibilityNodeInfo obtain(View source) {
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        info.setSource(source);
+        return info;
+    }
+
+    /**
+     * Returns a cached instance if such is available otherwise a new one.
+     *
+     * @return An instance.
+     */
+    public static AccessibilityNodeInfo obtain() {
+        synchronized (sPoolLock) {
+            if (sPool != null) {
+                AccessibilityNodeInfo info = sPool;
+                sPool = sPool.mNext;
+                sPoolSize--;
+                info.mNext = null;
+                info.mIsInPool = false;
+            }
+            return new AccessibilityNodeInfo();
+        }
+    }
+
+    /**
+     * Return an instance back to be reused.
+     * <p>
+     * <b>Note: You must not touch the object after calling this function.</b>
+     *
+     * @throws IllegalStateException If the info is already recycled.
+     */
+    public void recycle() {
+        if (mIsInPool) {
+            throw new IllegalStateException("Info already recycled!");
+        }
+        clear();
+        synchronized (sPoolLock) {
+            if (sPoolSize <= MAX_POOL_SIZE) {
+                mNext = sPool;
+                sPool = this;
+                mIsInPool = true;
+                sPoolSize++;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     *   <b>Note: After the instance is written to a parcel it is recycled.
+     *      You must not touch the object after calling this function.</b>
+     * </p>
+     */
+    public void writeToParcel(Parcel parcel, int flags) {
+        if (mConnection == null) {
+            parcel.writeInt(0);
+        } else {
+            parcel.writeInt(1);
+            parcel.writeStrongBinder(mConnection.asBinder());
+        }
+        parcel.writeInt(isSealed() ? 1 : 0);
+        parcel.writeInt(mAccessibilityViewId);
+        parcel.writeInt(mAccessibilityWindowId);
+        parcel.writeInt(mParentAccessibilityViewId);
+
+        SparseIntArray childIds = mChildAccessibilityIds;
+        final int childIdsSize = childIds.size();
+        parcel.writeInt(childIdsSize);
+        for (int i = 0; i < childIdsSize; i++) {
+            parcel.writeInt(childIds.valueAt(i));
+        }
+
+        parcel.writeInt(mBounds.top);
+        parcel.writeInt(mBounds.bottom);
+        parcel.writeInt(mBounds.left);
+        parcel.writeInt(mBounds.right);
+
+        parcel.writeInt(mActions);
+
+        parcel.writeInt(mBooleanProperties);
+
+        TextUtils.writeToParcel(mPackageName, parcel, flags);
+        TextUtils.writeToParcel(mClassName, parcel, flags);
+        TextUtils.writeToParcel(mText, parcel, flags);
+        TextUtils.writeToParcel(mContentDescription, parcel, flags);
+
+        // Since instances of this class are fetched via synchronous i.e. blocking
+        // calls in IPCs and we always recycle as soon as the instance is marshaled.
+        recycle();
+    }
+
+    /**
+     * Creates a new instance from a {@link Parcel}.
+     *
+     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
+     */
+    private void initFromParcel(Parcel parcel) {
+        if (parcel.readInt() == 1) {
+            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+                    parcel.readStrongBinder());
+        }
+        mSealed = (parcel.readInt()  == 1);
+        mAccessibilityViewId = parcel.readInt();
+        mAccessibilityWindowId = parcel.readInt();
+        mParentAccessibilityViewId = parcel.readInt();
+
+        SparseIntArray childIds = mChildAccessibilityIds;
+        final int childrenSize = parcel.readInt();
+        for (int i = 0; i < childrenSize; i++) {
+            final int childId = parcel.readInt();
+            childIds.put(i, childId);
+        }
+
+        mBounds.top = parcel.readInt();
+        mBounds.bottom = parcel.readInt();
+        mBounds.left = parcel.readInt();
+        mBounds.right = parcel.readInt();
+
+        mActions = parcel.readInt();
+
+        mBooleanProperties = parcel.readInt();
+
+        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+    }
+
+    /**
+     * Clears the state of this instance.
+     */
+    private void clear() {
+        mSealed = false;
+        mConnection = null;
+        mAccessibilityViewId = View.NO_ID;
+        mParentAccessibilityViewId = View.NO_ID;
+        mChildAccessibilityIds.clear();
+        mBounds.set(0, 0, 0, 0);
+        mBooleanProperties = 0;
+        mPackageName = null;
+        mClassName = null;
+        mText = null;
+        mContentDescription = null;
+        mActions = 0;
+    }
+
+    /**
+     * Gets the human readable action symbolic name.
+     *
+     * @param action The action.
+     * @return The symbolic name.
+     */
+    private static String getActionSymbolicName(int action) {
+        SparseArray<String> actionSymbolicNames = sActionSymbolicNames;
+        if (actionSymbolicNames == null) {
+            actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>();
+            actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS");
+            actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS");
+            actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT");
+            actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT");
+        }
+        return actionSymbolicNames.get(action);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + mAccessibilityViewId;
+        result = prime * result + mAccessibilityWindowId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(super.toString());
+
+        if (DEBUG) {
+            builder.append("; accessibilityId: " + mAccessibilityViewId);
+            builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId);
+            SparseIntArray childIds = mChildAccessibilityIds;
+            builder.append("; childAccessibilityIds: [");
+            for (int i = 0, count = childIds.size(); i < count; i++) {
+                builder.append(childIds.valueAt(i));
+                if (i < count - 1) {
+                    builder.append(", ");
+                }
+           }
+           builder.append("]");
+        }
+
+        builder.append("; bounds: " + mBounds);
+
+        builder.append("; packageName: ").append(mPackageName);
+        builder.append("; className: ").append(mClassName);
+        builder.append("; text: ").append(mText);
+        builder.append("; contentDescription: ").append(mContentDescription);
+
+        builder.append("; checkable: ").append(isCheckable());
+        builder.append("; checked: ").append(isChecked());
+        builder.append("; focusable: ").append(isFocusable());
+        builder.append("; focused: ").append(isFocused());
+        builder.append("; selected: ").append(isSelected());
+        builder.append("; clickable: ").append(isClickable());
+        builder.append("; longClickable: ").append(isLongClickable());
+        builder.append("; enabled: ").append(isEnabled());
+        builder.append("; password: ").append(isPassword());
+
+        builder.append("; [");
+
+        for (int actionBits = mActions; actionBits != 0;) {
+            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
+            actionBits &= ~action;
+            builder.append(getActionSymbolicName(action));
+            if (actionBits != 0) {
+                builder.append(", ");
+            }
+        }
+
+        builder.append("]");
+
+        return builder.toString();
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
+            new Parcelable.Creator<AccessibilityNodeInfo>() {
+        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
+            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+            info.initFromParcel(parcel);
+            return info;
+        }
+
+        public AccessibilityNodeInfo[] newArray(int size) {
+            return new AccessibilityNodeInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 7819b17..4bf03a7 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -38,13 +38,14 @@
     private static final int PROPERTY_PASSWORD = 0x00000004;
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
 
+    // Housekeeping
     private static final int MAX_POOL_SIZE = 10;
     private static final Object sPoolLock = new Object();
     private static AccessibilityRecord sPool;
     private static int sPoolSize;
-
     private AccessibilityRecord mNext;
     private boolean mIsInPool;
+    private boolean mSealed;
 
     protected int mBooleanProperties;
     protected int mCurrentItemIndex;
@@ -68,6 +69,26 @@
     }
 
     /**
+     * Initialize this record from another one.
+     *
+     * @param record The to initialize from.
+     */
+    void init(AccessibilityRecord record) {
+        mSealed = record.isSealed();
+        mBooleanProperties = record.mBooleanProperties;
+        mCurrentItemIndex = record.mCurrentItemIndex;
+        mItemCount = record.mItemCount;
+        mFromIndex = record.mFromIndex;
+        mAddedCount = record.mAddedCount;
+        mRemovedCount = record.mRemovedCount;
+        mClassName = record.mClassName;
+        mContentDescription = record.mContentDescription;
+        mBeforeText = record.mBeforeText;
+        mParcelableData = record.mParcelableData;
+        mText.addAll(record.mText);
+    }
+
+    /**
      * Gets if the source is checked.
      *
      * @return True if the view is checked, false otherwise.
@@ -80,8 +101,11 @@
      * Sets if the source is checked.
      *
      * @param isChecked True if the view is checked, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setChecked(boolean isChecked) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_CHECKED, isChecked);
     }
 
@@ -98,8 +122,11 @@
      * Sets if the source is enabled.
      *
      * @param isEnabled True if the view is enabled, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEnabled(boolean isEnabled) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_ENABLED, isEnabled);
     }
 
@@ -116,21 +143,15 @@
      * Sets if the source is a password field.
      *
      * @param isPassword True if the view is a password field, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPassword(boolean isPassword) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_PASSWORD, isPassword);
     }
 
     /**
-     * Sets if the source is taking the entire screen.
-     *
-     * @param isFullScreen True if the source is full screen, false otherwise.
-     */
-    public void setFullScreen(boolean isFullScreen) {
-        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
-    }
-
-    /**
      * Gets if the source is taking the entire screen.
      *
      * @return True if the source is full screen, false otherwise.
@@ -140,6 +161,18 @@
     }
 
     /**
+     * Sets if the source is taking the entire screen.
+     *
+     * @param isFullScreen True if the source is full screen, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFullScreen(boolean isFullScreen) {
+        enforceNotSealed();
+        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
+    }
+
+    /**
      * Gets the number of items that can be visited.
      *
      * @return The number of items.
@@ -152,8 +185,11 @@
      * Sets the number of items that can be visited.
      *
      * @param itemCount The number of items.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setItemCount(int itemCount) {
+        enforceNotSealed();
         mItemCount = itemCount;
     }
 
@@ -170,8 +206,11 @@
      * Sets the index of the source in the list of items that can be visited.
      *
      * @param currentItemIndex The current item index.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setCurrentItemIndex(int currentItemIndex) {
+        enforceNotSealed();
         mCurrentItemIndex = currentItemIndex;
     }
 
@@ -188,8 +227,11 @@
      * Sets the index of the first character of the changed sequence.
      *
      * @param fromIndex The index of the first character.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFromIndex(int fromIndex) {
+        enforceNotSealed();
         mFromIndex = fromIndex;
     }
 
@@ -206,8 +248,11 @@
      * Sets the number of added characters.
      *
      * @param addedCount The number of added characters.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setAddedCount(int addedCount) {
+        enforceNotSealed();
         mAddedCount = addedCount;
     }
 
@@ -224,8 +269,11 @@
      * Sets the number of removed characters.
      *
      * @param removedCount The number of removed characters.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setRemovedCount(int removedCount) {
+        enforceNotSealed();
         mRemovedCount = removedCount;
     }
 
@@ -242,8 +290,11 @@
      * Sets the class name of the source.
      *
      * @param className The lass name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClassName(CharSequence className) {
+        enforceNotSealed();
         mClassName = className;
     }
 
@@ -270,8 +321,11 @@
      * Sets the text before a change.
      *
      * @param beforeText The text before the change.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBeforeText(CharSequence beforeText) {
+        enforceNotSealed();
         mBeforeText = beforeText;
     }
 
@@ -288,8 +342,11 @@
      * Sets the description of the source.
      *
      * @param contentDescription The description.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setContentDescription(CharSequence contentDescription) {
+        enforceNotSealed();
         mContentDescription = contentDescription;
     }
 
@@ -306,18 +363,71 @@
      * Sets the {@link Parcelable} data of the event.
      *
      * @param parcelableData The parcelable data.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setParcelableData(Parcelable parcelableData) {
+        enforceNotSealed();
         mParcelableData = parcelableData;
     }
 
     /**
+     * Sets if this instance is sealed.
+     *
+     * @param sealed Whether is sealed.
+     *
+     * @hide
+     */
+    public void setSealed(boolean sealed) {
+        mSealed = sealed;
+    }
+
+    /**
+     * Gets if this instance is sealed.
+     *
+     * @return Whether is sealed.
+     *
+     * @hide
+     */
+    public boolean isSealed() {
+        return mSealed;
+    }
+
+    /**
+     * Enforces that this instance is sealed.
+     *
+     * @throws IllegalStateException If this instance is not sealed.
+     *
+     * @hide
+     */
+    protected void enforceSealed() {
+        if (!isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on a not sealed instance.");
+        }
+    }
+
+    /**
+     * Enforces that this instance is not sealed.
+     *
+     * @throws IllegalStateException If this instance is sealed.
+     *
+     * @hide
+     */
+    protected void enforceNotSealed() {
+        if (isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on an sealed instance.");
+        }
+    }
+
+    /**
      * Gets the value of a boolean property.
      *
      * @param property The property.
      * @return The value.
      */
-    public boolean getBooleanProperty(int property) {
+    private boolean getBooleanProperty(int property) {
         return (mBooleanProperties & property) == property;
     }
 
@@ -337,6 +447,19 @@
 
     /**
      * Returns a cached instance if such is available or a new one is
+     * instantiated. The instance is initialized with data from the
+     * given record.
+     *
+     * @return An instance.
+     */
+    public static AccessibilityRecord obtain(AccessibilityRecord record) {
+       AccessibilityRecord clone = AccessibilityRecord.obtain();
+       clone.init(record);
+       return clone;
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
      * instantiated.
      *
      * @return An instance.
@@ -379,8 +502,11 @@
 
     /**
      * Clears the state of this instance.
+     *
+     * @hide
      */
     protected void clear() {
+        mSealed = false;
         mBooleanProperties = 0;
         mCurrentItemIndex = INVALID_POSITION;
         mItemCount = 0;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
new file mode 100644
index 0000000..77dcd07
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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 android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+/**
+ * Interface for interaction between the AccessibilityManagerService
+ * and the ViewRoot in a given window.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnection {
+
+    void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void findAccessibilityNodeInfoByViewId(int id, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void performAccessibilityAction(int accessibilityId, int action, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
new file mode 100644
index 0000000..9c5e8dc
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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 android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import java.util.List;
+
+/**
+ * Callback for specifying the result for an asynchronous request made
+ * via calling a method on IAccessibilityInteractionConnectionCallback.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnectionCallback {
+
+    void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
+
+    void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
+        int interactionId);
+
+    void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 6b2aae2..b14f02a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -18,8 +18,13 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.IWindow;
 
 /**
  * Interface implemented by the AccessibilityManagerService called by
@@ -38,4 +43,11 @@
     List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
 
     void interrupt();
+
+    int addAccessibilityInteractionConnection(IWindow windowToken,
+        in IAccessibilityInteractionConnection connection);
+
+    void removeAccessibilityInteractionConnection(IWindow windowToken);
+
+    IAccessibilityServiceConnection registerEventListener(IEventListener client);
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9e61ecf..4aad022 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -6461,7 +6461,7 @@
                     if (hscroll != 0 || vscroll != 0) {
                         final int vdelta = (int) (vscroll * getVerticalScrollFactor());
                         final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
-                        if (pinScrollBy(hdelta, vdelta, true, 0)) {
+                        if (pinScrollBy(hdelta, vdelta, false, 0)) {
                             return true;
                         }
                     }
@@ -6503,6 +6503,8 @@
     // arrow key events, not trackball events, from one child to the next
     private boolean mMapTrackballToArrowKeys = true;
 
+    private DrawData mDelaySetPicture;
+
     public void setMapTrackballToArrowKeys(boolean setMap) {
         checkThread();
         mMapTrackballToArrowKeys = setMap;
@@ -7912,7 +7914,8 @@
                 // after WebView's destroy() is called, skip handling messages.
                 return;
             }
-            if (mBlockWebkitViewMessages) {
+            if (mBlockWebkitViewMessages
+                    && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
                 // Blocking messages from webkit
                 return;
             }
@@ -8060,6 +8063,10 @@
                             BrowserFrame.DRAWABLEDIR, mContext);
                     AssetManager am = mContext.getAssets();
                     nativeCreate(msg.arg1, drawableDir, am);
+                    if (mDelaySetPicture != null) {
+                        setNewPicture(mDelaySetPicture);
+                        mDelaySetPicture = null;
+                    }
                     break;
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
@@ -8371,6 +8378,15 @@
     }
 
     void setNewPicture(final WebViewCore.DrawData draw) {
+        if (mNativeClass == 0) {
+            if (mDelaySetPicture != null) {
+                throw new IllegalStateException(
+                        "Tried to setNewPicture with a delay picture already set! (memory leak)");
+            }
+            // Not initialized yet, delay set
+            mDelaySetPicture = draw;
+            return;
+        }
         WebViewCore.ViewState viewState = draw.mViewState;
         boolean isPictureAfterFirstLayout = viewState != null;
         setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 4ee16e7..6b498fe 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -115,7 +115,7 @@
     }
 
     /**
-     * Describes how the foreground is positioned. Defaults to BEFORE and TOP.
+     * Describes how the foreground is positioned. Defaults to START and TOP.
      *
      * @param foregroundGravity See {@link android.view.Gravity}
      *
@@ -125,7 +125,7 @@
     public void setForegroundGravity(int foregroundGravity) {
         if (mForegroundGravity != foregroundGravity) {
             if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                foregroundGravity |= Gravity.BEFORE;
+                foregroundGravity |= Gravity.START;
             }
 
             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f843574..0cdbc5b 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -116,10 +116,10 @@
                 equals = Gravity.LEFT, name = "LEFT"),
             @ViewDebug.FlagToString(mask = Gravity.RIGHT,
                 equals = Gravity.RIGHT, name = "RIGHT"),
-            @ViewDebug.FlagToString(mask = Gravity.BEFORE,
-                equals = Gravity.BEFORE, name = "BEFORE"),
-            @ViewDebug.FlagToString(mask = Gravity.AFTER,
-                equals = Gravity.AFTER, name = "AFTER"),
+            @ViewDebug.FlagToString(mask = Gravity.START,
+                equals = Gravity.START, name = "START"),
+            @ViewDebug.FlagToString(mask = Gravity.END,
+                equals = Gravity.END, name = "END"),
             @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
                 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
             @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
@@ -132,10 +132,10 @@
                 equals = Gravity.CENTER, name = "CENTER"),
             @ViewDebug.FlagToString(mask = Gravity.FILL,
                 equals = Gravity.FILL, name = "FILL"),
-            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION,
-                equals = Gravity.RELATIVE_HORIZONTAL_DIRECTION, name = "RELATIVE")
+            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
+                equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
         })
-    private int mGravity = Gravity.BEFORE | Gravity.TOP;
+    private int mGravity = Gravity.START | Gravity.TOP;
 
     @ViewDebug.ExportedProperty(category = "measurement")
     private int mTotalLength;
@@ -1649,7 +1649,7 @@
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.BEFORE;
+                gravity |= Gravity.START;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1742,8 +1742,8 @@
             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
-            @ViewDebug.IntToString(from = Gravity.BEFORE,            to = "BEFORE"),
-            @ViewDebug.IntToString(from = Gravity.AFTER,             to = "AFTER"),
+            @ViewDebug.IntToString(from = Gravity.START,            to = "START"),
+            @ViewDebug.IntToString(from = Gravity.END,             to = "END"),
             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index acd8539..a4771d5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,6 +16,15 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -32,14 +41,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.SortedSet;
-import java.util.TreeSet;
 
 import static android.util.Log.d;
 
@@ -222,7 +223,7 @@
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.BEFORE;
+                gravity |= Gravity.START;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1440,6 +1441,7 @@
             );
 
             private Node mNext;
+            private boolean mIsPooled;
 
             public void setNextPoolable(Node element) {
                 mNext = element;
@@ -1449,6 +1451,14 @@
                 return mNext;
             }
 
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
+
             static Node acquire(View view) {
                 final Node node = sPool.acquire();
                 node.view = view;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35e78fb..eba9d37 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -113,6 +113,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
@@ -2094,7 +2095,7 @@
      */
     public void setGravity(int gravity) {
         if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-            gravity |= Gravity.BEFORE;
+            gravity |= Gravity.START;
         }
         if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
             gravity |= Gravity.TOP;
@@ -7647,7 +7648,18 @@
     protected int computeVerticalScrollExtent() {
         return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
     }
-    
+
+    @Override
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+        CharSequence thisText = getText();
+        if (TextUtils.isEmpty(thisText)) {
+            return;
+        }
+        if (thisText.toString().toLowerCase().contains(text)) {
+            outViews.add(this);
+        }
+    }
+
     public enum BufferType {
         NORMAL, SPANNABLE, EDITABLE,
     }
@@ -7899,6 +7911,17 @@
         event.setPassword(isPassword);
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        final boolean isPassword = hasPasswordTransformationMethod();
+        if (!isPassword) {
+            info.setText(getText());
+        }
+        info.setPassword(isPassword);
+    }
+
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
         AccessibilityEvent event =
@@ -10005,7 +10028,7 @@
 
     private boolean                 mSelectAllOnFocus = false;
 
-    private int                     mGravity = Gravity.TOP | Gravity.BEFORE;
+    private int                     mGravity = Gravity.TOP | Gravity.START;
     private boolean                 mHorizontallyScrolling;
 
     private int                     mAutoLinkMask;
diff --git a/core/java/com/android/internal/util/Objects.java b/core/java/com/android/internal/util/Objects.java
index 598a079..2664182 100644
--- a/core/java/com/android/internal/util/Objects.java
+++ b/core/java/com/android/internal/util/Objects.java
@@ -16,34 +16,47 @@
 
 package com.android.internal.util;
 
+import java.util.Arrays;
+
 /**
  * Object utility methods.
  */
 public class Objects {
 
     /**
-     * Ensures the given object isn't {@code null}.
+     * Determines whether two possibly-null objects are equal. Returns:
      *
-     * @return the given object
-     * @throws NullPointerException if the object is null
+     * <ul>
+     * <li>{@code true} if {@code a} and {@code b} are both null.
+     * <li>{@code true} if {@code a} and {@code b} are both non-null and they are
+     *     equal according to {@link Object#equals(Object)}.
+     * <li>{@code false} in all other situations.
+     * </ul>
+     *
+     * <p>This assumes that any non-null objects passed to this function conform
+     * to the {@code equals()} contract.
      */
-    public static <T> T nonNull(T t) {
-        if (t == null) {
-            throw new NullPointerException();
-        }
-        return t;
+    public static boolean equal(Object a, Object b) {
+        return a == b || (a != null && a.equals(b));
     }
 
     /**
-     * Ensures the given object isn't {@code null}.
+     * Generates a hash code for multiple values. The hash code is generated by
+     * calling {@link Arrays#hashCode(Object[])}.
      *
-     * @return the given object
-     * @throws NullPointerException if the object is null
+     * <p>This is useful for implementing {@link Object#hashCode()}. For example,
+     * in an object that has three properties, {@code x}, {@code y}, and
+     * {@code z}, one could write:
+     * <pre>
+     * public int hashCode() {
+     *   return Objects.hashCode(getX(), getY(), getZ());
+     * }</pre>
+     *
+     * <b>Warning</b>: When a single object is supplied, the returned hash code
+     * does not equal the hash code of that object.
      */
-    public static <T> T nonNull(T t, String message) {
-        if (t == null) {
-            throw new NullPointerException(message);
-        }
-        return t;
+    public static int hashCode(Object... objects) {
+        return Arrays.hashCode(objects);
     }
+
 }
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
new file mode 100644
index 0000000..a53a9c0
--- /dev/null
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ */
+public class Preconditions {
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static <T> T checkNotNull(T reference) {
+        if (reference == null) {
+            throw new NullPointerException();
+        }
+        return reference;
+    }
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static <T> T checkNotNull(T reference, Object errorMessage) {
+        if (reference == null) {
+            throw new NullPointerException(String.valueOf(errorMessage));
+        }
+        return reference;
+    }
+
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c41b2cb..b9948fe 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.view;
 
-import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -26,8 +24,6 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 
 public class BaseIWindow extends IWindow.Stub {
     private IWindowSession mSession;
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 4a09232..942aa8a1 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -755,32 +755,46 @@
             jfloat x, jfloat y, int flags, SkPaint* paint) {
 
         jint count = end - start;
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, end, flags);
+        sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+        value = gTextLayoutCache.getValue(paint, textArray, start, count, end, flags);
         if (value == NULL) {
             LOGE("Cannot get TextLayoutCache value");
             return ;
         }
+#else
+        value = new TextLayoutCacheValue();
+        value->computeValues(paint, textArray, start, count, end, flags);
+#endif
+
 #if DEBUG_GLYPHS
         logGlyphs(value);
 #endif
-        doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                x, y, flags, paint);
+
+    doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+            x, y, flags, paint);
     }
 
     static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
             int start, int count, int contextCount,
             jfloat x, jfloat y, int flags, SkPaint* paint) {
 
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, contextCount, flags);
+        sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+        value = gTextLayoutCache.getValue(paint, textArray, start, count, contextCount, flags);
         if (value == NULL) {
             LOGE("Cannot get TextLayoutCache value");
             return ;
         }
+#else
+        value = new TextLayoutCacheValue();
+        value->computeValues(paint, textArray, start, count, contextCount, flags);
+#endif
+
 #if DEBUG_GLYPHS
         logGlyphs(value);
 #endif
+
         doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
                 x, y, flags, paint);
     }
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 46e6c2b..7e89a37 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -254,21 +254,21 @@
 void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
                                     jint count, jint contextCount, jint dirFlags,
                                     jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+    sp<TextLayoutCacheValue> value;
 #if USE_TEXT_LAYOUT_CACHE
     // Return advances from the cache. Compute them if needed
-    sp<TextLayoutCacheValue> layout = gTextLayoutCache.getValue(
+    value = gTextLayoutCache.getValue(
             paint, chars, start, count, contextCount, dirFlags);
-    if (layout != NULL) {
-        if (resultAdvances != NULL) {
-            memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat));
-        }
-        resultTotalAdvance = layout->getTotalAdvance();
-    }
 #else
-    // Compute advances and return them
-    TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount,
-            dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL );
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, chars, start, count, contextCount, dirFlags);
 #endif
+    if (value != NULL) {
+        if (resultAdvances != NULL) {
+            memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
+        }
+        resultTotalAdvance = value->getTotalAdvance();
+    }
 }
 
 void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index ecfe5ff..b36fa3e 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -73,6 +73,8 @@
 static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
         jstring domainObj, jstring linkdomain,
         jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+    int ret;
+
     // Extract the various strings, allowing for null object pointers
     const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
     const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index e5c28489..0960b25 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -38,18 +38,7 @@
 #include <grp.h>
 #include <pwd.h>
 #include <signal.h>
-
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
+#include <unistd.h>
 
 #define POLICY_DEBUG 0
 #define GUARD_THREAD_PRIORITY 0
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 31988f7..57a97bd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -438,12 +438,17 @@
 static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
         jfloat x, jfloat y, int flags, SkPaint* paint) {
 #if RTL_USE_HARFBUZZ
-    sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-            paint, text, 0, count, count, flags);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    value = gTextLayoutCache.getValue(paint, text, 0, count, count, flags);
     if (value == NULL) {
         LOGE("Cannot get TextLayoutCache value");
         return ;
     }
+#else
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, text, 0, count, count, flags);
+#endif
 #if DEBUG_GLYPHS
     logGlyphs(value);
 #endif
@@ -466,12 +471,17 @@
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int flags, SkPaint* paint) {
 #if RTL_USE_HARFBUZZ
-    sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-            paint, text, start, count, contextCount, flags);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    value = gTextLayoutCache.getValue(paint, text, start, count, contextCount, flags);
     if (value == NULL) {
         LOGE("Cannot get TextLayoutCache value");
         return ;
     }
+#else
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, text, start, count, contextCount, flags);
+#endif
 #if DEBUG_GLYPHS
     logGlyphs(value);
 #endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 110268e..3efb83c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -677,6 +677,14 @@
         android:label="@string/permlab_dump"
         android:description="@string/permdesc_dump" />
 
+    <!-- Allows an application to retrieve the content of the active window
+         An active window is the window that has fired an accessibility event. -->
+    <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_retrieve_window_content"
+        android:description="@string/permdesc_retrieve_window_content" />
+
     <!-- Allows an application to open windows using the type
          {@link android.view.WindowManager.LayoutParams#TYPE_SYSTEM_ALERT},
          shown on top of all other applications.  Very few applications
diff --git a/core/res/res/drawable-hdpi/pointer_arrow.png b/core/res/res/drawable-hdpi/pointer_arrow.png
new file mode 100644
index 0000000..fbd187c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_arrow_icon.xml b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
new file mode 100644
index 0000000..2f5676f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_arrow"
+    android:hotSpotX="6"
+    android:hotSpotY="6" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor.png b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
new file mode 100644
index 0000000..d7aca36
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
new file mode 100644
index 0000000..2222b8e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_anchor"
+    android:hotSpotX="33"
+    android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover.png b/core/res/res/drawable-hdpi/pointer_spot_hover.png
new file mode 100644
index 0000000..5041aa3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
new file mode 100644
index 0000000..dc62a69
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_hover"
+    android:hotSpotX="33"
+    android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch.png b/core/res/res/drawable-hdpi/pointer_spot_touch.png
new file mode 100644
index 0000000..64a42a1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
new file mode 100644
index 0000000..4bffee6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_touch"
+    android:hotSpotX="24"
+    android:hotSpotY="24" />
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
index b0ca3e8..7303003 100644
--- a/core/res/res/layout/volume_adjust.xml
+++ b/core/res/res/layout/volume_adjust.xml
@@ -15,22 +15,21 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="left">
-
+    android:layout_width="480dp"
+    android:layout_height="wrap_content">
     <LinearLayout
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="80dip"
+        android:layout_marginTop="80dp"
         android:background="@android:drawable/dialog_full_holo_dark"
         android:orientation="horizontal"
         >
 
         <LinearLayout
             android:id="@+id/slider_group"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:orientation="vertical"
             >
             <!-- Sliders go here -->
@@ -56,9 +55,6 @@
             android:background="?attr/selectableItemBackground"
             android:src="@drawable/ic_sysbar_quicksettings"
             />
-        
+
     </LinearLayout>
-
-</FrameLayout>
-
-
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
index e841d87..beb511d 100644
--- a/core/res/res/layout/volume_adjust_item.xml
+++ b/core/res/res/layout/volume_adjust_item.xml
@@ -15,7 +15,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="80dip"
     android:orientation="horizontal"
     android:layout_marginTop="8dip"
@@ -34,8 +34,9 @@
     <SeekBar
         style="?android:attr/seekBarStyle"
         android:id="@+id/seekbar"
-        android:layout_width="300dip"
+        android:layout_width="300dp"
         android:layout_height="wrap_content"
+        android:layout_weight="1"
         android:padding="16dip"
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 38b62d4..cab43ac 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -412,6 +412,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Laat \'n program toe om die skerm te enige tyd te draai. Behoort vir gewone programme nooit nodig te wees nie."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Laat program toe om te versoek dat die voorsiende materiaal aan alle aanhoudende prosesse gestuur word."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9c22f0e..b97bc7a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"ትግበራው በማንኛውም ሰዓት የማያንማሽከርከሪያለመለወጥ ይፈቅዳል።ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"ትግበራዎች ለሁሉም ተከታታይ ሂደቶች ልከው የሚያቀርቧቸው ሲግናሎችን ለመጠየቅ ይፈቅዳል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c9454d3..8d5ecb8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"للسماح للمالك بإرسال الأهداف إلى أحد مشرفي الجهاز. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"تغيير اتجاه الشاشة"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"للسماح لتطبيق ما بتغيير تدوير الشاشة في أي وقت. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"إرسال إشارات Linux للتطبيقات"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"للسماح للتطبيق بطلب إرسال الإشارة المزوّدة لجميع العمليات المستمرة."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"تشغيل التطبيق دائمًا"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 70eb79a..b43b50f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Разрешава на притежателя да изпраща намерения до администратор на устройството. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"промяна на ориентацията на екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Разрешава на приложението да променя ориентацията на екрана по всяко време. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"изпращане на сигнали от Linux до приложенията"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Разрешава на приложението да подаде заявка предоставеният сигнал да се изпрати до всички постоянни процеси."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"задаване на постоянно изпълнение на приложението"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 45201c1..2947b93 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet al titular enviar intencions a un administrador del sistema. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"canviar l\'orientació de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet a una aplicació canviar el gir de la pantalla en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar senyals Linux a les aplicacions"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet a l\'aplicació sol·licitar que el senyal subministrat s\'enviï a tots els processos persistents."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"fer que l\'aplicació s\'executi sempre"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f88e381..92deaca 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteli oprávnění odesílat informace správci zařízení. Běžné aplikace by toto oprávnění nikdy neměly požadovat."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"změna orientace obrazovky"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikaci kdykoli změnit orientaci obrazovky. Běžné aplikace by toto nastavení nikdy neměly využívat."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odeslání signálů Linux aplikacím"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikaci vyžádat zaslání poskytnutého signálu všem trvalým procesům."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spuštění aplikace"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index c6946d4..a3149a2 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillader brugeren at sende hensigter til en enhedsadministrator. Bør aldrig være nødvendigt for almindelige programmer."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"skift skærmretning"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillader, at et program ændrer rotationen af skærmen når som helst. Bør aldrig være nødvendigt til normale programmer."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux-signaler til programmer"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillader, at programmet kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"lad altid programmet køre"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ffef226..b6b6be5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ermöglicht dem Halter, Intents an einen Geräteadministrator zu senden. Sollte nie für normale Anwendungen benötigt werden."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"Bildschirmausrichtung ändern"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ermöglicht der Anwendung, die Bildschirmdrehung jederzeit zu ändern. Sollte nicht für normale Anwendungen benötigt werden."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-Signale an Anwendungen senden"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ermöglicht der Anwendung, das Senden des gelieferten Signals an alle anhaltenden Prozesse zu fordern"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"Anwendungen permanent ausführen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index bf1d432f..a5e13f1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Επιτρέπει στον κάτοχο την αποστολή στόχων σε έναν διαχειριστή συσκευής. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"αλλαγή προσανατολισμού οθόνης"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Επιτρέπει σε μια εφαρμογή την αλλαγή της περιστροφής της οθόνης οποιαδήποτε στιγμή. Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"αποστολή σημάτων Linux σε εφαρμογές"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Επιτρέπει σε μια εφαρμογή την αποστολή αιτήματος για την αποστολή του παρεχόμενου σήματος σε όλες τις υπάρχουσες διαδικασίες."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"η εφαρμογή να εκτελείται συνεχώς"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index b0e77ba..d5ef98b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Allows the holder to send intents to a device administrator. Should never be needed for normal applications."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"change screen orientation"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Allows an application to change the rotation of the screen at any time. Should never be needed for normal applications."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux signals to applications"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Allows application to request that the supplied signal be sent to all persistent processes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"make application always run"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index fb97c4a..3cb61e4 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que el propietario envíe sus intentos a un administrador de dispositivos. No se necesita para las aplicaciones normales."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar la orientación de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Admite una aplicación que cambia la rotación de la pantalla en cualquier momento. Se debe evitar utilizarlo en aplicaciones normales."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales de Linux a las aplicaciones"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Admite que la aplicación solicite el envío de la señal suministrada a todos los procesos continuos."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que siempre se ejecute la aplicación"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a1e3a54..9db80ca 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite enviar intentos a un administrador de dispositivos. Este permiso nunca debería ser necesario para las aplicaciones normales."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar orientación de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesario nunca para las aplicaciones normales."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales Linux a aplicaciones"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que la aplicación solicite que la señal suministrada se envíe a todos los procesos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que la aplicación se ejecute siempre"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 00d173d..3fcb21d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"به نگهدارنده اجازه می دهد مفاد را به یک سرپرست دستگاه ارسال کند. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"تغییر جهت صفحه"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"به یک برنامه کاربردی اجازه می دهد چرخش صفحه را در هر زمانی تغییر دهد. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ارسال سیگنال های Linux برای برنامه ها"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"به برنامه کاربردی اجازه می دهد درخواست کند که سیگنال ارائه شده را به تمام فرایندهای ثابت ارسال کند."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"برنامه همیشه اجرا شود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2d3bf48..70e5ff7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Antaa sovelluksen lähettää kyselyitä laitteen järjestelmänvalvojalle. Ei tavallisten sovelluksien käyttöön."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"muuta näytön suuntaa"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Antaa sovelluksen vaihtaa näytön suuntaa milloin tahansa. Ei tavallisten sovelluksien käyttöön."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"lähettää sovelluksiin Linux-signaaleja"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Antaa sovelluksen pyytää, että signaali lähetetään kaikille kiinteille prosesseille."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"aseta sovellus olemaan jatkuvasti käynnissä"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3f3eb93..c618edf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet à l\'application d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standard ne devraient jamais avoir recours à cette fonctionnalité."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"Changement d\'orientation de l\'écran"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Envoi de signaux Linux aux applications"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet à une application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"Exécution de l\'application en continu"</string>
@@ -449,7 +453,7 @@
     <string name="permlab_nfc" msgid="4423351274757876953">"contrôler la communication en champ proche"</string>
     <string name="permdesc_nfc" msgid="9171401851954407226">"Permet à une application de communiquer avec des tags, cartes et lecteurs prenant en charge la communication en champ proche (NFC)."</string>
     <string name="permlab_vpn" msgid="8345800584532175312">"intercepter et modifier l\'ensemble du trafic réseau"</string>
-    <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et d\'inspecter l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
+    <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et de contrôler l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
     <string name="permlab_disableKeyguard" msgid="4977406164311535092">"Désactivation du verrouillage des touches"</string>
     <string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet à une application de désactiver le verrouillage des touches et toute sécurité par mot de passe. Exemple : Votre téléphone désactive le verrouillage du clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Lecture des paramètres de synchronisation"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 773a8f3..f17a272 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Nositelju omogućuje slanje namjera administratoru uređaja. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"promjena orijentacije zaslona"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Aplikaciji omogućuje promjenu rotacije zaslona u svakom trenutku. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"slanje Linux signala u aplikaciju"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Aplikacijama omogućuje traženje zahtjeva da se dobiveni signal pošalje na sve trajne postupke."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"uvijek pokrenuta aplikacija"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 09dd648..0c2aef3 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Lehetővé teszi a használó számára, hogy célokat küldjön egy eszközkezelőnek. A normál alkalmazásoknak erre soha nincs szüksége."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"képernyő irányának módosítása"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lehetővé teszi egy alkalmazás számára a képernyő elforgatásának módosítását. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux jelek küldése alkalmazásoknak"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lehetővé teszi az alkalmazás számára, hogy a megadott jelet elküldje az összes állandó folyamatnak."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"alkalmazások folyamatos futtatása"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b2f3af8..00e2dd4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Mengizinkan pemegang mengirimkan tujuan kepada administrator perangkat. Tidak diperlukan untuk aplikasi normal."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ubah orientasi layar"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Mengizinkan aplikasi mengubah rotasi layar kapan saja. Tidak diperlukan untuk aplikasi normal."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"kirim sinyal Linux ke aplikasi"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Mengizinkan aplikasi meminta sinyal yang diberikan untuk dikirimkan ke semua proses yang ada."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"jadikan aplikasi selalu berjalan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 19d317e..0d78861 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Consente l\'invio di intent a un amministratore del dispositivo. L\'autorizzazione non deve mai essere necessaria per le normali applicazioni."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"modifica orientamento dello schermo"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"invio segnali Linuz alle applicazioni"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"esecuzione permanente delle applicazioni"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4aba678..6b50982 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"מאפשר למשתמש לשלוח כוונות למנהל התקן. לא אמור להידרש לעולם ביישומים רגילים."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"שנה את כיוון המסך"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"מאפשר ליישום לשנות את סיבוב המסך בכל עת. לא אמור להידרש לעולם ביישומים רגילים."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"שלח אותות Linux ליישומים"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"מאפשר ליישום לבקש שהאות המסופק יישלח לכל התהליכים המתמשכים."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"הגדר את היישום לפעול תמיד"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e078274..bdad7b6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"デバイス管理者へのintentの送信を所有者に許可します。通常のアプリケーションでは不要です。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"画面の向きの変更"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"いつでも画面の回転を変更することをアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linuxのシグナルをアプリケーションに送信"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"受信した電波を継続プロセスに送信することをアプリケーションに許可します。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"アプリケーションを常に実行する"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4d1778e..5a3584a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"애플리케이션이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"애플리케이션에 Linux 시그널 보내기"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"애플리케이션이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"애플리케이션이 항상 실행되도록 설정"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5b7ef8c..eab92c6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Leidžia savininkui siųsti tikslus įrenginio administratoriui. Neturėtų reikėti įprastose programose."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"keisti ekrano padėtį"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Leidžia programai bet kuriuo metu keisti ekrano sukimą. Neturėtų reikėti įprastoms programoms."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"programoms siųsti „Linux“ signalus"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Leidžia programai pateikti užklausą, kad teikiamas signalas būtų siunčiamas visiems nuolatiniams procesams."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"nustatyti, kad programa būtų visada paleista"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2aa49ca..5c5525c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ļauj īpašniekam sūtīt nolūkus ierīces administratoram. Nekad nav nepieciešams parastajām lietojumprogrammām."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"mainīt ekrāna orientāciju"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ļauj lietojumprogrammai jebkurā brīdī mainīt ekrāna pagriešanas iestatījumu. Parastajām lietojumprogrammām nekad nav nepieciešama."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sūtīt Linux signālus uz lietojumprogrammām"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ļauj lietojumprogrammai pieprasīt, lai piegādātais signāls tiktu sūtīts uz visiem pastāvīgajiem procesiem."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"likt lietojumprogrammai vienmēr darboties"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e82fc10..6b0e18e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -297,6 +297,10 @@
     <skip />
     <string name="permlab_setOrientation" msgid="3365947717163866844">"tukar orientasi skrin"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Membolehkan aplikasi untuk menukar putaran skrin pada bila-bila masa. Tidak seharusnya diperlukan untuk aplikasi biasa."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"hantar isyarat Linux kepada aplikasi"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Membenarkan aplikasi meminta isyarat yang dibekalkan dihantar kepada semua proses gigih."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"buatkan aplikasi sentiasa berjalan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 02a414c..2413685 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillater innehaveren å sende hensikter til enhetsadministrator. Bør aldri være nødvendig for normale programmer."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"snu skjermen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sende Linux-signaler til applikasjoner"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lar applikasjonen spørre om at et gitt signal blir sendt til alle varige prosesser."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"forbli kjørende"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index ad214bf..dbb7e98 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Staat de houder toe intenties te verzenden naar een apparaatbeheerder. Nooit vereist voor normale toepassingen."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"schermstand wijzigen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Hiermee kan een app op elk gewenst moment de oriëntatie van het scherm wijzigen. Nooit vereist voor normale apps."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-signalen verzenden naar toepassingen"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Hiermee kan de app ervoor zorgen dat het geleverde signaal wordt verzonden naar alle persistente processen."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"app altijd laten uitvoeren"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ed877bb..36d3cdd 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Zezwala posiadaczowi na wysyłanie informacji o zamiarach do administratora urządzenia. Opcja nie powinna być nigdy potrzebna w przypadku zwykłych aplikacji."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"zmienianie orientacji ekranu"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pozwala aplikacji na zmianę orientacji ekranu w dowolnym momencie. Nigdy nie powinno być potrzeby stosowania w normalnych aplikacjach."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"wysyłanie sygnałów systemu Linux do aplikacji"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pozwala aplikacjom żądać, aby dostarczany sygnał był wysyłany do wszystkich trwających procesów."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"sprawianie, że aplikacja jest cały czas uruchomiona"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7ba9ad0..9cec4b6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite ao titular enviar intenções para um administrador do dispositivo. Nunca deverá ser necessário para aplicações normais."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"mudar orientação do ecrã"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite a uma aplicação mudar a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais Linux para aplicações"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite à aplicação pedir que o sinal fornecido seja enviado a todos os processos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"fazer com que a aplicação seja sempre executada"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 750a751..57737c9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que o detentor envie tentativas ao administrador de um dispositivo. Não é necessário para aplicativos normais."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"alterar orientação da tela"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Aplicativos normais não devem precisar disso em momento algum."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais de Linux para os aplicativos"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"executar sempre o aplicativo"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index ff2eecb..60f6049 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -275,6 +275,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permetta al possessur da trametter intenziuns a l\'administratur dal apparat periferic. Betg previs per applicaziuns normalas."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"midar l\'orientaziun dal visur"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permetta a l\'applicaziun da midar da tut temp la orientaziun dal visur. Betg previs per applicaziuns normalas."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trametter signals Linux a las applicaziuns"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permetta a l\'applicaziun da pretender ch\'il signal furnì vegnia tramess a tut ils process persistents."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"exequir permanentamain applicaziuns"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7fb4f73..1961903 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite proprietarului să trimită intenţii către un administrator al dispozitivului. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"modificare orientare ecran"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite unei aplicaţii să modifice rotaţia ecranului în orice moment. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trimitere semnale Linux către aplicaţii"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite aplicaţiei să solicite trimiterea semnalului furnizat către toate procesele persistente."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"rulare întotdeauna a aplicaţiei"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fe5815f..83dc30f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Позволяет владельцу отправлять целевые значения администратору устройства. Никогда не используется обычными приложениями."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"изменять ориентацию экрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Позволяет приложению изменять ориентацию экрана в любое время. Не требуется для обычных приложений."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"отправлять приложениям сигналы Linux"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Позволяет приложению направлять запрос на передачу предоставленного сигнала всем постоянным процессам."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"запускать постоянную работу приложения"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d7d9725..3439f59 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteľovi odosielať informácie správcovi zariadenia. Bežné aplikácie by toto oprávnenie nemali nikdy požadovať."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"zmena orientácie obrazovky"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikácii kedykoľvek zmeniť orientáciu obrazovky. Bežné aplikácie by toto nastavenie nemali vôbec využívať."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odoslanie signálov systému Linux aplikáciám"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikácii vyžiadať zaslanie poskytnutého signálu všetkým trvalým procesom."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spustenie aplikácie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cbb097b..cd4cd07 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Dovoljuje lastniku, da pošlje namere skrbniku naprave. Nikoli se ne uporablja za navadne programe."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"spreminjanje usmerjenosti zaslona"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Dovoljuje, da program kadar koli spremeni smer sukanja zaslona. Tega nikoli ni treba uporabiti za navadne programe."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"pošiljanje signalov Linuxa programom"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Dovoljuje, da program zahteva, da je posredovan signal poslan vsem trajnim procesom."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"neprekinjeno izvajanje programov"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 93faf2f..59329b4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Омогућава власнику да шаље своје намере администратору уређаја. Обичне апликације никада не би требало да је користе."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"промена положаја екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Омогућава да апликација у сваком тренутку промени ротацију екрана. Обичне апликације никада не би требало да је користе."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"слање Linux сигнала апликацијама"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Омогућава да апликација захтева да испоручени сигнал буде послат свим трајним процесима."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"омогућавање непрекидне активности апликације"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c3561d7..a3350e5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillåter att innehavaren skickar avsikter till en enhetsadministratör. Vanliga program behöver aldrig göra detta."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ändra bildskärmens rikting"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillåter att ett program när som helst ändrar skärmens rotering. Behövs inte för vanliga program."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"skicka Linux-signaler till appar"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillåter att programmet begär att den angivna signalen skickas till alla beständiga processer."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"se till att programmet alltid körs"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 453f74e..ff1907a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Huruhusu programu kubadilisha uzungukaji wa skrini wakati wowote. Haipaswi kuhitajika kwa programu za kawaida za kompyuta."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Huruhusu programu kuomba kwamba mawimbi yaliyotolewa yatumwe kwa michakato yote isiyokoma."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 54b33f4..be958dc 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"อนุญาตให้ผู้ถือส่งเนื้อหาไปยังโปรแกรมควบคุมอุปกรณ์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"เปลี่ยนการวางแนวหน้าจอ"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"อนุญาตให้แอปพลิเคชันเปลี่ยนการหมุนหน้าจอได้ตลอดเวลา ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ส่งสัญญาณ Linux ไปยังแอปพลิเคชัน"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"อนุญาตให้แอปพลิเคชันร้องขอให้ส่งสัญญาณแจ้งไปยังกระบวนการที่ยังทำงานอยู่ทั้งหมด"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"ทำให้แอปพลิเคชันทำงานตลอดเวลา"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9eeac34..b6942a4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Pinapayagan ang holder na magpadala ng mga intensyon sa administrator ng device. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"baguhin ang orientation ng screen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pinapayagan ang isang application na baguhin ang pag-rotate ng screen anumang oras. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"magpadala ng mga Linux signal sa mga application"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pinapayagan ang application na hilinging ipadala ang na-supply na signal sa lahat ng mga paulit-ulit na proseso."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"palaging patakbuhin ang mga application"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7f1d3c3..e97d834 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cihazın sahibinin cihaz yöneticisine amaç göndermesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ekran yönünü değiştir"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Uygulamaların ekran yönünü istedikleri zaman değiştirmesine izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"uygulamalara Linux sinyalleri gönder"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Uygulamaların, sağlanan sinyalin tüm kalıcı işlemlere gönderilmesini istemesine izin verir."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"uygulamayı her zaman çalıştır"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 00e6333..07b11f7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Дозволяє власнику надсилати цілі адміністратору пристрою. Ніколи не потрібний для звичайних програм."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"змінювати орієнтацію екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Дозволяє програмі будь-коли змінювати обертання екрана. Ніколи не потрібний для звичайних програм."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"надсилати сигнали Linux програмам"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Дозволяє програмі подавати запит щодо надсилання наданого сигналу всім постійним процесам."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"збер. програму завжди запущ."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 45c3ad8..cfb53ac 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cho phép chủ nhân gửi các ý định đến quản trị viên thiết bị. Không cần thiết cho các ứng dụng thông thường."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"thay đổi hướng màn hình"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Cho phép ứng dụng thay đổi việc xoay màn hình bất kỳ khi nào. Không cần thiết cho các ứng dụng thông thường."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"gửi tín hiệu Linux đến ứng dụng"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Cho phép ứng dụng yêu cầu tín hiệu đã cung cấp được gửi đến tất cả các quá trình liên tục."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"đặt ứng dụng luôn chạy"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f5ee751..b45b346 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允许持有对象将意向发送到设备管理器。普通的应用程序一律无需此权限。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"向应用程序发送 Linux 信号"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允许应用程序请求将所提供的信号发送给所有持久进程。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"让应用程序始终运行"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1779baf..399a5b0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允許應用程式將調用請求 (intent) 傳送至裝置管理員;一般應用程式不需使用此選項。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方向"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"允許應用程式可隨時變更螢幕旋轉方向。一般應用不應使用這項功能。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"傳送 Linux 訊號到應用程式"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允許應用程式要求將支援的訊號傳送到所有持續的程序。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"設定應用程式持續執行"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ad143a7..6f147e7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ivumela uhlelo lokusebenza ukushintsha ukujikeleleza kwesikrini nganoma isiphi isikhathi. Ayisoze yadingeka izinhlelo zokusebenza ezivamile."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ivumela uhlelo lokusebenza ukucela ukuthi isiginali enikeziwe ithunyelwe kuzo zonke izinqubo eziphikelelayo"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c55627..4a7c690 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1191,9 +1191,9 @@
              edge, a right gravity will clip the left edge, and neither will clip both edges. -->
         <flag name="clip_horizontal" value="0x08" />
         <!-- Push object to the beginning of its container, not changing its size. -->
-        <flag name="before" value="0x00800003" />
+        <flag name="start" value="0x00800003" />
         <!-- Push object to the end of its container, not changing its size. -->
-        <flag name="after" value="0x00800005" />
+        <flag name="end" value="0x00800005" />
     </attr>
 
     <!-- Standard orientation constant. -->
@@ -1932,11 +1932,11 @@
         </attr>
 
         <!-- Defines the direction of layout drawing. This typically is associated with writing
-             direction of the language script used. The possible values are Left-to-Right,
-             Right-to-Left, Locale and Inherit from parent view. If there is nothing to inherit,
-             Locale is used. Locale fallsback to 'en-US'. Left-to-Right is the direction used in
-             'en-US'. The default for this attribute is 'inherit'. -->
-        <attr name="horizontalDirection">
+             direction of the language script used. The possible values are "ltr" for Left-to-Right,
+             "rtl" for Right-to-Left, "locale" and "inherit" from parent view. If there is nothing
+             to inherit, "locale" is used. "locale" falls back to "en-US". "ltr" is the direction
+             used in "en-US". The default for this attribute is "inherit". -->
+        <attr name="layoutDirection">
             <!-- Left-to-Right -->
             <enum name="ltr" value="0" />
             <!-- Right-to-Left -->
@@ -5156,6 +5156,8 @@
         <!-- number of megabytes of storage MTP should reserve for free storage
              (used for emulated storage that is shared with system's data partition) -->
         <attr name="mtpReserve" format="integer" />
+        <!-- true if the storage can be shared via USB mass storage -->
+        <attr name="allowMassStorage" format="boolean" />
     </declare-styleable>
 
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc14a2a..f9fa51c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -156,6 +156,7 @@
 
     <!-- Regex array of allowable upstream ifaces for tethering - for example if you want
          tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
+    <!-- Interfaces will be prioritized according to the order listed -->
     <string-array translatable="false" name="config_tether_upstream_regexs">
     </string-array>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 81f888d..55312e3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1657,6 +1657,9 @@
   <public type="attr" name="compatibleWidthLimitDp" />
   <public type="attr" name="largestWidthLimitDp" />
 
+  <public type="style" name="Theme.Holo.Light.NoActionBar" />
+  <public type="style" name="Theme.Holo.Light.NoActionBar.Fullscreen" />
+
 <!-- ===============================================================
      Resources added in version 13 of the platform (Ice Cream Sandwich)
      =============================================================== -->
@@ -1666,7 +1669,6 @@
   <public type="attr" name="state_drag_hovered" />
   <public type="attr" name="stopWithTask" />
 
-  <public type="style" name="Theme.Holo.Light.NoActionBar" />
   <public type="style" name="TextAppearance.SuggestionHighlight" />
   <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
   <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
@@ -1675,7 +1677,8 @@
   <public type="attr" name="textEditSuggestionsBottomWindowLayout" />
   <public type="attr" name="textEditSuggestionsTopWindowLayout" />
   <public type="attr" name="textEditSuggestionItemLayout" />
-  <public type="attr" name="horizontalDirection" />
+
+  <public type="attr" name="layoutDirection" />
 
   <public type="attr" name="fullBackupAgent" />
   <public type="attr" name="suggestionsEnabled" />
@@ -1703,4 +1706,5 @@
   <public type="attr" name="notificationTimeout" />
   <public type="attr" name="accessibilityFlags" />
   <public type="attr" name="canRetrieveWindowContent" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8a4b74b..29c23a68 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -562,6 +562,13 @@
         never normally need.</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_retrieve_window_content">retrieve screen content</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_retrieve_window_content">Allows application to retrieve
+        the content of the active window. Malicious applications may retrieve
+        the entire window content and examine all its text except passwords.</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_shutdown">partial shutdown</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_shutdown">Puts the activity manager into a shutdown
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b02d904..00f47fa 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -12,7 +12,7 @@
 	$(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6084dd2..8e2d925 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -98,6 +98,9 @@
     <uses-permission android:name="android.permission.ASEC_RENAME" />
     <uses-permission android:name="android.permission.SHUTDOWN" />
 
+    <!-- accessibility test permissions -->
+    <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
+
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
@@ -1225,6 +1228,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.accessibilityservice.InterrogationActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml
new file mode 100644
index 0000000..28d965b
--- /dev/null
+++ b/core/tests/coretests/res/layout/interrogation_activity.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    >
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button1"
+        />
+        <Button
+            android:id="@+id/button2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button2"
+        />
+        <Button
+            android:id="@+id/button3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button3"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button4"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button4"
+        />
+        <Button
+            android:id="@+id/button5"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button5"
+        />
+        <Button
+            android:id="@+id/button6"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button6"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button7"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button7"
+        />
+        <Button
+            android:id="@+id/button8"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button8"
+        />
+        <Button
+            android:id="@+id/button9"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button9"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 807386a..f51b08e 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -117,4 +117,16 @@
 
     <string name="searchable_label">SearchManager Test</string>
     <string name="searchable_hint">A search hint</string>
+
+    <!-- InterrogationActivity -->
+    <string name="button1">Button1</string>
+    <string name="button2">Button2</string>
+    <string name="button3">Button3</string>
+    <string name="button4">Button4</string>
+    <string name="button5">Button5</string>
+    <string name="button6">Button6</string>
+    <string name="button7">Button7</string>
+    <string name="button8">Button8</string>
+    <string name="button9">Button9</string>
+
 </resources>
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
new file mode 100644
index 0000000..b4a0581
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
@@ -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.
+ */
+
+package android.accessibilityservice;
+
+import com.android.frameworks.coretests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.interrogation_activity);
+
+        findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                /* do nothing */
+            }
+        });
+        findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
+            public boolean onLongClick(View v) {
+                return true;
+            }
+        });
+    }
+}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
new file mode 100644
index 0000000..a20cc1f
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -0,0 +1,464 @@
+/**
+ * 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 android.accessibilityservice;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
+
+import com.android.frameworks.coretests.R;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityManager;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivityTest
+        extends ActivityInstrumentationTestCase2<InterrogationActivity> {
+
+    // Timeout before give up wait for the system to process an accessibility setting change.
+    private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
+
+    // Helpers to figure out the first and last test methods
+    // This is a workaround for the lack of such support in JUnit3
+    private static int sTestMethodCount;
+    private static int sExecutedTestMethodCount;
+
+    // Handle to a connection to the AccessibilityManagerService
+    private static IAccessibilityServiceConnection sConnection;
+
+    // The last received accessibility event
+    private static volatile AccessibilityEvent sLastAccessibilityEvent;
+
+    public InterrogationActivityTest() {
+        super(InterrogationActivity.class);
+        sTestMethodCount = getTestMethodCount();
+    }
+
+    @LargeTest
+    public void testFindAccessibilityNodeInfoByViewId() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertNotNull(button);
+            assertEquals(0, button.getChildCount());
+
+            // bounds
+            Rect bounds = new Rect();
+            button.getBounds(bounds);
+            assertEquals(0, bounds.left);
+            assertEquals(0, bounds.top);
+            assertEquals(73, bounds.right);
+            assertEquals(48, bounds.bottom);
+
+            // char sequence attributes
+            assertEquals("com.android.frameworks.coretests", button.getPackageName());
+            assertEquals("android.widget.Button", button.getClassName());
+            assertEquals("Button5", button.getText());
+            assertNull(button.getContentDescription());
+
+            // boolean attributes
+            assertTrue(button.isFocusable());
+            assertTrue(button.isClickable());
+            assertTrue(button.isEnabled());
+            assertFalse(button.isFocused());
+            assertTrue(button.isClickable());
+            assertFalse(button.isPassword());
+            assertFalse(button.isSelected());
+            assertFalse(button.isCheckable());
+            assertFalse(button.isChecked());
+
+            // actions
+            assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION,
+                button.getActions());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testFindAccessibilityNodeInfoByViewText() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view by text
+            List<AccessibilityNodeInfo> buttons =
+                getConnection().findAccessibilityNodeInfosByViewText("butto");
+            assertEquals(9, buttons.size());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testTraverseAllViews() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // make list of expected nodes
+            List<String> classNameAndTextList = new ArrayList<String>();
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.ButtonButton1");
+            classNameAndTextList.add("android.widget.ButtonButton2");
+            classNameAndTextList.add("android.widget.ButtonButton3");
+            classNameAndTextList.add("android.widget.ButtonButton4");
+            classNameAndTextList.add("android.widget.ButtonButton5");
+            classNameAndTextList.add("android.widget.ButtonButton6");
+            classNameAndTextList.add("android.widget.ButtonButton7");
+            classNameAndTextList.add("android.widget.ButtonButton8");
+            classNameAndTextList.add("android.widget.ButtonButton9");
+
+            AccessibilityNodeInfo root =  getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.root);
+            assertNotNull("We must find the existing root.", root);
+
+            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+            fringe.add(root);
+
+            // do a BFS traversal and check nodes
+            while (!fringe.isEmpty()) {
+                AccessibilityNodeInfo current = fringe.poll();
+
+                CharSequence className = current.getClassName();
+                CharSequence text = current.getText();
+                String receivedClassNameAndText = className.toString()
+                   + ((text != null) ? text.toString() : "");
+                String expectedClassNameAndText = classNameAndTextList.remove(0);
+
+                assertEquals("Did not get the expected node info",
+                        expectedClassNameAndText, receivedClassNameAndText);
+
+                final int childCount = current.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    AccessibilityNodeInfo child = current.getChild(i);
+                    fringe.add(child);
+                }
+            }
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionFocus() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isFocused());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+
+            // find the view again and make sure it is focused
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isFocused());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionClearFocus() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button =  getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isFocused());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+
+            // find the view again and make sure it is focused
+            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isFocused());
+
+            // unfocus the view
+            assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
+
+            // find the view again and make sure it is not focused
+            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertFalse(button.isFocused());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionSelect() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not selected
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // select the view
+            assertTrue(button.performAction(ACTION_SELECT));
+
+            // find the view again and make sure it is selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isSelected());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionClearSelection() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not selected
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // select the view
+            assertTrue(button.performAction(ACTION_SELECT));
+
+            // find the view again and make sure it is selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isSelected());
+
+            // unselect the view
+            assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
+
+            // find the view again and make sure it is not selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertFalse(button.isSelected());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testAccessibilityEventGetSource() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+            SystemClock.sleep(200);
+
+            // check that last event source
+            AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource();
+            assertNotNull(source);
+
+            // bounds
+            Rect buttonBounds = new Rect();
+            button.getBounds(buttonBounds);
+            Rect sourceBounds = new Rect();
+            source.getBounds(sourceBounds);
+
+            assertEquals(buttonBounds.left, sourceBounds.left);
+            assertEquals(buttonBounds.right, sourceBounds.right);
+            assertEquals(buttonBounds.top, sourceBounds.top);
+            assertEquals(buttonBounds.bottom, sourceBounds.bottom);
+
+            // char sequence attributes
+            assertEquals(button.getPackageName(), source.getPackageName());
+            assertEquals(button.getClassName(), source.getClassName());
+            assertEquals(button.getText(), source.getText());
+            assertSame(button.getContentDescription(), source.getContentDescription());
+
+            // boolean attributes
+            assertSame(button.isFocusable(), source.isFocusable());
+            assertSame(button.isClickable(), source.isClickable());
+            assertSame(button.isEnabled(), source.isEnabled());
+            assertNotSame(button.isFocused(), source.isFocused());
+            assertSame(button.isLongClickable(), source.isLongClickable());
+            assertSame(button.isPassword(), source.isPassword());
+            assertSame(button.isSelected(), source.isSelected());
+            assertSame(button.isCheckable(), source.isCheckable());
+            assertSame(button.isChecked(), source.isChecked());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @Override
+    protected void scrubClass(Class<?> testCaseClass) {
+        /* intentionally do not scrub */
+    }
+
+    /**
+     * Sets accessibility in a given state by writing the state to the
+     * settings and waiting until the accessibility manager service picks
+     * it up for max {@link #TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING}.
+     *
+     * @param state The accessibility state.
+     * @throws Exception If any error occurs.
+     */
+    private void ensureAccessibilityState(boolean state) throws Exception {
+        Context context = getInstrumentation().getContext();
+        // If the local manager ready => nothing to do.
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
+        if (accessibilityManager.isEnabled() == state) {
+            return;
+        }
+        synchronized (this) {
+            // Check if the system already knows about the desired state. 
+            final boolean currentState = Settings.Secure.getInt(context.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_ENABLED) == 1;
+            if (currentState != state) {
+                // Make sure we wake ourselves as the desired state is propagated.
+                accessibilityManager.addAccessibilityStateChangeListener(
+                        new AccessibilityManager.AccessibilityStateChangeListener() {
+                            public void onAccessibilityStateChanged(boolean enabled) {
+                                synchronized (this) {
+                                    notifyAll();
+                                }
+                            }
+                        });
+                Settings.Secure.putInt(context.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_ENABLED, state ? 1 : 0);
+            }
+            // No while one attempt and that is it.
+            try {
+                wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+        if (accessibilityManager.isEnabled() != state) {
+            throw new IllegalStateException("Could not set accessibility state to: " + state);
+        }
+    }
+
+    /**
+     * Execute some set up code before any test method.
+     *
+     * NOTE: I miss Junit4's @BeforeClass
+     *
+     * @throws Exception If an error occurs.
+     */
+    private void beforeClassIfNeeded() throws Exception {
+        sExecutedTestMethodCount++;
+        if (sExecutedTestMethodCount == 1) {
+            ensureAccessibilityState(true);
+        }
+    }
+
+    /**
+     * Execute some clean up code after all test methods.
+     *
+     * NOTE: I miss Junit4's @AfterClass
+     *
+     * @throws Exception If an error occurs.
+     */
+    public void afterClassIfNeeded() throws Exception {
+        if (sExecutedTestMethodCount == sTestMethodCount) {
+            sExecutedTestMethodCount = 0;
+            ensureAccessibilityState(false);
+        }
+    }
+
+    private static IAccessibilityServiceConnection getConnection() throws Exception {
+        if (sConnection == null) {
+            IEventListener listener = new IEventListener.Stub() {
+                public void setConnection(IAccessibilityServiceConnection connection)
+                        throws RemoteException {
+                    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+                    info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+                    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
+                    info.notificationTimeout = 0;
+                    info.flags = AccessibilityServiceInfo.DEFAULT;
+                    connection.setServiceInfo(info);
+                }
+
+                public void onInterrupt() {}
+
+                public void onAccessibilityEvent(AccessibilityEvent event) {
+                    sLastAccessibilityEvent= AccessibilityEvent.obtain(event);
+                }
+            };
+            IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+                ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+            sConnection = manager.registerEventListener(listener);
+        }
+        return sConnection;
+    }
+
+    /**
+     * @return The number of test methods.
+     */
+    private int getTestMethodCount() {
+        int testMethodCount = 0;
+        for (Method method : getClass().getMethods()) {
+            final int modifiers = method.getModifiers();
+            if (method.getName().startsWith("test")
+                    && (modifiers & Modifier.PUBLIC) != 0
+                    && (modifiers & Modifier.STATIC) == 0) {
+                testMethodCount++;
+            }
+        }
+        return testMethodCount;
+    }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index eb63c0d..0b72c3c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.UID_ALL;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -51,7 +49,7 @@
 
     public void testRecordSingleBucket() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record data into narrow window to get single bucket
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
@@ -62,7 +60,7 @@
 
     public void testRecordEqualBuckets() throws Exception {
         final long bucketDuration = HOUR_IN_MILLIS;
-        stats = buildStats(bucketDuration);
+        stats = new NetworkStatsHistory(bucketDuration);
 
         // split equally across two buckets
         final long recordStart = TEST_START + (bucketDuration / 2);
@@ -75,7 +73,7 @@
 
     public void testRecordTouchingBuckets() throws Exception {
         final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // split almost completely into middle bucket, but with a few minutes
         // overlap into neighboring buckets. total record is 20 minutes.
@@ -94,7 +92,7 @@
 
     public void testRecordGapBuckets() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record some data today and next week with large gap
         final long firstStart = TEST_START;
@@ -122,7 +120,7 @@
 
     public void testRecordOverlapBuckets() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record some data in one bucket, and another overlapping buckets
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
@@ -135,9 +133,80 @@
         assertBucket(stats, 1, 512L, 512L);
     }
 
+    public void testRecordEntireGapIdentical() throws Exception {
+        final long[] total = new long[2];
+
+        // first, create two separate histories far apart
+        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
+
+        final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
+        final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
+
+        // combine together with identical bucket size
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 3000L, 1500L);
+
+        // now inspect internal buckets
+        assertBucket(stats, 0, 1000L, 500L);
+        assertBucket(stats, 1, 1000L, 500L);
+        assertBucket(stats, 2, 500L, 250L);
+        assertBucket(stats, 3, 500L, 250L);
+    }
+
+    public void testRecordEntireOverlapVaryingBuckets() throws Exception {
+        final long[] total = new long[2];
+
+        // create history just over hour bucket boundary
+        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
+
+        final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
+        final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+        stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
+
+        // combine together with minute bucket size
+        stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 650L, 650L);
+
+        // now inspect internal buckets
+        assertBucket(stats, 0, 10L, 10L);
+        assertBucket(stats, 1, 20L, 20L);
+        assertBucket(stats, 2, 20L, 20L);
+        assertBucket(stats, 3, 20L, 20L);
+        assertBucket(stats, 4, 20L, 20L);
+        assertBucket(stats, 5, 20L, 20L);
+        assertBucket(stats, 6, 10L, 10L);
+
+        // now combine using 15min buckets
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 650L, 650L);
+
+        // and inspect buckets
+        assertBucket(stats, 0, 200L, 200L);
+        assertBucket(stats, 1, 150L, 150L);
+        assertBucket(stats, 2, 150L, 150L);
+        assertBucket(stats, 3, 150L, 150L);
+    }
+
     public void testRemove() throws Exception {
-        final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
 
         // record some data across 24 buckets
         stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
@@ -165,13 +234,44 @@
         assertEquals(0, stats.bucketCount);
     }
 
+    public void testTotalData() throws Exception {
+        final long BUCKET_SIZE = HOUR_IN_MILLIS;
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
+
+        // record uniform data across day
+        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
+
+        final long[] total = new long[2];
+
+        // verify that total outside range is 0
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total);
+        assertTotalEquals(total, 0, 0);
+
+        // verify total in first hour
+        stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total);
+        assertTotalEquals(total, 100, 200);
+
+        // verify total across 1.5 hours
+        stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total);
+        assertTotalEquals(total, 150, 300);
+
+        // verify total beyond end
+        stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 100, 200);
+
+        // verify everything total
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 2400, 4800);
+
+    }
+
     @Suppress
     public void testFuzzing() throws Exception {
         try {
             // fuzzing with random events, looking for crashes
             final Random r = new Random();
             for (int i = 0; i < 500; i++) {
-                stats = buildStats(r.nextLong());
+                stats = new NetworkStatsHistory(r.nextLong());
                 for (int j = 0; j < 10000; j++) {
                     if (r.nextBoolean()) {
                         // add range
@@ -191,10 +291,6 @@
         }
     }
 
-    private static NetworkStatsHistory buildStats(long bucketSize) {
-        return new NetworkStatsHistory(TYPE_MOBILE, null, UID_ALL, bucketSize);
-    }
-
     private static void assertConsistent(NetworkStatsHistory stats) {
         // verify timestamps are monotonic
         for (int i = 1; i < stats.bucketCount; i++) {
@@ -202,6 +298,11 @@
         }
     }
 
+    private static void assertTotalEquals(long[] total, long rx, long tx) {
+        assertEquals("unexpected rx", rx, total[0]);
+        assertEquals("unexpected tx", tx, total[1]);
+    }
+
     private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) {
         assertEquals("unexpected rx", rx, stats.rx[index]);
         assertEquals("unexpected tx", tx, stats.tx[index]);
diff --git a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
index ee89414..ee4ce95 100644
--- a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
@@ -16,6 +16,10 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+import com.google.mockwebserver.SocketPolicy;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -33,10 +37,6 @@
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
 import org.apache.http.conn.ssl.SSLSocketFactory;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
-import tests.http.SocketPolicy;
 
 public abstract class AbstractProxyTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java
index e736bc9..29e590f 100644
--- a/core/tests/coretests/src/android/net/http/CookiesTest.java
+++ b/core/tests/coretests/src/android/net/http/CookiesTest.java
@@ -16,6 +16,9 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -30,9 +33,6 @@
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.params.ConnRoutePNames;
 import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
 
 public final class CookiesTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
index ad3ec3d..da77298 100644
--- a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -16,6 +16,12 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.SocketPolicy;
+import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -24,12 +30,6 @@
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.SocketPolicy;
-import static tests.http.SocketPolicy.DISCONNECT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
 
 public final class DefaultHttpClientTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/view/GravityTest.java b/core/tests/coretests/src/android/view/GravityTest.java
index 180a390..2a7a64f 100644
--- a/core/tests/coretests/src/android/view/GravityTest.java
+++ b/core/tests/coretests/src/android/view/GravityTest.java
@@ -59,11 +59,11 @@
         assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, false);
         assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, true);
 
-        assertOneGravity(Gravity.LEFT, Gravity.BEFORE, false);
-        assertOneGravity(Gravity.RIGHT, Gravity.BEFORE, true);
+        assertOneGravity(Gravity.LEFT, Gravity.START, false);
+        assertOneGravity(Gravity.RIGHT, Gravity.START, true);
 
-        assertOneGravity(Gravity.RIGHT, Gravity.AFTER, false);
-        assertOneGravity(Gravity.LEFT, Gravity.AFTER, true);
+        assertOneGravity(Gravity.RIGHT, Gravity.END, false);
+        assertOneGravity(Gravity.LEFT, Gravity.END, true);
     }
 
     private void assertOneGravity(int expected, int initial, boolean isRtl) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 5ed7966..b9c0d80 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -132,6 +132,7 @@
     <assign-permission name="android.permission.BATTERY_STATS" uid="shell" />
     <assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" />
     <assign-permission name="android.permission.INJECT_EVENTS" uid="shell" />
+    <assign-permission name="android.permission.RETRIEVE_WINDOW_CONTENT" uid="shell" />
     <assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" />
     <assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" />
     <assign-permission name="android.permission.SET_ORIENTATION" uid="shell" />
diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd
index 60bffdb..180322f 100644
--- a/docs/html/guide/topics/graphics/renderscript.jd
+++ b/docs/html/guide/topics/graphics/renderscript.jd
@@ -630,7 +630,7 @@
     <dt><code>ScriptC_helloworld</code></dt>
 
     <dd>This class is generated by the Android build tools and is the reflected version of the
-    <code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the
+    <code>helloworld.rs</code> Renderscript. It provides a high level entry point into the
     <code>helloworld.rs</code> native code by defining the corresponding methods that you can call
     from the traditional framework APIs.</dd>
 
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 9ac1a00..3c8aba3 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -100,7 +100,6 @@
     public static final int USAGE_GRAPHICS_CONSTANTS = 0x0008;
 
     /**
-     * @hide
      * USAGE_GRAPHICS_RENDER_TARGET The allcation will be used as a
      * target for offscreen rendering
      *
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index dc5fc84..83d4f6d 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -263,7 +263,7 @@
     // direction is not affected by the rotation or mirroring of
     // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates range from -1000 to 1000.
     // (-1000,-1000) is the upper left point. (1000, 1000) is the lower right
-    // point. The length and width of focus areas cannot be 0 or negative.
+    // point. The width and height of focus areas cannot be 0 or negative.
     //
     // The fifth element is the weight. Values for weight must range from 1 to
     // 1000.  The weight should be interpreted as a per-pixel weight - all
@@ -373,7 +373,7 @@
     // direction is not affected by the rotation or mirroring of
     // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates of the rectangle range
     // from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000)
-    // is the lower right point. The length and width of metering areas cannot
+    // is the lower right point. The width and height of metering areas cannot
     // be 0 or negative.
     //
     // The fifth element is the weight. Values for weight must range from 1 to
@@ -550,6 +550,7 @@
     static const char PIXEL_FORMAT_YUV422I[]; // YUY2
     static const char PIXEL_FORMAT_YUV420P[]; // YV12
     static const char PIXEL_FORMAT_RGB565[];
+    static const char PIXEL_FORMAT_RGBA8888[];
     static const char PIXEL_FORMAT_JPEG[];
 
     // Values for focus mode settings.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 3b5aba4..c9f694a 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -655,11 +655,6 @@
     // Oldest sample to consider when calculating the velocity.
     static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
 
-    // When the total duration of the window of samples being averaged is less
-    // than the window size, the resulting velocity is scaled to reduce the impact
-    // of overestimation in short traces.
-    static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms
-
     // The minimum duration between samples when estimating velocity.
     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
 
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 6b69b8a..fd6c22c 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -20,8 +20,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
-
+import com.android.org.bouncycastle.openssl.PEMReader;
+import com.android.org.bouncycastle.openssl.PEMWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charsets;
 import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * {@hide}
@@ -60,6 +71,40 @@
     /** Data type for PKCS12. */
     public static final String PKCS12 = "PKCS12";
 
+    /**
+     * Convert objects to a PEM format, which is used for
+     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+     * entries.
+     */
+    public static byte[] convertToPem(Object... objects) throws IOException {
+        ByteArrayOutputStream bao = new ByteArrayOutputStream();
+        Writer writer = new OutputStreamWriter(bao, Charsets.US_ASCII);
+        PEMWriter pw = new PEMWriter(writer);
+        for (Object o : objects) {
+            pw.writeObject(o);
+        }
+        pw.close();
+        return bao.toByteArray();
+    }
+    /**
+     * Convert objects from PEM format, which is used for
+     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+     * entries.
+     */
+    public static List<Object> convertFromPem(byte[] bytes) throws IOException {
+        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
+        Reader reader = new InputStreamReader(bai, Charsets.US_ASCII);
+        PEMReader pr = new PEMReader(reader);
+
+        List<Object> result = new ArrayList<Object>();
+        Object o;
+        while ((o = pr.readObject()) != null) {
+            result.add(o);
+        }
+        pr.close();
+        return result;
+    }
+
     private static Credentials singleton;
 
     public static Credentials getInstance() {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index ec820cf..ba784ed 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -34,6 +34,7 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -185,11 +186,9 @@
             throw new IllegalArgumentException("bytes == null");
         }
         try {
-            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError(e);
-        } catch (InvalidKeySpecException e) {
+            KeyPair keyPair = (KeyPair) Credentials.convertFromPem(bytes).get(0);
+            return keyPair.getPrivate();
+        } catch (IOException e) {
             throw new AssertionError(e);
         }
     }
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index c295315..9392cf2 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -146,6 +146,7 @@
 const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
 const char CameraParameters::PIXEL_FORMAT_YUV420P[]  = "yuv420p";
 const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565";
+const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888";
 const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
 
 // Values for focus mode settings.
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index e95dbe4..0af7f80 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -700,7 +700,6 @@
 
 const uint32_t VelocityTracker::HISTORY_SIZE;
 const nsecs_t VelocityTracker::MAX_AGE;
-const nsecs_t VelocityTracker::MIN_WINDOW;
 const nsecs_t VelocityTracker::MIN_DURATION;
 
 VelocityTracker::VelocityTracker() {
@@ -891,14 +890,6 @@
 
         // Make sure we used at least one sample.
         if (samplesUsed != 0) {
-            // Scale the velocity linearly if the window of samples is small.
-            nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime;
-            if (totalDuration < MIN_WINDOW) {
-                float scale = float(totalDuration) / float(MIN_WINDOW);
-                accumVx *= scale;
-                accumVy *= scale;
-            }
-
             *outVx = accumVx;
             *outVy = accumVy;
             return true;
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index a54cf28..7634c6c 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -76,8 +76,9 @@
     public static final int FILE_TYPE_PNG     = 33;
     public static final int FILE_TYPE_BMP     = 34;
     public static final int FILE_TYPE_WBMP    = 35;
+    public static final int FILE_TYPE_WEBP    = 36;
     private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG;
-    private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WBMP;
+    private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WEBP;
    
     // Playlist file types
     public static final int FILE_TYPE_M3U      = 41;
@@ -213,6 +214,7 @@
         addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
         addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
         addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
+        addFileType("WEBP", FILE_TYPE_WBMP, "image/webp");
  
         addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
         addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 8a180d8..6cb3847 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -26,13 +26,6 @@
 #include <system/audio.h>
 
 // ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
 
 namespace android {
 
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index ebe821f..07152d8 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -30,26 +30,6 @@
 // foreground thread.
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
-static int myTid() {
-#ifdef HAVE_GETTID
-    return gettid();
-#else
-    return getpid();
-#endif
-}
-
 #undef LOG_TAG
 #define LOG_TAG "IMediaMetadataRetriever"
 #include <utils/Log.h>
@@ -60,18 +40,18 @@
 static void sendSchedPolicy(Parcel& data)
 {
     SchedPolicy policy;
-    get_sched_policy(myTid(), &policy);
+    get_sched_policy(gettid(), &policy);
     data.writeInt32(policy);
 }
 
 static void setSchedPolicy(const Parcel& data)
 {
     SchedPolicy policy = (SchedPolicy) data.readInt32();
-    set_sched_policy(myTid(), policy);
+    set_sched_policy(gettid(), policy);
 }
 static void restoreSchedPolicy()
 {
-    set_sched_policy(myTid(), SP_FOREGROUND);
+    set_sched_policy(gettid(), SP_FOREGROUND);
 }
 }; // end namespace android
 #endif
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 88157d2..8b953e0 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -23,13 +23,6 @@
 #include <media/JetPlayer.h>
 
 
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
-
 namespace android
 {
 
@@ -188,7 +181,7 @@
     // signal main thread that we started
     {
         Mutex::Autolock l(mMutex);
-        mTid = myTid();
+        mTid = gettid();
         LOGV("JetPlayer::render(): render thread(%d) signal", mTid);
         mCondition.signal();
     }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index eae93ff..54a6547 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -68,18 +68,6 @@
 
 #include <OMX.h>
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
 namespace {
 using android::media::Metadata;
 using android::status_t;
@@ -387,14 +375,6 @@
     return NO_ERROR;
 }
 
-static int myTid() {
-#ifdef HAVE_GETTID
-    return gettid();
-#else
-    return getpid();
-#endif
-}
-
 status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
@@ -426,7 +406,7 @@
         }
 
         result.append(" Files opened and/or mapped:\n");
-        snprintf(buffer, SIZE, "/proc/%d/maps", myTid());
+        snprintf(buffer, SIZE, "/proc/%d/maps", gettid());
         FILE *f = fopen(buffer, "r");
         if (f) {
             while (!feof(f)) {
@@ -446,13 +426,13 @@
             result.append("\n");
         }
 
-        snprintf(buffer, SIZE, "/proc/%d/fd", myTid());
+        snprintf(buffer, SIZE, "/proc/%d/fd", gettid());
         DIR *d = opendir(buffer);
         if (d) {
             struct dirent *ent;
             while((ent = readdir(d)) != NULL) {
                 if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
-                    snprintf(buffer, SIZE, "/proc/%d/fd/%s", myTid(), ent->d_name);
+                    snprintf(buffer, SIZE, "/proc/%d/fd/%s", gettid(), ent->d_name);
                     struct stat s;
                     if (lstat(buffer, &s) == 0) {
                         if ((s.st_mode & S_IFMT) == S_IFLNK) {
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 8f776b4..06fb103 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -40,18 +40,6 @@
 #include "MetadataRetrieverClient.h"
 #include "StagefrightMetadataRetriever.h"
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
 namespace android {
 
 extern player_type getPlayerType(const char* url);
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 589c625..7e04523 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -29,17 +29,12 @@
 #include <libsonivox/eas_reverb.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <system/audio.h>
 
 #include "MidiFile.h"
 
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -455,7 +450,7 @@
     // signal main thread that we started
     {
         Mutex::Autolock l(mMutex);
-        mTid = myTid();
+        mTid = gettid();
         LOGV("render thread(%d) signal", mTid);
         mCondition.signal();
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f6a8b17..d0f6730 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2862,8 +2862,8 @@
      * For historical reasons, any user data start
      * with "\0xA9", must be followed by its assoicated
      * language code.
-     * 0x0012: locale en
-     * 0x15c7: language 5575
+     * 0x0012: text string length
+     * 0x15c7: lang (locale) code: en
      */
     writeInt32(0x001215c7);
     writeLatitude(mLatitudex10000);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index 79fd2cb..e7f98de 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -123,63 +123,6 @@
       intent.setDataAndType(path, mimetype);
       startActivity(intent);
     }
-    
-    @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
-      switch (keyCode) {
-          case KeyEvent.KEYCODE_0:
-            MediaPlayer mp = new MediaPlayer();
-            try{
-              mp.setDataSource(MediaNames.VIDEO_RTSP3GP);
-              Log.v("emily","awb  " + testfilepath);
-              mp.setDisplay(mSurfaceView.getHolder());
-              mp.prepare();
-              mp.start();
-            }catch (Exception e){}
-              break;
-          
-          //start the music player intent with the test URL from PV    
-          case KeyEvent.KEYCODE_1:
-            startPlayback(MediaNames.STREAM_MP3_1);
-            break;
-          
-          case KeyEvent.KEYCODE_2:
-            startPlayback(MediaNames.STREAM_MP3_2);
-            break;
-          
-          case KeyEvent.KEYCODE_3:
-            startPlayback(MediaNames.STREAM_MP3_3);
-            break;
-          
-          case KeyEvent.KEYCODE_4:
-            startPlayback(MediaNames.STREAM_MP3_4);
-            break;
-          
-          case KeyEvent.KEYCODE_5:
-            startPlayback(MediaNames.STREAM_MP3_5);
-            break;
-          
-          case KeyEvent.KEYCODE_6:
-            startPlayback(MediaNames.STREAM_MP3_6);
-            break;
-          
-          case KeyEvent.KEYCODE_7:
-            startPlayback(MediaNames.STREAM_MP3_7);
-            break;
-          
-          case KeyEvent.KEYCODE_8:
-            startPlayback(MediaNames.STREAM_MP3_8);
-            break;
-          
-          case KeyEvent.KEYCODE_9:
-            startPlayback(MediaNames.STREAM_MP3_9);
-            break;
-          
-              
-              
-      }
-      return super.onKeyDown(keyCode, event);
-     
-  }  
 
   public static boolean checkStreamingServer() throws Exception {
       InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index ca6e999..d6e1346 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -50,19 +50,9 @@
     public static final long PAUSE_WAIT_TIME = 3000;
     public static final long WAIT_TIME = 2000;
     public static final long WAIT_SNAPSHOT_TIME = 5000;
-  
-    //Streaming Video
-    public static final String VIDEO_HTTP3GP = "http://pvs.pv.com/jj/lipsync0.3gp";  
-    public static final String VIDEO_RTSP3GP = "rtsp://63.241.31.203/public/jj/md.3gp";
-    public static final String VIDEO_RTSP3GP2 = "rtsp://pvs.pv.com/public/live_dvd1.3gp";
-    public static final String VIDEO_RTSP3GP3 = 
-      "rtsp://ehug.rtsp-youtube.l.google.com/" +
-      "Ci4LENy73wIaJQmeRVCJq4HuQBMYDSANFEIJbXYtZ29vZ2xlSARSB2RldGFpbHMM/0/0/0/video.3gp";
-    //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp";
-  
+
     //local video
     public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4";
-    public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp";
     public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp";
     public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp";
     public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp";
@@ -73,205 +63,9 @@
     public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
     public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
     
-    //ringtone
-    public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3";
-
-    //streaming mp3
-    public static final String STREAM_MP3_1 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3";
-    public static final String STREAM_MP3_2 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/mp3/dualStereo.mp3";
-    public static final String STREAM_MP3_3 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/15%20Keep%20Holding%20On.mp3";
-    public static final String STREAM_MP3_4 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/1%20-%20Apologize.mp3";
-    public static final String STREAM_MP3_5 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/" +
-      "03%20You're%20Gonna%20Miss%20This.mp3";
-    public static final String STREAM_MP3_6 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
-      "/02%20Looney%20Tunes%20%C3%82%C2%B7%20Light%20Cavalry%20Overture%20(LP%20Version).mp3";
-    public static final String STREAM_MP3_7 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
-      "/01%20Love%20Song%20(Album%20Version).mp3";
-    public static final String STREAM_MP3_8 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/1%20-%20Apologize.mp3";
-    public static final String STREAM_MP3_9 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads" +
-      "/1%20-%20Smile%20(Explicit%20Version).mp3";
-    public static final String STREAM_MP3_10 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/beefcake.mp3";
-
-    //Sonivox
-    public static String MIDIFILES[] = { 
-        "/sdcard/media_api/music/Leadsol.mxmf",
-        "/sdcard/media_api/music/abba.imy", "/sdcard/media_api/music/ants.mid",
-        "/sdcard/media_api/music/greensleeves.rtttl", "/sdcard/media_api/music/test.ota"};
-  
-    //Performance measurement
-    public static String[] WAVFILES = { 
-        "/sdcard/media_api/music_perf/WAV/M1F1-AlawWE-AFsp.wav",
-        "/sdcard/media_api/music_perf/WAV/M1F1-float64-AFsp.wav",
-        "/sdcard/media_api/music_perf/WAV/song.wav",
-        "/sdcard/media_api/music_perf/WAV/WAVEtest.wav",
-        "/sdcard/media_api/music_perf/WAV/WAVEtest_out.wav",
-        "/sdcard/media_api/music_perf/WAV/test_out.wav"};
-        
-    public static String[] AMRNBFILES = { 
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"};
-  
-    public static String[] AMRWBFILES = { 
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", };
- 
-    public static String[] MP3FILES = { 
-        "/sdcard/media_api/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3",
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3", 
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Apologize.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Because_Of_You.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Complicated.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Glamorous.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Im_With_You.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Smile.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Suddenly_I_See.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/When You Say Nothing At All.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/my_happy_ending.mp3"};
-  
-    public static String[] AACFILES = { 
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",      
-    };
-    
-    public static String[] VIDEOFILES = { "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv",
-      "/sdcard/media_api/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv",
-      "/sdcard/media_api/video_perf/Chicken.wmv",
-      "/sdcard/media_api/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4",
-      "/sdcard/media_api/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4",
-      "/sdcard/media_api/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/Stevie-1.wmv",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/bugs.wmv",
-      "/sdcard/media_api/video_perf/niceday.wmv",
-      "/sdcard/media_api/video_perf/eaglesatopnflpe.wmv",
-     
-    };
-    
-    //wma - only support up to wma 9
-    public static String[] WMASUPPORTED = {
-      "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma"
-      
-    };
-    
-    public static String[] WMAUNSUPPORTED = { 
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma"
-    };
-    
     //Media Recorder
     public static final String RECORDER_OUTPUT = "/sdcard/media_api/recorderOutput.amr";
-    
+
     //video thumbnail
     public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png";
     public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png";
@@ -318,7 +112,7 @@
       "/sdcard/media_api/metadata/test34.wmv",
       "/sdcard/media_api/metadata/test_metadata.mp4",
   };
-  
+
   public static final String[] METADATA_RETRIEVAL_TEST_FILES = {
       // Raw AAC is not supported
       // "/sdcard/media_api/test_raw.aac",
@@ -405,54 +199,8 @@
           null, null, null, null, null, null, "295", "1", null}
   };
 
-  public static final String META_DATA_OTHERS [][] = {
-      {"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null,
-          null, null, "20080309T002415.000Z", null,
-          null, null, "63916", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null,
-          null, null, null, null,
-          null, null, "126540", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null,
-          null, null, null, null,
-          null, null, "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation",
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          "12", "Jaws Of Life", "2005", "449329", "1", "m4a composer"},
-      {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, 
-          null, null, null, "20051220T202015.000Z", 
-          null, null, null, "85500", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation", 
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", "2/0", "mp4 album Kung Fu Panda",
-          "mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z", 
-          "40", "Kung Fu Panda", "2008", "128521", "2", "mp4 composer"},
-      {"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation", 
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg", 
-          null, "Suspended Animation", "John Petrucci", 
-          null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null, 
-          null, null, null, null, 
-          null, null, null, "224000", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal", 
-          "Alien Crime Syndicate", "Alien Crime Syndicate", 
-          "wma 9 Composer", "20040521T175729.483Z", 
-          "Rock", "Run for the Money", "2004", "134479", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album", 
-          "wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z", 
-          "Acid Jazz", "wma 10 Title", "2010", "126574", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album", 
-          null, "wmv 9 Artist ", null, "20051122T155247.540Z", 
-          null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album", 
-          null, "Hallau Shoots & Company", null, "20020226T170045.891Z", 
-          null, "CODEC Shootout", "1986", "43709", "2", null}
-  };
-  
   //output recorded video
-  
+
   public static final String RECORDED_HVGA_H263 = "/sdcard/HVGA_H263.3gp";
   public static final String RECORDED_QVGA_H263 = "/sdcard/QVGA_H263.3gp";
   public static final String RECORDED_SQVGA_H263 = "/sdcard/SQVGA_H263.3gp";
@@ -474,22 +222,7 @@
  
   public static final long RECORDED_TIME = 5000;
   public static final long VALID_VIDEO_DURATION = 2000;
-  
-  //Videos for the mediaplayer stress test
-  public static String[] H263_STRESS = { 
-      "/sdcard/media_api/video_stress/h263/H263_CIF.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_QCIF.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_QVGA.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_SQVGA.3gp"
-  };
-  
-  public static String[] MPEG4_STRESS = { 
-    "/sdcard/media_api/video_stress/h263/mpeg4_CIF.mp4",
-    "/sdcard/media_api/video_stress/h263/mpeg4_QCIF.3gp",
-    "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp",
-    "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4"
-  };
-  
+
   //Streaming test files
   public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204};
   public static final String STREAM_H264_480_360_1411k = 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index 6ded74d..00e0a52 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -47,14 +47,8 @@
         CORRUPTED_ID3V2_TYER, CORRUPTED_ID3V2_TYER_2, CORRUPTED_ID3V2_TIT
     }
     
-    public static enum NON_MP3_TEST_FILE{
-         THREE3GP, AMRNB, AMRWB, M4A1, M4V, MIDI,
-         H264, OGG1, OGG2, WAV, WMA9, WMA10, WMV9, WMV7
-    }
-    
     public static METADATA_EXPECTEDRESULT meta;
     public static MP3_TEST_FILE mp3_test_file;
-    public static NON_MP3_TEST_FILE non_mp3_test_file;
    
     @MediumTest
     public static void testID3V1V2Metadata() throws Exception {
@@ -116,88 +110,11 @@
         validateMetatData(mp3_test_file.CORRUPTED_ID3V2_TIT.ordinal(), MediaNames.META_DATA_MP3);
     }
    
-    @MediumTest
-    public static void test3gp_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.THREE3GP.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testAmr_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.AMRNB.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testAmrWb_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.AMRWB.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testM4A1_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.M4A1.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testM4v_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.M4V.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testH264_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.H264.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    //bug# 1440489
-    @Suppress
-    @MediumTest
-    public static void testOgg1_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.OGG1.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testOgg2_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.OGG2.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testMidi_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.MIDI.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testWav_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WAV.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWma9_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMA9.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWma10_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMA10.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWmv9_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMV9.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWmv10_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMV7.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
      
     private static void validateMetatData(int fileIndex, String meta_data_file[][]) {
         Log.v(TAG, "filePath = "+ meta_data_file[fileIndex][0]);
         if ((meta_data_file[fileIndex][0].endsWith("wma") && !MediaProfileReader.getWMAEnable()) ||
             (meta_data_file[fileIndex][0].endsWith("wmv") && !MediaProfileReader.getWMVEnable())) {
-            Log.v(TAG, "Skip test since windows media is not supported");
             return;
         }
         String value = null;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index d22025c..3a9564d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -369,13 +369,7 @@
       boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_MP4);
       assertTrue("Local MP4 SeekTo", isSeek);          
     }
-    
-    @LargeTest
-    public void testVideoLong3gpSeekTo() throws Exception {
-      boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_LONG_3GP);
-      assertTrue("Local 3gp SeekTo", isSeek);         
-    }
-   
+
     @LargeTest
     public void testVideoH263AACSeekTo() throws Exception {
       boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_H263_AAC);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 34affa7..3b5b9a3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -100,109 +100,6 @@
             MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
     }
 
-    public void createDB() {
-        mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
-        mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," + 
-                "file TEXT," + "setdatatime LONG," + "preparetime LONG," +
-                "playtime LONG" + ");");
-        //clean the table before adding new data
-        mDB.execSQL("DELETE FROM perfdata");
-    }
-
-    public void audioPlaybackStartupTime(String[] testFile) {
-        long t1 = 0;
-        long t2 = 0;
-        long t3 = 0;
-        long t4 = 0;
-        long setDataSourceDuration = 0;
-        long prepareDuration = 0;
-        long startDuration = 0;
-        long totalSetDataTime = 0;
-        long totalPrepareTime = 0;
-        long totalStartDuration = 0;
-
-        int numberOfFiles = testFile.length;
-        Log.v(TAG, "File length " + numberOfFiles);
-        for (int k = 0; k < numberOfFiles; k++) {
-            MediaPlayer mp = new MediaPlayer();
-            try {
-                t1 = SystemClock.uptimeMillis();
-                FileInputStream fis = new FileInputStream(testFile[k]);
-                FileDescriptor fd = fis.getFD();
-                mp.setDataSource(fd);
-                fis.close();
-                t2 = SystemClock.uptimeMillis();
-                mp.prepare();
-                t3 = SystemClock.uptimeMillis();
-                mp.start();
-                t4 = SystemClock.uptimeMillis();
-            } catch (Exception e) {
-                Log.v(TAG, e.toString());
-            }
-            setDataSourceDuration = t2 - t1;
-            prepareDuration = t3 - t2;
-            startDuration = t4 - t3;
-            totalSetDataTime = totalSetDataTime + setDataSourceDuration;
-            totalPrepareTime = totalPrepareTime + prepareDuration;
-            totalStartDuration = totalStartDuration + startDuration;
-            mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," +
-                    " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' +
-                    setDataSourceDuration + ',' + prepareDuration +
-            		',' + startDuration + ");");
-            Log.v(TAG, "File name " + testFile[k]);
-            mp.stop();
-            mp.release();
-        }
-        Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles);
-        Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles);
-        Log.v(TAG, "start average " + totalStartDuration / numberOfFiles);
-
-    }
-
-    @Suppress
-    public void testStartUpTime() throws Exception {
-        createDB();
-        audioPlaybackStartupTime(MediaNames.MP3FILES);
-        audioPlaybackStartupTime(MediaNames.AACFILES);
-
-        //close the database after all transactions
-        if (mDB.isOpen()) {
-            mDB.close();
-        }
-    }
-
-    public void wmametadatautility(String[] testFile) {
-        long t1 = 0;
-        long t2 = 0;
-        long sum = 0;
-        long duration = 0;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        String value;
-        for (int i = 0, n = testFile.length; i < n; ++i) {
-            try {
-                t1 = SystemClock.uptimeMillis();
-                retriever.setDataSource(testFile[i]);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
-                value =
-                    retriever
-                    .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER);
-                t2 = SystemClock.uptimeMillis();
-                duration = t2 - t1;
-                Log.v(TAG, "Time taken = " + duration);
-                sum = sum + duration;
-            } catch (Exception e) {
-                Log.v(TAG, e.getMessage());
-            }
-
-        }
-        Log.v(TAG, "Average duration = " + sum / testFile.length);
-    }
-
     private void initializeMessageLooper() {
         final ConditionVariable startDone = new ConditionVariable();
         new Thread() {
@@ -421,13 +318,6 @@
         return true;
     }
 
-    @Suppress
-    public void testWmaParseTime() throws Exception {
-        // createDB();
-        wmametadatautility(MediaNames.WMASUPPORTED);
-    }
-
-
     // Test case 1: Capture the memory usage after every 20 h263 playback
     @LargeTest
     public void testH263VideoPlaybackMemoryUsage() throws Exception {
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index 52bfc28..ed413e6 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -60,6 +60,7 @@
     IBackupManager mBackupManager;
     FullObserver mObserver;
     int mToken;
+    boolean mDidAcknowledge;
 
     TextView mStatusView;
     Button mAllowButton;
@@ -70,6 +71,7 @@
         Context mContext;
         ObserverHandler(Context context) {
             mContext = context;
+            mDidAcknowledge = false;
         }
 
         @Override
@@ -157,11 +159,7 @@
         mAllowButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                try {
-                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
-                } catch (RemoteException e) {
-                    // TODO: bail gracefully if we can't contact the backup manager
-                }
+                sendAcknowledgement(mToken, true, mObserver);
                 mAllowButton.setEnabled(false);
                 mDenyButton.setEnabled(false);
             }
@@ -170,11 +168,7 @@
         mDenyButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                try {
-                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, mObserver);
-                } catch (RemoteException e) {
-                    // TODO: bail gracefully if we can't contact the backup manager
-                }
+                sendAcknowledgement(mToken, false, mObserver);
                 mAllowButton.setEnabled(false);
                 mDenyButton.setEnabled(false);
             }
@@ -187,14 +181,21 @@
 
         // We explicitly equate departure from the UI with refusal.  This includes the
         // implicit configuration-changed stop/restart cycle.
-        try {
-            mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, null);
-        } catch (RemoteException e) {
-            // if this fails we'll still time out with no acknowledgment
-        }
+        sendAcknowledgement(mToken, false, null);
         finish();
     }
 
+    void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
+        if (!mDidAcknowledge) {
+            mDidAcknowledge = true;
+            try {
+                mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
+            } catch (RemoteException e) {
+                // TODO: bail gracefully if we can't contact the backup manager
+            }
+        }
+    }
+
     /**
      * The observer binder for showing backup/restore progress.  This binder just bounces
      * the notifications onto the main thread.
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
new file mode 100644
index 0000000..1d4f4da7
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.mk
@@ -0,0 +1,33 @@
+#
+# 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)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
new file mode 100644
index 0000000..258059c
--- /dev/null
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.android.sharedstoragebackup" >
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+
+    <application android:allowClearUserData="false"
+                 android:permission="android.permission.CONFIRM_FULL_BACKUP"
+                 android:fullBackupAgent=".SharedStorageAgent"
+                 android:allowBackup="false" >
+    </application>
+</manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
new file mode 100644
index 0000000..f43cb81
--- /dev/null
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.sharedstoragebackup.SharedStorageAgent
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
new file mode 100644
index 0000000..b02ca2e
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -0,0 +1,93 @@
+package com.android.sharedstoragebackup;
+
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SharedStorageAgent extends FullBackupAgent {
+    static final String TAG = "SharedStorageAgent";
+    static final boolean DEBUG = true;
+
+    StorageVolume[] mVolumes;
+
+    @Override
+    public void onCreate() {
+        StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+        if (mgr != null) {
+            mVolumes = mgr.getVolumeList();
+        } else {
+            Slog.e(TAG, "Unable to access Storage Manager");
+        }
+    }
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) throws IOException {
+        // If there are shared-storage volumes available, run the inherited directory-
+        // hierarchy backup process on them.  By convention in the Storage Manager, the
+        // "primary" shared storage volume is first in the list.
+        if (mVolumes != null) {
+            for (int i = 0; i < mVolumes.length; i++) {
+                StorageVolume v = mVolumes[i];
+                // Express the contents of volume N this way in the tar stream:
+                //     shared/N/path/to/file
+                // The restore will then extract to the given volume
+                String domain = FullBackup.SHARED_PREFIX + i;
+                processTree(null, domain, v.getPath(), null, data);
+            }
+        }
+    }
+
+    /**
+     * Incremental onRestore() implementation is not used.
+     */
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+    }
+
+    /**
+     * Full restore of one file to shared storage
+     */
+    @Override
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String relpath, long mode, long mtime)
+            throws IOException {
+        Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
+
+        File outFile = null;
+
+        // The file path must be in the semantic form [number]/path/to/file...
+        int slash = relpath.indexOf('/');
+        if (slash > 0) {
+            try {
+                int i = Integer.parseInt(relpath.substring(0, slash));
+                if (i <= mVolumes.length) {
+                    outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
+                    if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
+                } else {
+                    Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
+                }
+            } catch (NumberFormatException e) {
+                if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
+            }
+        } else {
+            if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
+        }
+        if (outFile == null) {
+            Slog.e(TAG, "Skipping data with malformed path " + relpath);
+        }
+
+        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
+    }
+}
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..e2584e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..58b8510
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..2795c34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..bbed6a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
new file mode 100644
index 0000000..977e002
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_zoom_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index d9f3f23..707a8cb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -75,6 +75,13 @@
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     />
+                <com.android.systemui.statusbar.policy.CompatModeButton
+                    android:id="@+id/compat_button"
+                    android:layout_width="80dip"
+                    android:layout_height="match_parent"
+                    android:src="@drawable/ic_sysbar_zoom"
+                    android:visibility="invisible"
+                    />
             </LinearLayout>
 
             <!-- fake space bar zone -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
new file mode 100644
index 0000000..9b44f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
@@ -0,0 +1,63 @@
+/*
+ * 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.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class CompatModeButton extends ImageView implements View.OnClickListener {
+    private static final String TAG = "StatusBar.CompatModeButton";
+
+    private ActivityManager mAM;
+
+    public CompatModeButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs);
+
+        setClickable(true);
+
+        mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+        setOnClickListener(this);
+
+        refresh();
+    }
+
+    @Override
+    public void onClick(View v) {
+        mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
+    }
+
+    public void refresh() {
+        setVisibility(
+                (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER)
+                ? View.GONE
+                : View.VISIBLE
+            );
+    }
+}
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 70a78df..3175a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -446,12 +446,14 @@
     }
 
     boolean isCdmaEri() {
-        final int iconIndex = mServiceState.getCdmaEriIconIndex();
-        if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
-            final int iconMode = mServiceState.getCdmaEriIconMode();
-            if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
-                    || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
-                return true;
+        if (mServiceState != null) {
+            final int iconIndex = mServiceState.getCdmaEriIconIndex();
+            if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
+                final int iconMode = mServiceState.getCdmaEriIconMode();
+                if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
+                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
+                    return true;
+                }
             }
         }
         return false;
@@ -854,7 +856,7 @@
         pw.print("  mDataActivity=");
         pw.println(mDataActivity);
         pw.print("  mServiceState=");
-        pw.println(mServiceState.toString());
+        pw.println(mServiceState);
         pw.print("  mNetworkName=");
         pw.println(mNetworkName);
         pw.print("  mNetworkNameDefault=");
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 5b5801d..ffb45ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.*;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CompatModeButton;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.Prefs;
@@ -963,6 +964,10 @@
 
         // See above re: lights-out policy for legacy apps.
         if (visible) setLightsOn(true);
+
+        // XXX: HACK: not sure if this is the best way to catch a new activity that might require a
+        // change in compatibility features, but it's a start.
+        ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh();
     }
 
     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f806624..d4a8ddb 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -53,13 +53,6 @@
 #include <media/EffectVisualizerApi.h>
 
 // ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
 
 
 namespace android {
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index ef8d957..dd91788 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -39,13 +39,6 @@
 #include <hardware/audio_policy.h>
 #include <hardware/audio_policy_hal.h>
 
-// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
 namespace android {
 
 static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index f9fa30e..7a18831 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -438,18 +438,23 @@
     }
 
     static void __data_cb(int32_t msg_type,
-                          const camera_memory_t *data,
+                          const camera_memory_t *data, unsigned int index,
                           void *user)
     {
         LOGV("%s", __FUNCTION__);
         CameraHardwareInterface *__this =
                 static_cast<CameraHardwareInterface *>(user);
         sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
-        __this->mDataCb(msg_type, mem, __this->mCbUser);
+        if (index >= mem->mNumBufs) {
+            LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+                 index, mem->mNumBufs);
+            return;
+        }
+        __this->mDataCb(msg_type, mem->mBuffers[index], __this->mCbUser);
     }
 
     static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type,
-                             const camera_memory_t *data,
+                             const camera_memory_t *data, unsigned index,
                              void *user)
     {
         LOGV("%s", __FUNCTION__);
@@ -459,38 +464,85 @@
         // drop all references, it will be destroyed (as well as the enclosed
         // MemoryHeapBase.
         sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
-        __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser);
+        if (index >= mem->mNumBufs) {
+            LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+                 index, mem->mNumBufs);
+            return;
+        }
+        __this->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], __this->mCbUser);
     }
 
     // This is a utility class that combines a MemoryHeapBase and a MemoryBase
     // in one.  Since we tend to use them in a one-to-one relationship, this is
     // handy.
 
-    class CameraHeapMemory : public MemoryBase {
+    class CameraHeapMemory : public RefBase {
     public:
-        CameraHeapMemory(size_t size) :
-            MemoryBase(new MemoryHeapBase(size), 0, size)
+        CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) :
+                         mBufSize(buf_size),
+                         mNumBufs(num_buffers)
         {
-            handle.data = getHeap()->base();
-            handle.size = size;
-            handle.handle = this;
+            mHeap = new MemoryHeapBase(fd, buf_size * num_buffers);
+            commonInitialization();
         }
 
+        CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
+                         mBufSize(buf_size),
+                         mNumBufs(num_buffers)
+        {
+            mHeap = new MemoryHeapBase(buf_size * num_buffers);
+            commonInitialization();
+        }
+
+        void commonInitialization()
+        {
+            handle.data = mHeap->base();
+            handle.size = mBufSize * mNumBufs;
+            handle.handle = this;
+
+            mBuffers = new sp<MemoryBase>[mNumBufs];
+            for (uint_t i = 0; i < mNumBufs; i++)
+                mBuffers[i] = new MemoryBase(mHeap,
+                                             i * mBufSize,
+                                             mBufSize);
+
+            handle.release = __put_memory;
+        }
+
+        virtual ~CameraHeapMemory()
+        {
+            delete [] mBuffers;
+        }
+
+        size_t mBufSize;
+        uint_t mNumBufs;
+        sp<MemoryHeapBase> mHeap;
+        sp<MemoryBase> *mBuffers;
+
         camera_memory_t handle;
     };
 
-    static camera_memory_t* __get_memory(size_t size,
-                                    void *user __attribute__((unused)))
+    static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs,
+                                         void *user __attribute__((unused)))
     {
-        // We allocate the object here, but we do not assign it to a strong
-        // pointer yet.  The HAL will pass it back to us via the data callback
-        // or the data-timestamp callback, and from there on we will wrap it
-        // within a strong pointer.
-
-        CameraHeapMemory *mem = new CameraHeapMemory(size);
+        CameraHeapMemory *mem;
+        if (fd < 0)
+            mem = new CameraHeapMemory(buf_size, num_bufs);
+        else
+            mem = new CameraHeapMemory(fd, buf_size, num_bufs);
+        mem->incStrong(mem);
         return &mem->handle;
     }
 
+    static void __put_memory(camera_memory_t *data)
+    {
+        if (!data)
+            return;
+
+        CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle);
+        mem->decStrong(mem);
+    }
+
     static ANativeWindow *__to_anw(void *user)
     {
         CameraHardwareInterface *__this =
@@ -541,7 +593,7 @@
     static int __set_buffer_count(struct preview_stream_ops* w, int count)
     {
         ANativeWindow *a = anw(w);
-	return native_window_set_buffer_count(a, count);
+        return native_window_set_buffer_count(a, count);
     }
 
     static int __set_buffers_geometry(struct preview_stream_ops* w,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index c42e3ab..09b24a8 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -200,23 +200,6 @@
     return edgeFlags;
 }
 
-static void clampPositionUsingPointerBounds(
-        const sp<PointerControllerInterface>& pointerController, float* x, float* y) {
-    float minX, minY, maxX, maxY;
-    if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-        if (*x < minX) {
-            *x = minX;
-        } else if (*x > maxX) {
-            *x = maxX;
-        }
-        if (*y < minY) {
-            *y = minY;
-        } else if (*y > maxY) {
-            *y = maxY;
-        }
-    }
-}
-
 static float calculateCommonVector(float a, float b) {
     if (a > 0 && b > 0) {
         return a < b ? a : b;
@@ -787,8 +770,8 @@
             mConfig.pointerGestureTapSlop);
     dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
             mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
-    dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n",
-            mConfig.pointerGestureMultitouchMinSpeed);
+    dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+            mConfig.pointerGestureMultitouchMinDistance);
     dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
             mConfig.pointerGestureSwipeTransitionAngleCosine);
     dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
@@ -3509,11 +3492,18 @@
         cancelPreviousGesture = false;
     }
 
-    // Switch pointer presentation.
-    mPointerController->setPresentation(
-            mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
-                    ? PointerControllerInterface::PRESENTATION_SPOT
-                    : PointerControllerInterface::PRESENTATION_POINTER);
+    // Update the pointer presentation and spots.
+    if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+        if (finishPreviousGesture || cancelPreviousGesture) {
+            mPointerController->clearSpots();
+        }
+        mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+                mPointerGesture.currentGestureIdToIndex,
+                mPointerGesture.currentGestureIdBits);
+    } else {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+    }
 
     // Show or hide the pointer if needed.
     switch (mPointerGesture.currentGestureMode) {
@@ -3712,12 +3702,6 @@
                 mPointerGesture.currentGestureIdBits.clear();
 
                 mPointerGesture.pointerVelocityControl.reset();
-
-                if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                    mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-                    mPointerGesture.spotIdBits.clear();
-                    moveSpotsLocked();
-                }
                 return true;
             }
         }
@@ -3798,22 +3782,18 @@
     if (isQuietTime) {
         // Case 1: Quiet time. (QUIET)
 #if DEBUG_GESTURES
-        LOGD("Gestures: QUIET for next %0.3fms",
-                (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f);
+        LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
+                + mConfig->pointerGestureQuietInterval - when) * 0.000001f);
 #endif
-        *outFinishPreviousGesture = true;
+        if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+            *outFinishPreviousGesture = true;
+        }
 
         mPointerGesture.activeGestureId = -1;
         mPointerGesture.currentGestureMode = PointerGesture::QUIET;
         mPointerGesture.currentGestureIdBits.clear();
 
         mPointerGesture.pointerVelocityControl.reset();
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-            mPointerGesture.spotIdBits.clear();
-            moveSpotsLocked();
-        }
     } else if (isPointerDown(mCurrentTouch.buttonState)) {
         // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
         // The pointer follows the active touch point.
@@ -3899,32 +3879,11 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            if (activeTouchId >= 0) {
-                // Collapse all spots into one point at the pointer location.
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG;
-                mPointerGesture.spotIdBits.clear();
-                for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                    uint32_t id = mCurrentTouch.pointers[i].id;
-                    mPointerGesture.spotIdBits.markBit(id);
-                    mPointerGesture.spotIdToIndex[id] = i;
-                    mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0];
-                }
-            } else {
-                // No fingers.  Generate a spot at the pointer location so the
-                // anchor appears to be pressed.
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK;
-                mPointerGesture.spotIdBits.clear();
-                mPointerGesture.spotIdBits.markBit(0);
-                mPointerGesture.spotIdToIndex[0] = 0;
-                mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-            }
-            moveSpotsLocked();
-        }
     } else if (mCurrentTouch.pointerCount == 0) {
         // Case 3. No fingers down and button is not pressed. (NEUTRAL)
-        *outFinishPreviousGesture = true;
+        if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+            *outFinishPreviousGesture = true;
+        }
 
         // Watch for taps coming out of HOVER or TAP_DRAG mode.
         // Checking for taps after TAP_DRAG allows us to detect double-taps.
@@ -3965,15 +3924,6 @@
                     mPointerGesture.currentGestureCoords[0].setAxisValue(
                             AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-                    if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                        mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP;
-                        mPointerGesture.spotIdBits.clear();
-                        mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
-                        mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
-                        mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-                        moveSpotsLocked();
-                    }
-
                     tapped = true;
                 } else {
 #if DEBUG_GESTURES
@@ -3999,12 +3949,6 @@
             mPointerGesture.activeGestureId = -1;
             mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-                mPointerGesture.spotIdBits.clear();
-                moveSpotsLocked();
-            }
         }
     } else if (mCurrentTouch.pointerCount == 1) {
         // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
@@ -4067,7 +4011,9 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: HOVER");
 #endif
-            *outFinishPreviousGesture = true;
+            if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+                *outFinishPreviousGesture = true;
+            }
             mPointerGesture.activeGestureId = 0;
             down = false;
         }
@@ -4094,16 +4040,6 @@
             mPointerGesture.tapX = x;
             mPointerGesture.tapY = y;
         }
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG
-                    : PointerControllerInterface::SPOT_GESTURE_HOVER;
-            mPointerGesture.spotIdBits.clear();
-            mPointerGesture.spotIdBits.markBit(activeTouchId);
-            mPointerGesture.spotIdToIndex[activeTouchId] = 0;
-            mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-            moveSpotsLocked();
-        }
     } else {
         // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
         // We need to provide feedback for each finger that goes down so we cannot wait
@@ -4131,8 +4067,8 @@
             // Reset the gesture.
 #if DEBUG_GESTURES
             LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
-                    "settle time remaining %0.3fms",
-                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+                            + mConfig->pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
             *outCancelPreviousGesture = true;
@@ -4147,101 +4083,134 @@
             mPointerGesture.referenceIdBits.clear();
             mPointerGesture.pointerVelocityControl.reset();
 
-            if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
-                    && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
-                // The spot is already visible and has settled, use it as the reference point
-                // for the gesture.  Other spots will be positioned relative to this one.
+            // Use the centroid and pointer location as the reference points for the gesture.
 #if DEBUG_GESTURES
-                LOGD("Gestures: Using active spot as reference for MULTITOUCH, "
-                        "settle time expired %0.3fms ago",
-                        (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL)
-                                * 0.000001f);
+            LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+                            + mConfig->pointerGestureMultitouchSettleInterval - when)
+                            * 0.000001f);
 #endif
-                const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[
-                        mPointerGesture.activeTouchId]];
-                mPointerGesture.referenceTouchX = d.x;
-                mPointerGesture.referenceTouchY = d.y;
-                const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[
-                        mPointerGesture.activeTouchId]];
-                mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
-                mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+            mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+                    &mPointerGesture.referenceTouchY);
+            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+                    &mPointerGesture.referenceGestureY);
+        }
+
+        // Clear the reference deltas for fingers not yet included in the reference calculation.
+        for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
+                !idBits.isEmpty(); ) {
+            uint32_t id = idBits.firstMarkedBit();
+            idBits.clearBit(id);
+
+            mPointerGesture.referenceDeltas[id].dx = 0;
+            mPointerGesture.referenceDeltas[id].dy = 0;
+        }
+        mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
+
+        // Add delta for all fingers and calculate a common movement delta.
+        float commonDeltaX = 0, commonDeltaY = 0;
+        BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+        for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
+            bool first = (idBits == commonIdBits);
+            uint32_t id = idBits.firstMarkedBit();
+            idBits.clearBit(id);
+
+            const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
+            const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+            PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+            delta.dx += cpd.x - lpd.x;
+            delta.dy += cpd.y - lpd.y;
+
+            if (first) {
+                commonDeltaX = delta.dx;
+                commonDeltaY = delta.dy;
             } else {
-                // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
-                LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
-                        "settle time remaining %0.3fms",
-                        (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
-                                * 0.000001f);
-#endif
-                mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
-                        &mPointerGesture.referenceTouchY);
-                mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                        &mPointerGesture.referenceGestureY);
+                commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+                commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
             }
         }
 
+        // Consider transitions from PRESS to SWIPE or MULTITOUCH.
         if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
-            float d;
-            if (mCurrentTouch.pointerCount > 2) {
-                // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
-                LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
-                        mCurrentTouch.pointerCount);
-#endif
-                *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-            } else if (((d = distance(
-                    mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
-                    mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
-                            > mLocked.pointerGestureMaxSwipeWidth)) {
-                // There are two pointers but they are too far apart, switch to FREEFORM.
-#if DEBUG_GESTURES
-                LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
-                        d, mLocked.pointerGestureMaxSwipeWidth);
-#endif
-                *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-            } else {
-                // There are two pointers.  Wait for both pointers to start moving
-                // before deciding whether this is a SWIPE or FREEFORM gesture.
-                uint32_t id1 = mCurrentTouch.pointers[0].id;
-                uint32_t id2 = mCurrentTouch.pointers[1].id;
+            float dist[MAX_POINTER_ID + 1];
+            int32_t distOverThreshold = 0;
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
+                uint32_t id = idBits.firstMarkedBit();
+                idBits.clearBit(id);
 
-                float vx1, vy1, vx2, vy2;
-                mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1);
-                mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2);
+                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+                dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
+                        delta.dy * mLocked.pointerGestureYZoomScale);
+                if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) {
+                    distOverThreshold += 1;
+                }
+            }
 
-                float speed1 = hypotf(vx1, vy1);
-                float speed2 = hypotf(vx2, vy2);
-                if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed
-                        && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) {
-                    // Calculate the dot product of the velocity vectors.
-                    // When the vectors are oriented in approximately the same direction,
-                    // the angle betweeen them is near zero and the cosine of the angle
-                    // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
-                    float dot = vx1 * vx2 + vy1 * vy2;
-                    float cosine = dot / (speed1 * speed2); // denominator always > 0
-                    if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
-                        // Pointers are moving in the same direction.  Switch to SWIPE.
+            // Only transition when at least two pointers have moved further than
+            // the minimum distance threshold.
+            if (distOverThreshold >= 2) {
+                float d;
+                if (mCurrentTouch.pointerCount > 2) {
+                    // There are more than two pointers, switch to FREEFORM.
 #if DEBUG_GESTURES
-                        LOGD("Gestures: PRESS transitioned to SWIPE, "
-                                "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
-                                "cosine %0.3f >= %0.3f",
-                                speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
-                                cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+                    LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+                            mCurrentTouch.pointerCount);
 #endif
-                        mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
-                    } else {
-                        // Pointers are moving in different directions.  Switch to FREEFORM.
+                    *outCancelPreviousGesture = true;
+                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                } else if (((d = distance(
+                        mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
+                        mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
+                                > mLocked.pointerGestureMaxSwipeWidth)) {
+                    // There are two pointers but they are too far apart for a SWIPE,
+                    // switch to FREEFORM.
 #if DEBUG_GESTURES
-                        LOGD("Gestures: PRESS transitioned to FREEFORM, "
-                                "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
-                                "cosine %0.3f < %0.3f",
-                                speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
-                                cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+                    LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+                            d, mLocked.pointerGestureMaxSwipeWidth);
 #endif
-                        *outCancelPreviousGesture = true;
-                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                    *outCancelPreviousGesture = true;
+                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                } else {
+                    // There are two pointers.  Wait for both pointers to start moving
+                    // before deciding whether this is a SWIPE or FREEFORM gesture.
+                    uint32_t id1 = mCurrentTouch.pointers[0].id;
+                    uint32_t id2 = mCurrentTouch.pointers[1].id;
+                    float dist1 = dist[id1];
+                    float dist2 = dist[id2];
+                    if (dist1 >= mConfig->pointerGestureMultitouchMinDistance
+                            && dist2 >= mConfig->pointerGestureMultitouchMinDistance) {
+                        // Calculate the dot product of the displacement vectors.
+                        // When the vectors are oriented in approximately the same direction,
+                        // the angle betweeen them is near zero and the cosine of the angle
+                        // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+                        PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+                        PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+                        float dot = delta1.dx * delta2.dx + delta1.dy * delta2.dy;
+                        float cosine = dot / (dist1 * dist2); // denominator always > 0
+                        if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
+                            // Pointers are moving in the same direction.  Switch to SWIPE.
+#if DEBUG_GESTURES
+                            LOGD("Gestures: PRESS transitioned to SWIPE, "
+                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                    "cosine %0.3f >= %0.3f",
+                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+                            mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+                        } else {
+                            // Pointers are moving in different directions.  Switch to FREEFORM.
+#if DEBUG_GESTURES
+                            LOGD("Gestures: PRESS transitioned to FREEFORM, "
+                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                    "cosine %0.3f < %0.3f",
+                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+                            *outCancelPreviousGesture = true;
+                            mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                        }
                     }
                 }
             }
@@ -4258,67 +4227,28 @@
             }
         }
 
-        // Clear the reference deltas for fingers not yet included in the reference calculation.
-        for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
-                !idBits.isEmpty(); ) {
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
-
-            mPointerGesture.referenceDeltas[id].dx = 0;
-            mPointerGesture.referenceDeltas[id].dy = 0;
-        }
-        mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
-
-        // Move the reference points based on the overall group motion of the fingers.
-        // The objective is to calculate a vector delta that is common to the movement
-        // of all fingers.
-        BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
-        if (!commonIdBits.isEmpty()) {
-            float commonDeltaX = 0, commonDeltaY = 0;
-            for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
-                bool first = (idBits == commonIdBits);
+        // Move the reference points based on the overall group motion of the fingers
+        // except in PRESS mode while waiting for a transition to occur.
+        if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
+                && (commonDeltaX || commonDeltaY)) {
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
                 uint32_t id = idBits.firstMarkedBit();
                 idBits.clearBit(id);
 
-                const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
-                const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                delta.dx += cpd.x - lpd.x;
-                delta.dy += cpd.y - lpd.y;
-
-                if (first) {
-                    commonDeltaX = delta.dx;
-                    commonDeltaY = delta.dy;
-                } else {
-                    commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
-                    commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
-                }
+                delta.dx = 0;
+                delta.dy = 0;
             }
 
-            if (commonDeltaX || commonDeltaY) {
-                for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
-                    uint32_t id = idBits.firstMarkedBit();
-                    idBits.clearBit(id);
+            mPointerGesture.referenceTouchX += commonDeltaX;
+            mPointerGesture.referenceTouchY += commonDeltaY;
 
-                    PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                    delta.dx = 0;
-                    delta.dy = 0;
-                }
+            commonDeltaX *= mLocked.pointerGestureXMovementScale;
+            commonDeltaY *= mLocked.pointerGestureYMovementScale;
+            mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
 
-                mPointerGesture.referenceTouchX += commonDeltaX;
-                mPointerGesture.referenceTouchY += commonDeltaY;
-
-                commonDeltaX *= mLocked.pointerGestureXMovementScale;
-                commonDeltaY *= mLocked.pointerGestureYMovementScale;
-                mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
-                mPointerGesture.referenceGestureX += commonDeltaX;
-                mPointerGesture.referenceGestureY += commonDeltaY;
-
-                clampPositionUsingPointerBounds(mPointerController,
-                        &mPointerGesture.referenceGestureX,
-                        &mPointerGesture.referenceGestureY);
-            }
+            mPointerGesture.referenceGestureX += commonDeltaX;
+            mPointerGesture.referenceGestureY += commonDeltaY;
         }
 
         // Report gestures.
@@ -4344,10 +4274,6 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS;
-            }
         } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
             // SWIPE mode.
 #if DEBUG_GESTURES
@@ -4370,10 +4296,6 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE;
-            }
         } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
             // FREEFORM mode.
 #if DEBUG_GESTURES
@@ -4475,33 +4397,6 @@
                         "activeGestureId=%d", mPointerGesture.activeGestureId);
 #endif
             }
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM;
-            }
-        }
-
-        // Update spot locations for PRESS, SWIPE and FREEFORM.
-        // We use the same calculation as we do to calculate the gesture pointers
-        // for FREEFORM so that the spots smoothly track gestures.
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotIdBits.clear();
-            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                uint32_t id = mCurrentTouch.pointers[i].id;
-                mPointerGesture.spotIdBits.markBit(id);
-                mPointerGesture.spotIdToIndex[id] = i;
-
-                float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
-                        * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX;
-                float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
-                        * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY;
-
-                mPointerGesture.spotCoords[i].clear();
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-            }
-            moveSpotsLocked();
         }
     }
 
@@ -4544,11 +4439,6 @@
     return true;
 }
 
-void TouchInputMapper::moveSpotsLocked() {
-    mPointerController->setSpots(mPointerGesture.spotGesture,
-            mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits);
-}
-
 void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
         int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
         const PointerProperties* properties, const PointerCoords* coords,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 1d4ad87..36cd89c 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -101,8 +101,8 @@
     nsecs_t pointerGestureMultitouchSettleInterval;
 
     // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
-    // both of the pointers are moving at least this fast.
-    float pointerGestureMultitouchMinSpeed; // in pixels per second
+    // at least two pointers have moved at least this far from their starting place.
+    float pointerGestureMultitouchMinDistance; // in pixels
 
     // The transition from PRESS to SWIPE gesture mode can only occur when the
     // cosine of the angle between the two vectors is greater than or equal to than this value
@@ -134,7 +134,7 @@
             filterTouchEvents(false),
             filterJumpyTouchEvents(false),
             virtualKeyQuietTime(0),
-            pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
+            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
             pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
@@ -142,10 +142,10 @@
             pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
             pointerGestureTapSlop(10.0f), // 10 pixels
             pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
-            pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
+            pointerGestureMultitouchMinDistance(15), // 15 pixels
             pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
-            pointerGestureSwipeMaxWidthRatio(0.333f),
-            pointerGestureMovementSpeedRatio(0.3f),
+            pointerGestureSwipeMaxWidthRatio(0.25f),
+            pointerGestureMovementSpeedRatio(0.8f),
             pointerGestureZoomSpeedRatio(0.3f) { }
 };
 
@@ -1140,12 +1140,6 @@
         PointerProperties lastGestureProperties[MAX_POINTERS];
         PointerCoords lastGestureCoords[MAX_POINTERS];
 
-        // Pointer coords and ids for the current spots.
-        PointerControllerInterface::SpotGesture spotGesture;
-        BitSet32 spotIdBits; // same set of ids as touch ids
-        uint32_t spotIdToIndex[MAX_POINTER_ID + 1];
-        PointerCoords spotCoords[MAX_POINTERS];
-
         // Time the pointer gesture last went down.
         nsecs_t downTime;
 
@@ -1192,8 +1186,6 @@
             currentGestureIdBits.clear();
             lastGestureMode = NEUTRAL;
             lastGestureIdBits.clear();
-            spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-            spotIdBits.clear();
             downTime = 0;
             velocityTracker.clear();
             resetTap();
@@ -1219,7 +1211,6 @@
     void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
     bool preparePointerGestures(nsecs_t when,
             bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
-    void moveSpotsLocked();
 
     // Dispatches a motion event.
     // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index c18ebcf..12c7cba 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -240,15 +240,15 @@
     }
 }
 
-void PointerController::setSpots(SpotGesture spotGesture,
-        const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+void PointerController::setSpots(const PointerCoords* spotCoords,
+        const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
 #if DEBUG_POINTER_UPDATES
-    LOGD("setSpots: spotGesture=%d", spotGesture);
+    LOGD("setSpots: idBits=%08x", spotIdBits.value);
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
         uint32_t id = idBits.firstMarkedBit();
         idBits.clearBit(id);
         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        LOGD("  spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+        LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
                 c.getAxisValue(AMOTION_EVENT_AXIS_X),
                 c.getAxisValue(AMOTION_EVENT_AXIS_Y),
                 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index 1c21db1..700ef72 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -90,38 +90,6 @@
     /* Sets the mode of the pointer controller. */
     virtual void setPresentation(Presentation presentation) = 0;
 
-    // Describes the current gesture.
-    enum SpotGesture {
-        // No gesture.
-        // Do not display any spots.
-        SPOT_GESTURE_NEUTRAL,
-        // Tap at current location.
-        // Briefly display one spot at the tapped location.
-        SPOT_GESTURE_TAP,
-        // Drag at current location.
-        // Display spot at pressed location.
-        SPOT_GESTURE_DRAG,
-        // Button pressed but no finger is down.
-        // Display spot at pressed location.
-        SPOT_GESTURE_BUTTON_CLICK,
-        // Button pressed and a finger is down.
-        // Display spot at pressed location.
-        SPOT_GESTURE_BUTTON_DRAG,
-        // One finger down and hovering.
-        // Display spot at the hovered location.
-        SPOT_GESTURE_HOVER,
-        // Two fingers down but not sure in which direction they are moving so we consider
-        // it a press at the pointer location.
-        // Display two spots near the pointer location.
-        SPOT_GESTURE_PRESS,
-        // Two fingers down and moving in same direction.
-        // Display two spots near the pointer location.
-        SPOT_GESTURE_SWIPE,
-        // Two or more fingers down and moving in arbitrary directions.
-        // Display two or more spots near the pointer location, one for each finger.
-        SPOT_GESTURE_FREEFORM,
-    };
-
     /* Sets the spots for the current gesture.
      * The spots are not subject to the inactivity timeout like the pointer
      * itself it since they are expected to remain visible for so long as
@@ -131,8 +99,7 @@
      * For spotCoords, pressure != 0 indicates that the spot's location is being
      * pressed (not hovering).
      */
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+    virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
             BitSet32 spotIdBits) = 0;
 
     /* Removes all spots. */
@@ -198,8 +165,8 @@
     virtual void unfade(Transition transition);
 
     virtual void setPresentation(Presentation presentation);
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+    virtual void setSpots(const PointerCoords* spotCoords,
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
     virtual void clearSpots();
 
     void setDisplaySize(int32_t width, int32_t height);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 00b4222..d04c9e7 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -101,8 +101,8 @@
     virtual void setPresentation(Presentation presentation) {
     }
 
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+    virtual void setSpots(const PointerCoords* spotCoords,
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
     }
 
     virtual void clearSpots() {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7c6d3c1..c28732e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -134,6 +134,7 @@
     // Timeout intervals for agent backup & restore operations
     static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
+    static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
 
     // User confirmation timeout for a full backup/restore operation
@@ -1691,7 +1692,7 @@
         public void run() {
             final List<PackageInfo> packagesToBackup;
 
-            Slog.i(TAG, "--- Performing full-dataset restore ---");
+            Slog.i(TAG, "--- Performing full-dataset backup ---");
             sendStartBackup();
 
             // doAllApps supersedes the package set if any
@@ -1720,64 +1721,23 @@
                 }
             }
 
-            // Now back up the app data via the agent mechanism
             PackageInfo pkg = null;
             try {
+                // Now back up the app data via the agent mechanism
                 int N = packagesToBackup.size();
                 for (int i = 0; i < N; i++) {
                     pkg = packagesToBackup.get(i);
+                    backupOnePackage(pkg);
+                }
 
-                    Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
-
-                    IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
-                            IApplicationThread.BACKUP_MODE_FULL);
-                    if (agent != null) {
-                        try {
-                            ApplicationInfo app = pkg.applicationInfo;
-                            boolean sendApk = mIncludeApks
-                                    && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
-                                    && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
-                                        (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
-
-                            sendOnBackupPackage(pkg.packageName);
-
-                            {
-                                BackupDataOutput output = new BackupDataOutput(
-                                        mOutputFile.getFileDescriptor());
-
-                                if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
-                                writeAppManifest(pkg, mManifestFile, sendApk);
-                                FullBackup.backupToTar(pkg.packageName, null, null,
-                                        mFilesDir.getAbsolutePath(),
-                                        mManifestFile.getAbsolutePath(),
-                                        output);
-                            }
-
-                            if (DEBUG) Slog.d(TAG, "Calling doBackup()");
-                            final int token = generateToken();
-                            prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
-                            agent.doBackup(null, mOutputFile, null, sendApk,
-                                    token, mBackupManagerBinder);
-                            boolean success = waitUntilOperationComplete(token);
-                            if (!success) {
-                                Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
-                            } else {
-                                if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
-                            }
-                        } catch (IOException e) {
-                            Slog.e(TAG, "Error backing up " + pkg.packageName, e);
-                        }
-                    } else {
-                        Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
-                    }
-                    tearDown(pkg);
+                // Finally, shared storage if requested
+                if (mIncludeShared) {
+                    backupSharedStorage();
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "App died during full backup");
             } finally {
-                if (pkg != null) {
-                    tearDown(pkg);
-                }
+                tearDown(pkg);
                 try {
                     mOutputFile.close();
                 } catch (IOException e) {
@@ -1796,6 +1756,79 @@
             }
         }
 
+        private void backupOnePackage(PackageInfo pkg) throws RemoteException {
+            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+            IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+                    IApplicationThread.BACKUP_MODE_FULL);
+            if (agent != null) {
+                try {
+                    ApplicationInfo app = pkg.applicationInfo;
+                    boolean sendApk = mIncludeApks
+                            && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+                                (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+                    sendOnBackupPackage(pkg.packageName);
+
+                    {
+                        BackupDataOutput output = new BackupDataOutput(
+                                mOutputFile.getFileDescriptor());
+
+                        if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+                        writeAppManifest(pkg, mManifestFile, sendApk);
+                        FullBackup.backupToTar(pkg.packageName, null, null,
+                                mFilesDir.getAbsolutePath(),
+                                mManifestFile.getAbsolutePath(),
+                                output);
+                    }
+
+                    if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+                    final int token = generateToken();
+                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+                    agent.doBackup(null, mOutputFile, null, sendApk,
+                            token, mBackupManagerBinder);
+                    if (!waitUntilOperationComplete(token)) {
+                        Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+                    }
+                } catch (IOException e) {
+                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+                }
+            } else {
+                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+            }
+            tearDown(pkg);
+        }
+
+        private void backupSharedStorage() throws RemoteException {
+            PackageInfo pkg = null;
+            try {
+                pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
+                IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+                        IApplicationThread.BACKUP_MODE_FULL);
+                if (agent != null) {
+                    sendOnBackupPackage("Shared storage");
+
+                    final int token = generateToken();
+                    prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
+                    agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
+                    if (!waitUntilOperationComplete(token)) {
+                        Slog.e(TAG, "Full backup failed on shared storage");
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
+                    }
+                } else {
+                    Slog.e(TAG, "Could not bind to shared storage backup agent");
+                }
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "Shared storage backup package not found");
+            } finally {
+                tearDown(pkg);
+            }
+        }
+
         private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
                 throws IOException {
             // Manifest format. All data are strings ending in LF:
@@ -1836,23 +1869,24 @@
         }
 
         private void tearDown(PackageInfo pkg) {
-            final ApplicationInfo app = pkg.applicationInfo;
-            try {
-                // unbind and tidy up even on timeout or failure, just in case
-                mActivityManager.unbindBackupAgent(app);
+            if (pkg != null) {
+                final ApplicationInfo app = pkg.applicationInfo;
+                if (app != null) {
+                    try {
+                        // unbind and tidy up even on timeout or failure, just in case
+                        mActivityManager.unbindBackupAgent(app);
 
-                // The agent was running with a stub Application object, so shut it down.
-                // !!! We hardcode the confirmation UI's package name here rather than use a
-                //     manifest flag!  TODO something less direct.
-                if (app.uid != Process.SYSTEM_UID
-                        && !pkg.packageName.equals("com.android.backupconfirm")) {
-                    if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
-                    mActivityManager.killApplicationProcess(app.processName, app.uid);
-                } else {
-                    if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+                        // The agent was running with a stub Application object, so shut it down.
+                        if (app.uid != Process.SYSTEM_UID) {
+                            if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+                            mActivityManager.killApplicationProcess(app.processName, app.uid);
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.d(TAG, "Lost app trying to shut down");
+                    }
                 }
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Lost app trying to shut down");
             }
         }
 
@@ -1949,6 +1983,7 @@
             // with a whitelist of packages known to be unclearable.
             mClearedPackages.add("android");
             mClearedPackages.add("com.android.providers.settings");
+
         }
 
         class RestoreFileRunnable implements Runnable {
@@ -1988,6 +2023,11 @@
             Slog.i(TAG, "--- Performing full-dataset restore ---");
             sendStartRestore();
 
+            // Are we able to restore shared-storage data?
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
+            }
+
             try {
                 byte[] buffer = new byte[32 * 1024];
                 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
@@ -2707,7 +2747,9 @@
                         info.path, 0, FullBackup.SHARED_PREFIX.length())) {
                     // File in shared storage.  !!! TODO: implement this.
                     info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+                    info.packageName = "com.android.sharedstoragebackup";
                     info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+                    if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
                 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
                         info.path, 0, FullBackup.APPS_PREFIX.length())) {
                     // App content!  Parse out the package name and domain
@@ -2817,6 +2859,7 @@
             final int end = offset + maxChars;
             for (int i = offset; i < end; i++) {
                 final byte b = data[i];
+                // Numeric fields in tar can terminate with either NUL or SPC
                 if (b == 0 || b == ' ') break;
                 if (b < '0' || b > ('0' + radix - 1)) {
                     throw new IOException("Invalid number in header");
@@ -2829,8 +2872,8 @@
         String extractString(byte[] data, int offset, int maxChars) throws IOException {
             final int end = offset + maxChars;
             int eos = offset;
-            // tar string fields can end with either NUL or SPC
-            while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
+            // tar string fields terminate early with a NUL
+            while (eos < end && data[eos] != 0) eos++;
             return new String(data, offset, eos-offset, "US-ASCII");
         }
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index dd76eb8..e3d4c45 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -38,6 +39,7 @@
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkState;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
@@ -65,6 +67,7 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
+import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -458,6 +461,12 @@
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceRegexs().length != 0);
 
+        try {
+            nmService.registerObserver(mTethering);
+        } catch (RemoteException e) {
+            loge("Error registering observer :" + e);
+        }
+
         if (DBG) {
             mInetLog = new ArrayList();
         }
@@ -557,25 +566,32 @@
      */
     private boolean isNetworkBlocked(NetworkInfo info, int uid) {
         synchronized (mUidRules) {
-            return isNetworkBlockedLocked(info, uid);
+            // TODO: expand definition of "paid" network to cover tethered or
+            // paid hotspot use cases.
+            final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+            final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+            if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+                return true;
+            }
+
+            // no restrictive rules; network is visible
+            return false;
         }
     }
 
     /**
-     * Check if UID is blocked from using the given {@link NetworkInfo}.
+     * Return a filtered version of the given {@link NetworkInfo}, potentially
+     * marked {@link DetailedState#BLOCKED} based on
+     * {@link #isNetworkBlocked(NetworkInfo, int)}.
      */
-    private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
-        // TODO: expand definition of "paid" network to cover tethered or paid
-        // hotspot use cases.
-        final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
-        final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
-
-        if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
-            return true;
+    private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
+        if (isNetworkBlocked(info, uid)) {
+            // network is blocked; clone and override state
+            info = new NetworkInfo(info);
+            info.setDetailedState(DetailedState.BLOCKED, null, null);
         }
-
-        // no restrictive rules; network is visible
-        return false;
+        return info;
     }
 
     /**
@@ -610,12 +626,7 @@
         if (isNetworkTypeValid(networkType)) {
             final NetworkStateTracker tracker = mNetTrackers[networkType];
             if (tracker != null) {
-                info = tracker.getNetworkInfo();
-                if (isNetworkBlocked(info, uid)) {
-                    // network is blocked; clone and override state
-                    info = new NetworkInfo(info);
-                    info.setDetailedState(DetailedState.BLOCKED, null, null);
-                }
+                info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
             }
         }
         return info;
@@ -625,22 +636,15 @@
     public NetworkInfo[] getAllNetworkInfo() {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
-        final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
-        int i = 0;
+        final ArrayList<NetworkInfo> result = Lists.newArrayList();
         synchronized (mUidRules) {
             for (NetworkStateTracker tracker : mNetTrackers) {
                 if (tracker != null) {
-                    NetworkInfo info = tracker.getNetworkInfo();
-                    if (isNetworkBlockedLocked(info, uid)) {
-                        // network is blocked; clone and override state
-                        info = new NetworkInfo(info);
-                        info.setDetailedState(DetailedState.BLOCKED, null, null);
-                    }
-                    result[i++] = info;
+                    result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
                 }
             }
         }
-        return result;
+        return result.toArray(new NetworkInfo[result.size()]);
     }
 
     /**
@@ -668,6 +672,23 @@
         return null;
     }
 
+    @Override
+    public NetworkState[] getAllNetworkState() {
+        enforceAccessPermission();
+        final int uid = Binder.getCallingUid();
+        final ArrayList<NetworkState> result = Lists.newArrayList();
+        synchronized (mUidRules) {
+            for (NetworkStateTracker tracker : mNetTrackers) {
+                if (tracker != null) {
+                    final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+                    result.add(new NetworkState(
+                            info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+                }
+            }
+        }
+        return result.toArray(new NetworkState[result.size()]);
+    }
+
     public boolean setRadios(boolean turnOn) {
         boolean result = true;
         enforceChangePermission();
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f78dca9..c86f962 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1121,16 +1121,20 @@
                             com.android.internal.R.styleable.Storage_emulated, false);
                     int mtpReserve = a.getInt(
                             com.android.internal.R.styleable.Storage_mtpReserve, 0);
+                    boolean allowMassStorage = a.getBoolean(
+                            com.android.internal.R.styleable.Storage_allowMassStorage, false);
 
                     Slog.d(TAG, "got storage path: " + path + " description: " + description +
                             " primary: " + primary + " removable: " + removable +
-                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve);
+                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
+                            " allowMassStorage: " + allowMassStorage);
                     if (path == null || description == null) {
                         Slog.e(TAG, "path or description is null in readStorageList");
                     } else {
                         String pathString = path.toString();
                         StorageVolume volume = new StorageVolume(pathString,
-                                description.toString(), removable, emulated, mtpReserve);
+                                description.toString(), removable, emulated,
+                                mtpReserve, allowMassStorage);
                         if (primary) {
                             if (mPrimaryVolume == null) {
                                 mPrimaryVolume = volume;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 99c8af6..08c6699 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -138,7 +138,7 @@
             if (outFile == null) {
                 Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
             }
-            FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+            FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
 
             if (restoredWallpaper) {
                 WallpaperManagerService wallpaper =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 596cbac..fd03e5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -305,6 +305,7 @@
                 Slog.i(TAG, "Connectivity Service");
                 connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
                 ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+                networkStats.bindConnectivityManager(connectivity);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting Connectivity Service", e);
             }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 41ec63a..4e2501b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -321,6 +321,13 @@
                     }
                     break;
                 }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
+                    mWifiStateMachineChannel = null;
+                    //Re-establish connection to state machine
+                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
+                    break;
+                }
                 default: {
                     Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
                     break;
@@ -593,7 +600,12 @@
      */
     public WifiConfiguration getWifiApConfiguration() {
         enforceAccessPermission();
-        return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        if (mWifiStateMachineChannel != null) {
+            return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        } else {
+            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+            return null;
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 6ef6963..6c7895e 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -36,16 +36,21 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import java.io.BufferedInputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
+import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.net.UnknownHostException;
+import java.net.URL;
 import java.util.Collection;
 import java.util.List;
 import java.util.Random;
+import java.util.Scanner;
 
 /**
  * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
@@ -195,6 +200,34 @@
         return Settings.Secure.getInt(mContentResolver,
             Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250);
     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
+     */
+     private Boolean isWalledGardenTestEnabled() {
+        return Settings.Secure.getInt(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
+     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
+     */
+     private String getWalledGardenUrl() {
+        String url = Settings.Secure.getString(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
+        if (TextUtils.isEmpty(url)) return "http://www.google.com/";
+        return url;
+     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
+     */
+     private String getWalledGardenPattern() {
+        String pattern = Settings.Secure.getString(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
+        if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>";
+        return pattern;
+     }
     
     /**
      * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE
@@ -509,7 +542,7 @@
             // This access point does not require a watchdog, so queue idle on the main thread
             mHandler.idle();
         }
-        mHandler.checkWalledGarden(ssid);
+        if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid);
     }
     
     /**
@@ -1118,19 +1151,35 @@
             }
         }
 
+        /**
+         * DNS based detection techniques do not work at all hotspots. The one sure way to check
+         * a walled garden is to see if a URL fetch on a known address fetches the data we
+         * expect
+         */
         private boolean isWalledGardenConnection() {
-            //One way to detect a walled garden is to see if multiple DNS queries
-            //resolve to the same IP address
+            InputStream in = null;
+            HttpURLConnection  urlConnection = null;
             try {
-                String host1 = "www.google.com";
-                String host2 = "www.android.com";
-                String address1 = InetAddress.getByName(host1).getHostAddress();
-                String address2 = InetAddress.getByName(host2).getHostAddress();
-                if (address1.equals(address2)) return true;
-            } catch (UnknownHostException e) {
+                URL url = new URL(getWalledGardenUrl());
+                urlConnection = (HttpURLConnection) url.openConnection();
+                in = new BufferedInputStream(urlConnection.getInputStream());
+                Scanner scanner = new Scanner(in);
+                if (scanner.findInLine(getWalledGardenPattern()) != null) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } catch (IOException e) {
                 return false;
+            } finally {
+                if (in != null) {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                    }
+                }
+                if (urlConnection != null) urlConnection.disconnect();
             }
-            return false;
         }
 
         private void handleWalledGardenCheck(String ssid) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index fba293c..86671d6 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,7 @@
 
 package com.android.server.accessibility;
 
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.HandlerCaller.SomeArgs;
-import com.android.server.wm.WindowManagerService;
-
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -37,7 +31,6 @@
 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.net.Uri;
 import android.os.Binder;
@@ -46,15 +39,27 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.IWindow;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -64,6 +69,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * This class is instantiated by the system as a system level service and can be
@@ -80,12 +86,17 @@
 
     private static final String LOG_TAG = "AccessibilityManagerService";
 
+    private static final String FUNCTION_REGISTER_EVENT_LISTENER =
+        "registerEventListener";
+
     private static int sIdCounter = 0;
 
     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
 
     private static final int DO_SET_SERVICE_INFO = 10;
 
+    private static int sNextWindowId;
+
     final HandlerCaller mCaller;
 
     final Context mContext;
@@ -103,6 +114,11 @@
 
     private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
 
+    private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap =
+        new SparseArray<IAccessibilityInteractionConnection>();
+
+    private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
+
     private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
 
     private PackageManager mPackageManager;
@@ -117,6 +133,10 @@
 
     private boolean mHasInputFilter;
 
+    private final WindowManagerService mWindowManagerService;
+
+    private final SecurityPolicy mSecurityPolicy;
+
     /**
      * Handler for delayed event dispatch.
      */
@@ -145,6 +165,9 @@
         mContext = context;
         mPackageManager = mContext.getPackageManager();
         mCaller = new HandlerCaller(context, this);
+        mWindowManagerService = (WindowManagerService) ServiceManager.getService(
+                Context.WINDOW_SERVICE);
+        mSecurityPolicy = new SecurityPolicy();
 
         registerPackageChangeAndBootCompletedBroadcastReceiver();
         registerSettingsContentObservers();
@@ -208,7 +231,6 @@
                 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
                     synchronized (mLock) {
                         populateAccessibilityServiceListLocked();
-
                         // get the accessibility enabled setting on boot
                         mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
@@ -223,7 +245,7 @@
                     
                     return;
                 }
-                
+
                 super.onReceive(context, intent);
             }
         };
@@ -297,6 +319,7 @@
 
     public boolean sendAccessibilityEvent(AccessibilityEvent event) {
         synchronized (mLock) {
+            mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
             notifyAccessibilityServicesDelayedLocked(event, false);
             notifyAccessibilityServicesDelayedLocked(event, true);
         }
@@ -352,7 +375,7 @@
 
     public void executeMessage(Message message) {
         switch (message.what) {
-            case DO_SET_SERVICE_INFO:
+            case DO_SET_SERVICE_INFO: {
                 SomeArgs arguments = ((SomeArgs) message.obj);
 
                 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
@@ -365,17 +388,73 @@
                     AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
                     if (oldInfo != null) {
                         oldInfo.updateDynamicallyConfigurableProperties(info);
-                        service.setAccessibilityServiceInfo(oldInfo);
+                        service.setDynamicallyConfigurableProperties(oldInfo);
                     } else {
-                        service.setAccessibilityServiceInfo(info);
+                        service.setDynamicallyConfigurableProperties(info);
                     }
                 }
-                return;
+            } return;
             default:
                 Slog.w(LOG_TAG, "Unknown message type: " + message.what);
         }
     }
 
+    public int addAccessibilityInteractionConnection(IWindow windowToken,
+            IAccessibilityInteractionConnection connection) throws RemoteException {
+        synchronized (mLock) {
+            final IWindow addedWindowToken = windowToken;
+            final int windowId = sNextWindowId++;
+            connection.asBinder().linkToDeath(new DeathRecipient() {
+                public void binderDied() {
+                    synchronized (mLock) {
+                        removeAccessibilityInteractionConnection(addedWindowToken);
+                    }
+                }
+            }, 0);
+            mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
+            mWindowIdToInteractionConnectionMap.put(windowId, connection);
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
+            }
+            return windowId;
+        }
+    }
+
+    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+        synchronized (mLock) {
+            final int count = mWindowIdToWindowTokenMap.size();
+            for (int i = 0; i < count; i++) {
+                if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
+                    final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
+                    mWindowIdToWindowTokenMap.remove(windowId);
+                    mWindowIdToInteractionConnectionMap.remove(windowId);
+                    if (DEBUG) {
+                        Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
+                FUNCTION_REGISTER_EVENT_LISTENER);
+        ComponentName componentName = new ComponentName("foo.bar", "FakeAccessibilityService");
+        synchronized (mLock) {
+            Service oldService = mComponentNameToServiceMap.get(componentName);
+            if (oldService != null) {
+                tryRemoveServiceLocked(oldService);
+            }
+        }
+        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
+        accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+        accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
+        Service service = new Service(componentName, accessibilityServiceInfo, true);
+        service.onServiceConnected(componentName, listener.asBinder());
+        return service;
+    }
+
     /**
      * Populates the cached list of installed {@link AccessibilityService}s.
      */
@@ -488,6 +567,12 @@
         AccessibilityEvent event = service.mPendingEvents.get(eventType);
 
         try {
+            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
+                event.setConnection(service);
+            } else {
+                event.setSource(null);
+            }
+            event.setSealed(true);
             listener.onAccessibilityEvent(event);
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
@@ -646,7 +731,7 @@
             if (isEnabled) {
                 if (enabledServices.contains(componentName)) {
                     if (service == null) {
-                        service = new Service(installedService);
+                        service = new Service(componentName, installedService, false);
                     }
                     service.bind();
                 } else if (!enabledServices.contains(componentName)) {
@@ -683,8 +768,6 @@
      * spoken feedback.
      */
     private void updateInputFilterLocked() {
-        WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
-                Context.WINDOW_SERVICE);
         if (mIsEnabled) {
             final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
@@ -695,19 +778,14 @@
                 if (mInputFilter == null) {
                     mInputFilter = new AccessibilityInputFilter(mContext);
                 }
-                wm.setInputFilter(mInputFilter);
+                mWindowManagerService.setInputFilter(mInputFilter);
                 mHasInputFilter = true;
-            } else {
-                if (mHasInputFilter) {
-                    wm.setInputFilter(null);
-                    mHasInputFilter = false;
-                }
+                return;
             }
-        } else {
-            if (mHasInputFilter) {
-                wm.setInputFilter(null);
-                mHasInputFilter = false;
-            }
+        }
+        if (mHasInputFilter) {
+            mWindowManagerService.setInputFilter(null);
+            mHasInputFilter = false;
         }
     }
 
@@ -743,24 +821,38 @@
 
         Intent mIntent;
 
+        boolean mCanRetrieveScreenContent;
+
+        boolean mIsFake;
+
+        final Callback mCallback = new Callback();
+
+        final AtomicInteger mInteractionIdCounter = new AtomicInteger();
+
         // the events pending events to be dispatched to this service
         final SparseArray<AccessibilityEvent> mPendingEvents =
             new SparseArray<AccessibilityEvent>();
 
-        Service(AccessibilityServiceInfo accessibilityServiceInfo) {
+        public Service(ComponentName componentName,
+                AccessibilityServiceInfo accessibilityServiceInfo, boolean isFake) {
             mId = sIdCounter++;
-            setAccessibilityServiceInfo(accessibilityServiceInfo);
-            mIntent = new Intent().setComponent(mComponentName);
-            mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
-                    com.android.internal.R.string.accessibility_binding_label);
-            mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
-                    mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+            mComponentName = componentName;
+            mAccessibilityServiceInfo = accessibilityServiceInfo;
+            mIsFake = isFake;
+            if (!isFake) {
+                mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
+                mIntent = new Intent().setComponent(mComponentName);
+                mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+                        com.android.internal.R.string.accessibility_binding_label);
+                mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+                        mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+            } else {
+                mCanRetrieveScreenContent = true;
+            }
+            setDynamicallyConfigurableProperties(accessibilityServiceInfo);
         }
 
-        public void setAccessibilityServiceInfo(AccessibilityServiceInfo info) {
-            mAccessibilityServiceInfo = info;
-            ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
-            mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
             mEventTypes = info.eventTypes;
             mFeedbackType = info.feedbackType;
             String[] packageNames = info.packageNames;
@@ -781,7 +873,7 @@
          * @return True if binding is successful.
          */
         public boolean bind() {
-            if (mService == null) {
+            if (!mIsFake && mService == null) {
                 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
             }
             return false;
@@ -798,7 +890,9 @@
                 synchronized (mLock) {
                     tryRemoveServiceLocked(this);
                 }
-                mContext.unbindService(this);
+                if (!mIsFake) {
+                    mContext.unbindService(this);
+                }
                 mService = null;
                 return true;
             }
@@ -832,6 +926,154 @@
             }
         }
 
+        public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                if (permissionGranted) {
+                    connection = getConnectionToRetrievalAllowingWindowLocked();
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to a retrieve allowing window.");
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, mCallback);
+                AccessibilityNodeInfo info = mCallback.getFindAccessibilityNodeInfoResultAndClear(
+                        interactionId);
+                if (info != null) {
+                    info.setConnection(this);
+                }
+                info.setSealed(true);
+                return info;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error finding node.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                if (permissionGranted) {
+                    connection = getConnectionToRetrievalAllowingWindowLocked();
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to focused window.");
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfosByViewText(text, interactionId, mCallback);
+                List<AccessibilityNodeInfo> infos =
+                    mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
+                if (infos != null) {
+                    final int infoCount = infos.size();
+                    for (int i = 0; i < infoCount; i++) {
+                        AccessibilityNodeInfo info = infos.get(i);
+                        info.setConnection(this);
+                        info.setSealed(true);
+                    }
+                }
+                return infos;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error finding node.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+                int accessibilityWindowId, int accessibilityViewId) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted =
+                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
+                if (permissionGranted) {
+                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to window: "
+                            + accessibilityWindowId);
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
+                        interactionId, mCallback);
+                AccessibilityNodeInfo info =
+                     mCallback.getFindAccessibilityNodeInfoResultAndClear(interactionId);
+                if (info != null) {
+                    info.setConnection(this);
+                    info.setSealed(true);
+                }
+                return info;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+                            + accessibilityViewId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public boolean performAccessibilityAction(int accessibilityWindowId,
+                int accessibilityViewId, int action) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
+                        accessibilityWindowId, action);
+                if (permissionGranted) {
+                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to window: "
+                            + accessibilityWindowId);
+                }
+                return false;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
+                        mCallback);
+                return mCallback.getPerformAccessibilityActionResult(interactionId);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+                            + accessibilityViewId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return false;
+        }
+
         public void onServiceDisconnected(ComponentName componentName) {
             /* do nothing - #binderDied takes care */
         }
@@ -849,5 +1091,171 @@
                 tryRemoveServiceLocked(this);
             }
         }
+
+        private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
+            final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
+            }
+            return mWindowIdToInteractionConnectionMap.get(windowId);
+        }
+    }
+
+    final class SecurityPolicy {
+        private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
+            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+
+        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
+            AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END
+            | AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START
+            | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
+            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
+        private int mRetrievalAlowingWindowId;
+
+        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
+            final int windowId = event.getSourceAccessibilityWindowId();
+            final int eventType = event.getEventType();
+            if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) {
+                mRetrievalAlowingWindowId = windowId;
+            } else {
+                event.setSource(null);
+            }
+        }
+
+        public int getRetrievalAllowingWindowLocked() {
+            return mRetrievalAlowingWindowId;
+        }
+
+        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
+            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+        }
+
+        public boolean canPerformActionLocked(Service service, int windowId, int action) {
+            return canRetrieveWindowContent(service)
+                && isRetrievalAllowingWindow(windowId)
+                && isActionPermitted(action);
+        }
+
+        public boolean canRetrieveWindowContent(Service service) {
+            return service.mCanRetrieveScreenContent;
+        }
+
+        private boolean isRetrievalAllowingWindow(int windowId) {
+            return (mRetrievalAlowingWindowId == windowId);
+        }
+
+        private boolean isActionPermitted(int action) {
+             return (VALID_ACTIONS & action) != 0;
+        }
+
+        private void enforceCallingPermission(String permission, String function) {
+            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+                return;
+            }
+            final int permissionStatus = mContext.checkCallingPermission(permission);
+            if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("You do not have " + permission
+                        + " required to call " + function);
+            }
+        }
+    }
+
+    final class Callback extends IAccessibilityInteractionConnectionCallback.Stub {
+        private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
+
+        private int mInteractionId = -1;
+        private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
+        private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
+        private boolean mPerformAccessibilityActionResult;
+
+        public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
+                int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mFindAccessibilityNodeInfoResult = info;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                AccessibilityNodeInfo result = mFindAccessibilityNodeInfoResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
+                int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mFindAccessibilityNodeInfosResult = infos;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
+                int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                List<AccessibilityNodeInfo> result = mFindAccessibilityNodeInfosResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mPerformAccessibilityActionResult = succeeded;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public boolean getPerformAccessibilityActionResult(int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                final boolean result = mPerformAccessibilityActionResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void clearLocked() {
+            mInteractionId = -1;
+            mFindAccessibilityNodeInfoResult = null;
+            mFindAccessibilityNodeInfosResult = null;
+            mPerformAccessibilityActionResult = false;
+        }
+
+        private void waitForResultTimedLocked(long waitTimeMillis, int interactionId) {
+            final long startTimeMillis = SystemClock.uptimeMillis();
+            while (true) {
+                try {
+                    if (mInteractionId == interactionId) {
+                        return;
+                    }
+                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                    waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
+                    if (waitTimeMillis <= 0) {
+                        return;
+                    }
+                    mLock.wait(waitTimeMillis);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
     }
 }
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 4c7f595..aab189a6 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -226,8 +226,9 @@
                 // Send a hover for every finger down so the user gets feedback
                 // where she is currently touching.
                 mSendHoverDelayed.forceSendAndRemove();
-                mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 1, policyFlags,
-                        DELAY_SEND_HOVER_MOVE);
+                final int pointerIdBits = (1 << event.getActionIndex());
+                mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits,
+                        policyFlags, DELAY_SEND_HOVER_MOVE);
             } break;
             case MotionEvent.ACTION_POINTER_DOWN: {
                 switch (activePointerCount) {
@@ -350,11 +351,12 @@
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
                             // a given distance perform a drag.
-                            mCurrentState = STATE_DRAGGING;      
+                            mCurrentState = STATE_DRAGGING;
                             if (mTouchExploreGestureInProgress) {
                                 sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                                 mTouchExploreGestureInProgress = false;
                             }
+                            mLastTouchExploreEvent = null;
                             mDraggingPointerId = pointerId;
                             sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
                                     policyFlags);
@@ -365,6 +367,7 @@
                                 sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                                 mTouchExploreGestureInProgress = false;
                             }
+                            mLastTouchExploreEvent = null;
                             sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                         }
                     } break;
@@ -382,6 +385,7 @@
                             sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                             mTouchExploreGestureInProgress = false;
                         }
+                        mLastTouchExploreEvent = null;
                         sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                     }
                 }
@@ -728,7 +732,7 @@
     /**
      * Sends an event.
      *
-     * @param event The event to send.
+     * @param prototype The prototype from which to create the injected events.
      * @param action The action of the event.
      * @param pointerIdBits The bits of the pointers to send.
      * @param policyFlags The policy flags associated with the event.
@@ -979,7 +983,7 @@
                 case MotionEvent.ACTION_DOWN: {
                     // New gesture so restart tracking injected down pointers.
                     mInjectedPointersDown = 0;
-                    handleReceivedPointerDown(0, event);
+                    handleReceivedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
                     handleReceivedPointerDown(event.getActionIndex(), event);
@@ -988,7 +992,7 @@
                     handleReceivedPointerMove(event);
                 } break;
                 case MotionEvent.ACTION_UP: {
-                    handleReceivedPointerUp(0, event);
+                    handleReceivedPointerUp(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_UP: {
                     handleReceivedPointerUp(event.getActionIndex(), event);
@@ -1008,13 +1012,13 @@
             final int action = event.getActionMasked();
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
-                    handleInjectedPointerDown(0, event);
+                    handleInjectedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
                     handleInjectedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_UP: {
-                    handleInjectedPointerUp(0, event);
+                    handleInjectedPointerUp(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_UP: {
                     handleInjectedPointerUp(event.getActionIndex(), event);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c2a8a1d..da9f1d6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -92,7 +92,6 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IInterface;
 import android.os.IPermissionController;
 import android.os.Looper;
 import android.os.Message;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e8155b4..5fa26ef 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -129,13 +129,6 @@
         mNMService = nmService;
         mLooper = looper;
 
-        // register for notifications from NetworkManagement Service
-        try {
-            mNMService.registerObserver(this);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error registering observer :" + e);
-        }
-
         mIfaces = new HashMap<String, TetherInterfaceSM>();
 
         // make our own thread so we don't anr the system
@@ -1212,8 +1205,8 @@
                     return null;
                 }
 
-                for (String iface : ifaces) {
-                    for (String regex : mUpstreamIfaceRegexs) {
+                for (String regex : mUpstreamIfaceRegexs) {
+                    for (String iface : ifaces) {
                         if (iface.matches(regex)) {
                             // verify it is active
                             InterfaceConfiguration ifcg = null;
diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java
new file mode 100644
index 0000000..ff86581
--- /dev/null
+++ b/services/java/com/android/server/net/InterfaceIdentity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.HashSet;
+
+/**
+ * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
+ * active on that interface.
+ *
+ * @hide
+ */
+public class InterfaceIdentity extends HashSet<NetworkIdentity> {
+    private static final int VERSION_CURRENT = 1;
+
+    public InterfaceIdentity() {
+    }
+
+    public InterfaceIdentity(DataInputStream in) throws IOException {
+        final int version = in.readInt();
+        switch (version) {
+            case VERSION_CURRENT: {
+                final int size = in.readInt();
+                for (int i = 0; i < size; i++) {
+                    add(new NetworkIdentity(in));
+                }
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+    }
+
+    public void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(VERSION_CURRENT);
+        out.writeInt(size());
+        for (NetworkIdentity ident : this) {
+            ident.writeToStream(out);
+        }
+    }
+
+    /**
+     * Test if any {@link NetworkIdentity} on this interface matches the given
+     * template and IMEI.
+     */
+    public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+        for (NetworkIdentity ident : this) {
+            if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
new file mode 100644
index 0000000..79feb95
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkIdentity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.getNetworkClass;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Objects;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+
+/**
+ * Identity of a {@link NetworkInfo}, defined by network type and billing
+ * relationship (such as IMSI).
+ *
+ * @hide
+ */
+public class NetworkIdentity {
+    private static final int VERSION_CURRENT = 1;
+
+    public final int type;
+    public final int subType;
+    public final String subscriberId;
+
+    public NetworkIdentity(int type, int subType, String subscriberId) {
+        this.type = type;
+        this.subType = subType;
+        this.subscriberId = subscriberId;
+    }
+
+    public NetworkIdentity(DataInputStream in) throws IOException {
+        final int version = in.readInt();
+        switch (version) {
+            case VERSION_CURRENT: {
+                type = in.readInt();
+                subType = in.readInt();
+                subscriberId = in.readUTF();
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+    }
+
+    public void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(VERSION_CURRENT);
+        out.writeInt(type);
+        out.writeInt(subType);
+        out.writeUTF(subscriberId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(type, subType, subscriberId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof NetworkIdentity) {
+            final NetworkIdentity ident = (NetworkIdentity) obj;
+            return type == ident.type && subType == ident.subType
+                    && Objects.equal(subscriberId, ident.subscriberId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        final String typeName = ConnectivityManager.getNetworkTypeName(type);
+        final String subTypeName;
+        if (ConnectivityManager.isNetworkTypeMobile(type)) {
+            subTypeName = TelephonyManager.getNetworkTypeName(subType);
+        } else {
+            subTypeName = Integer.toString(subType);
+        }
+
+        return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]";
+    }
+
+    /**
+     * Test if this network matches the given template and IMEI.
+     */
+    public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+        switch (networkTemplate) {
+            case TEMPLATE_MOBILE_ALL:
+                return matchesMobile(subscriberId);
+            case TEMPLATE_MOBILE_3G_LOWER:
+                return matchesMobile3gLower(subscriberId);
+            case TEMPLATE_MOBILE_4G:
+                return matchesMobile4g(subscriberId);
+            case TEMPLATE_WIFI:
+                return matchesWifi();
+            default:
+                throw new IllegalArgumentException("unknown network template");
+        }
+    }
+
+    /**
+     * Check if mobile network with matching IMEI. Also matches
+     * {@link #TYPE_WIMAX}.
+     */
+    private boolean matchesMobile(String subscriberId) {
+        if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) {
+            return true;
+        } else if (type == TYPE_WIMAX) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if mobile network classified 3G or lower with matching IMEI.
+     */
+    private boolean matchesMobile3gLower(String subscriberId) {
+        if (isNetworkTypeMobile(type)
+                && Objects.equal(this.subscriberId, subscriberId)) {
+            switch (getNetworkClass(subType)) {
+                case NETWORK_CLASS_2_G:
+                case NETWORK_CLASS_3_G:
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if mobile network classified 4G with matching IMEI. Also matches
+     * {@link #TYPE_WIMAX}.
+     */
+    private boolean matchesMobile4g(String subscriberId) {
+        if (isNetworkTypeMobile(type)
+                && Objects.equal(this.subscriberId, subscriberId)) {
+            switch (getNetworkClass(subType)) {
+                case NETWORK_CLASS_4_G:
+                    return true;
+            }
+        } else if (type == TYPE_WIMAX) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if matches Wi-Fi network template.
+     */
+    private boolean matchesWifi() {
+        if (type == TYPE_WIFI) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Build a {@link NetworkIdentity} from the given {@link NetworkState},
+     * assuming that any mobile networks are using the current IMSI.
+     */
+    public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+        final int type = state.networkInfo.getType();
+        final int subType = state.networkInfo.getSubtype();
+
+        // TODO: consider moving subscriberId over to LinkCapabilities, so it
+        // comes from an authoritative source.
+
+        final String subscriberId;
+        if (isNetworkTypeMobile(type)) {
+            final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            subscriberId = telephony.getSubscriberId();
+        } else {
+            subscriberId = null;
+        }
+        return new NetworkIdentity(type, subType, subscriberId);
+    }
+
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 17c7161..1a90a92 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -89,6 +89,8 @@
     // TODO: keep record of billing cycle details, and limit rules
     // TODO: keep map of interfaces-to-billing-relationship
 
+    // TODO: dispatch callbacks through handler when locked
+
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             IPowerManager powerManager, INetworkStatsService networkStats) {
         mContext = checkNotNull(context, "missing context");
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index d9c1f25..3892de8 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -16,12 +16,23 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.UID_ALL;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
+import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.app.AlarmManager;
 import android.app.IAlarmManager;
@@ -30,29 +41,33 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.net.IConnectivityManager;
 import android.net.INetworkStatsService;
-import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
-import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
 import android.util.NtpTrustedTime;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TrustedTime;
 
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
+import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 
 /**
  * Collect and persist detailed network statistics, and provide this data to
@@ -67,39 +82,49 @@
     private final IAlarmManager mAlarmManager;
     private final TrustedTime mTime;
 
+    private IConnectivityManager mConnManager;
+
     private static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
 
     private PendingIntent mPollIntent;
 
-    // TODO: move tweakable params to Settings.Secure
     // TODO: listen for kernel push events through netd instead of polling
 
     private static final long KB_IN_BYTES = 1024;
+    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
+    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
 
-    private static final long POLL_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
-    private static final long SUMMARY_BUCKET_DURATION = 6 * DateUtils.HOUR_IN_MILLIS;
-    private static final long SUMMARY_MAX_HISTORY = 90 * DateUtils.DAY_IN_MILLIS;
+    private LongSecureSetting mPollInterval = new LongSecureSetting(
+            NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
+    private LongSecureSetting mPersistThreshold = new LongSecureSetting(
+            NETSTATS_PERSIST_THRESHOLD, 64 * KB_IN_BYTES);
 
-    // TODO: remove these high-frequency testing values
-//    private static final long POLL_INTERVAL = 5 * DateUtils.SECOND_IN_MILLIS;
-//    private static final long SUMMARY_BUCKET_DURATION = 10 * DateUtils.SECOND_IN_MILLIS;
-//    private static final long SUMMARY_MAX_HISTORY = 2 * DateUtils.MINUTE_IN_MILLIS;
+    private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting(
+            NETSTATS_SUMMARY_BUCKET_DURATION, 6 * HOUR_IN_MILLIS);
+    private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting(
+            NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+    private LongSecureSetting mDetailBucketDuration = new LongSecureSetting(
+            NETSTATS_DETAIL_BUCKET_DURATION, 6 * HOUR_IN_MILLIS);
+    private LongSecureSetting mDetailMaxHistory = new LongSecureSetting(
+            NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS);
 
-    /** Minimum delta required to persist to disk. */
-    private static final long SUMMARY_PERSIST_THRESHOLD = 64 * KB_IN_BYTES;
-
-    private static final long TIME_CACHE_MAX_AGE = DateUtils.DAY_IN_MILLIS;
+    private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
     private final Object mStatsLock = new Object();
 
     /** Set of active ifaces during this boot. */
-    private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap();
-    /** Set of historical stats for known ifaces. */
-    private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap();
+    private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
 
-    private NetworkStats mLastPollStats;
-    private NetworkStats mLastPersistStats;
+    /** Set of historical stats for known ifaces. */
+    private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap();
+    /** Set of historical stats for known UIDs. */
+    private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>();
+
+    private NetworkStats mLastSummaryPoll;
+    private NetworkStats mLastSummaryPersist;
+
+    private NetworkStats mLastDetailPoll;
 
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
@@ -132,13 +157,9 @@
         readStatsLocked();
 
         // watch other system services that claim interfaces
-        // TODO: protect incoming broadcast with permissions check.
-        // TODO: consider migrating this to ConnectivityService, but it might
-        // cause a circular dependency.
-        final IntentFilter interfaceFilter = new IntentFilter();
-        interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(mInterfaceReceiver, interfaceFilter);
+        final IntentFilter ifaceFilter = new IntentFilter();
+        ifaceFilter.addAction(CONNECTIVITY_ACTION);
+        mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
 
         // listen for periodic polling events
         final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -155,9 +176,13 @@
         }
     }
 
+    public void bindConnectivityManager(IConnectivityManager connManager) {
+        mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+    }
+
     /**
      * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
-     * reschedule based on current {@link #POLL_INTERVAL} value.
+     * reschedule based on current {@link #mPollInterval} value.
      */
     private void registerPollAlarmLocked() throws RemoteException {
         if (mPollIntent != null) {
@@ -169,49 +194,72 @@
 
         final long currentRealtime = SystemClock.elapsedRealtime();
         mAlarmManager.setInexactRepeating(
-                AlarmManager.ELAPSED_REALTIME, currentRealtime, POLL_INTERVAL, mPollIntent);
+                AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent);
     }
 
     @Override
-    public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) {
-        // TODO: return history for requested types
-        return null;
+    public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+        synchronized (mStatsLock) {
+            // combine all interfaces that match template
+            final String subscriberId = getActiveSubscriberId();
+            final NetworkStatsHistory combined = new NetworkStatsHistory(
+                    mSummaryBucketDuration.get());
+            for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+                final NetworkStatsHistory history = mSummaryStats.get(ident);
+                if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+                    combined.recordEntireHistory(history);
+                }
+            }
+            return combined;
+        }
     }
 
     @Override
-    public NetworkStatsHistory getNetworkStatsUid(int uid) {
+    public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
         // TODO: return history for requested uid
         return null;
     }
 
+    @Override
+    public NetworkStats getSummaryPerUid(long start, long end, int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+        // TODO: apply networktemplate once granular uid stats are stored.
+
+        synchronized (mStatsLock) {
+            final int size = mDetailStats.size();
+            final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
+
+            final long[] total = new long[2];
+            for (int i = 0; i < size; i++) {
+                final int uid = mDetailStats.keyAt(i);
+                final NetworkStatsHistory history = mDetailStats.valueAt(i);
+                history.getTotalData(start, end, total);
+                stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
+            }
+            return stats.build();
+        }
+    }
+
     /**
-     * Receiver that watches for other system components that claim network
+     * Receiver that watches for {@link IConnectivityManager} to claim network
      * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
      * with mobile interfaces.
      */
-    private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) {
-                final LinkProperties prop = intent.getParcelableExtra(
-                        Phone.DATA_LINK_PROPERTIES_KEY);
-                final String iface = prop != null ? prop.getInterfaceName() : null;
-                if (iface != null) {
-                    final TelephonyManager teleManager = (TelephonyManager) context
-                            .getSystemService(Context.TELEPHONY_SERVICE);
-                    final InterfaceInfo info = new InterfaceInfo(
-                            iface, TYPE_MOBILE, teleManager.getSubscriberId());
-                    reportActiveInterface(info);
-                }
-            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
-                final LinkProperties prop = intent.getParcelableExtra(
-                        WifiManager.EXTRA_LINK_PROPERTIES);
-                final String iface = prop != null ? prop.getInterfaceName() : null;
-                if (iface != null) {
-                    final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null);
-                    reportActiveInterface(info);
-                }
+            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+            // permission above.
+            synchronized (mStatsLock) {
+                updateIfacesLocked();
             }
         }
     };
@@ -219,8 +267,8 @@
     private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // already running on background handler, network/io is safe, and
-            // caller verified to have UPDATE_DEVICE_STATS permission above.
+            // on background handler thread, and verified UPDATE_DEVICE_STATS
+            // permission above.
             synchronized (mStatsLock) {
                 // TODO: acquire wakelock while performing poll
                 performPollLocked();
@@ -231,13 +279,49 @@
     private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // persist stats during clean shutdown
+            // verified SHUTDOWN permission above.
             synchronized (mStatsLock) {
                 writeStatsLocked();
             }
         }
     };
 
+    /**
+     * Inspect all current {@link NetworkState} to derive mapping from {@code
+     * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
+     * are active on a single {@code iface}, they are combined under a single
+     * {@link InterfaceIdentity}.
+     */
+    private void updateIfacesLocked() {
+        if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+
+        // take one last stats snapshot before updating iface mapping. this
+        // isn't perfect, since the kernel may already be counting traffic from
+        // the updated network.
+        // TODO: verify that we only poll summary stats, not uid details
+        performPollLocked();
+
+        final NetworkState[] states;
+        try {
+            states = mConnManager.getAllNetworkState();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem reading network state");
+            return;
+        }
+
+        // rebuild active interfaces based on connected networks
+        mActiveIface.clear();
+
+        for (NetworkState state : states) {
+            if (state.networkInfo.isConnected()) {
+                // collect networks under their parent interfaces
+                final String iface = state.linkProperties.getInterfaceName();
+                final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface);
+                ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
+            }
+        }
+    }
+
     private void performPollLocked() {
         if (LOGD) Slog.v(TAG, "performPollLocked()");
 
@@ -250,58 +334,110 @@
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();
 
-        final NetworkStats current;
+        final NetworkStats summary;
+        final NetworkStats detail;
         try {
-            current = mNetworkManager.getNetworkStatsSummary();
+            summary = mNetworkManager.getNetworkStatsSummary();
+            detail = mNetworkManager.getNetworkStatsDetail();
         } catch (RemoteException e) {
             Slog.w(TAG, "problem reading network stats");
             return;
         }
 
-        // update historical usage with delta since last poll
-        final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current);
-        final long timeStart = currentTime - pollDelta.elapsedRealtime;
-        for (String iface : pollDelta.getKnownIfaces()) {
-            final InterfaceInfo info = mActiveIface.get(iface);
-            if (info == null) {
-                if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats");
-                continue;
-            }
-
-            final int index = pollDelta.findIndex(iface, UID_ALL);
-            final long rx = pollDelta.rx[index];
-            final long tx = pollDelta.tx[index];
-
-            final NetworkStatsHistory history = findOrCreateHistoryLocked(info);
-            history.recordData(timeStart, currentTime, rx, tx);
-            history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY);
-        }
-
-        mLastPollStats = current;
+        performSummaryPollLocked(summary, currentTime);
+        performDetailPollLocked(detail, currentTime);
 
         // decide if enough has changed to trigger persist
-        final NetworkStats persistDelta = computeStatsDelta(mLastPersistStats, current);
-        for (String iface : persistDelta.getKnownIfaces()) {
+        final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary);
+        final long persistThreshold = mPersistThreshold.get();
+        for (String iface : persistDelta.getUniqueIfaces()) {
             final int index = persistDelta.findIndex(iface, UID_ALL);
-            if (persistDelta.rx[index] > SUMMARY_PERSIST_THRESHOLD
-                    || persistDelta.tx[index] > SUMMARY_PERSIST_THRESHOLD) {
+            if (persistDelta.rx[index] > persistThreshold
+                    || persistDelta.tx[index] > persistThreshold) {
                 writeStatsLocked();
-                mLastPersistStats = current;
+                mLastSummaryPersist = summary;
                 break;
             }
         }
     }
 
-    private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) {
-        NetworkStatsHistory stats = mIfaceStats.get(info);
+    /**
+     * Update {@link #mSummaryStats} historical usage.
+     */
+    private void performSummaryPollLocked(NetworkStats summary, long currentTime) {
+        final ArrayList<String> unknownIface = Lists.newArrayList();
+
+        final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary);
+        final long timeStart = currentTime - delta.elapsedRealtime;
+        final long maxHistory = mSummaryMaxHistory.get();
+        for (String iface : delta.getUniqueIfaces()) {
+            final InterfaceIdentity ident = mActiveIface.get(iface);
+            if (ident == null) {
+                unknownIface.add(iface);
+                continue;
+            }
+
+            final int index = delta.findIndex(iface, UID_ALL);
+            final long rx = delta.rx[index];
+            final long tx = delta.tx[index];
+
+            final NetworkStatsHistory history = findOrCreateSummaryLocked(ident);
+            history.recordData(timeStart, currentTime, rx, tx);
+            history.removeBucketsBefore(currentTime - maxHistory);
+        }
+        mLastSummaryPoll = summary;
+
+        if (LOGD && unknownIface.size() > 0) {
+            Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
+        }
+    }
+
+    /**
+     * Update {@link #mDetailStats} historical usage.
+     */
+    private void performDetailPollLocked(NetworkStats detail, long currentTime) {
+        final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail);
+        final long timeStart = currentTime - delta.elapsedRealtime;
+        final long maxHistory = mDetailMaxHistory.get();
+        for (int uid : delta.getUniqueUids()) {
+            final int index = delta.findIndex(IFACE_ALL, uid);
+            final long rx = delta.rx[index];
+            final long tx = delta.tx[index];
+
+            final NetworkStatsHistory history = findOrCreateDetailLocked(uid);
+            history.recordData(timeStart, currentTime, rx, tx);
+            history.removeBucketsBefore(currentTime - maxHistory);
+        }
+        mLastDetailPoll = detail;
+    }
+
+    private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) {
+        NetworkStatsHistory stats = mSummaryStats.get(ident);
         if (stats == null) {
-            stats = new NetworkStatsHistory(
-                    info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION);
-            mIfaceStats.put(info, stats);
+            stats = new NetworkStatsHistory(mSummaryBucketDuration.get());
+            mSummaryStats.put(ident, stats);
         }
         return stats;
     }
 
+    private NetworkStatsHistory findOrCreateDetailLocked(int uid) {
+        NetworkStatsHistory stats = mDetailStats.get(uid);
+        if (stats == null) {
+            stats = new NetworkStatsHistory(mDetailBucketDuration.get());
+            mDetailStats.put(uid, stats);
+        }
+        return stats;
+    }
+
+    private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
+        InterfaceIdentity ident = mActiveIface.get(iface);
+        if (ident == null) {
+            ident = new InterfaceIdentity();
+            mActiveIface.put(iface, ident);
+        }
+        return ident;
+    }
+
     private void readStatsLocked() {
         if (LOGD) Slog.v(TAG, "readStatsLocked()");
         // TODO: read historical stats from disk using AtomicFile
@@ -310,70 +446,96 @@
     private void writeStatsLocked() {
         if (LOGD) Slog.v(TAG, "writeStatsLocked()");
         // TODO: persist historical stats to disk using AtomicFile
+        // TODO: consider duplicating stats and releasing lock while writing
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
 
-        pw.println("Active interfaces:");
-        for (InterfaceInfo info : mActiveIface.values()) {
-            info.dump("  ", pw);
+        final HashSet<String> argSet = new HashSet<String>();
+        for (String arg : args) {
+            argSet.add(arg);
         }
 
-        pw.println("Known historical stats:");
-        for (NetworkStatsHistory stats : mIfaceStats.values()) {
-            stats.dump("  ", pw);
+        synchronized (mStatsLock) {
+            // TODO: remove this testing code, since it corrupts stats
+            if (argSet.contains("generate")) {
+                generateRandomLocked();
+                pw.println("Generated stub stats");
+                return;
+            }
+
+            pw.println("Active interfaces:");
+            for (String iface : mActiveIface.keySet()) {
+                final InterfaceIdentity ident = mActiveIface.get(iface);
+                pw.print("  iface="); pw.print(iface);
+                pw.print(" ident="); pw.println(ident.toString());
+            }
+
+            pw.println("Known historical stats:");
+            for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+                final NetworkStatsHistory stats = mSummaryStats.get(ident);
+                pw.print("  ident="); pw.println(ident.toString());
+                stats.dump("    ", pw);
+            }
+
+            if (argSet.contains("detail")) {
+                pw.println("Known detail stats:");
+                for (int i = 0; i < mDetailStats.size(); i++) {
+                    final int uid = mDetailStats.keyAt(i);
+                    final NetworkStatsHistory stats = mDetailStats.valueAt(i);
+                    pw.print("  UID="); pw.println(uid);
+                    stats.dump("    ", pw);
+                }
+            }
         }
     }
 
     /**
-     * Details for a well-known network interface, including its name, network
-     * type, and billing relationship identity (such as IMSI).
+     * @deprecated only for temporary testing
      */
-    private static class InterfaceInfo {
-        public final String iface;
-        public final int networkType;
-        public final String identity;
+    @Deprecated
+    private void generateRandomLocked() {
+        long end = System.currentTimeMillis();
+        long start = end - mSummaryMaxHistory.get();
+        long rx = 3 * GB_IN_BYTES;
+        long tx = 2 * GB_IN_BYTES;
 
-        public InterfaceInfo(String iface, int networkType, String identity) {
-            this.iface = iface;
-            this.networkType = networkType;
-            this.identity = identity;
+        mSummaryStats.clear();
+        for (InterfaceIdentity ident : mActiveIface.values()) {
+            final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident);
+            stats.generateRandom(start, end, rx, tx);
         }
 
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((identity == null) ? 0 : identity.hashCode());
-            result = prime * result + ((iface == null) ? 0 : iface.hashCode());
-            result = prime * result + networkType;
-            return result;
-        }
+        end = System.currentTimeMillis();
+        start = end - mDetailMaxHistory.get();
+        rx = 500 * MB_IN_BYTES;
+        tx = 100 * MB_IN_BYTES;
 
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof InterfaceInfo) {
-                final InterfaceInfo info = (InterfaceInfo) obj;
-                return equal(iface, info.iface) && networkType == info.networkType
-                        && equal(identity, info.identity);
-            }
-            return false;
-        }
-
-        public void dump(String prefix, PrintWriter pw) {
-            pw.print(prefix);
-            pw.print("InterfaceInfo: iface="); pw.print(iface);
-            pw.print(" networkType="); pw.print(networkType);
-            pw.print(" identity="); pw.println(identity);
+        mDetailStats.clear();
+        for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) {
+            final int uid = info.uid;
+            final NetworkStatsHistory stats = findOrCreateDetailLocked(uid);
+            stats.generateRandom(start, end, rx, tx);
         }
     }
 
-    private void reportActiveInterface(InterfaceInfo info) {
-        synchronized (mStatsLock) {
-            // TODO: when interface redefined, port over historical stats
-            mActiveIface.put(info.iface, info);
+    private class LongSecureSetting {
+        private String mKey;
+        private long mDefaultValue;
+
+        public LongSecureSetting(String key, long defaultValue) {
+            mKey = key;
+            mDefaultValue = defaultValue;
+        }
+
+        public long get() {
+            if (mContext != null) {
+                return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue);
+            } else {
+                return mDefaultValue;
+            }
         }
     }
 
@@ -389,15 +551,10 @@
         }
     }
 
-    private static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
-    private static <T> T checkNotNull(T value, String message) {
-        if (value == null) {
-            throw new NullPointerException(message);
-        }
-        return value;
+    private String getActiveSubscriberId() {
+        final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        return telephony.getSubscriberId();
     }
 
 }
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 3095c37..ee69311 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -617,8 +617,13 @@
         }
 
         @SuppressWarnings("unused")
-        public int getTapTimeout() {
-            return ViewConfiguration.getTapTimeout();
+        public int getHoverTapTimeout() {
+            return ViewConfiguration.getHoverTapTimeout();
+        }
+
+        @SuppressWarnings("unused")
+        public int getHoverTapSlop() {
+            return ViewConfiguration.getHoverTapSlop();
         }
 
         @SuppressWarnings("unused")
@@ -632,11 +637,6 @@
         }
 
         @SuppressWarnings("unused")
-        public int getTouchSlop() {
-            return ViewConfiguration.get(mContext).getScaledTouchSlop();
-        }
-
-        @SuppressWarnings("unused")
         public int getMaxEventsPerSecond() {
             int result = 0;
             try {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d95d4c5..819c16e 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -153,6 +153,7 @@
     static final boolean DEBUG_WINDOW_MOVEMENT = false;
     static final boolean DEBUG_TOKEN_MOVEMENT = false;
     static final boolean DEBUG_ORIENTATION = false;
+    static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
     static final boolean DEBUG_APP_TRANSITIONS = false;
     static final boolean DEBUG_STARTING_WINDOW = false;
@@ -427,6 +428,7 @@
     boolean mWindowsFreezingScreen = false;
     long mFreezeGcPending = 0;
     int mAppsFreezingScreen = 0;
+    int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
     int mLayoutSeq = 0;
     
@@ -3187,6 +3189,15 @@
     }
 
     public int getOrientationFromWindowsLocked() {
+        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
+            // If the display is frozen, some activities may be in the middle
+            // of restarting, and thus have removed their old window.  If the
+            // window has the flag to hide the lock screen, then the lock screen
+            // can re-appear and inflict its own orientation on us.  Keep the
+            // orientation stable until this all settles down.
+            return mLastWindowForcedOrientation;
+        }
+
         int pos = mWindows.size() - 1;
         while (pos >= 0) {
             WindowState wtoken = mWindows.get(pos);
@@ -3194,7 +3205,7 @@
             if (wtoken.mAppToken != null) {
                 // We hit an application window. so the orientation will be determined by the
                 // app window. No point in continuing further.
-                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+                return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
             }
             if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) {
                 continue;
@@ -3204,10 +3215,10 @@
                     (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
                 continue;
             } else {
-                return req;
+                return (mLastWindowForcedOrientation=req);
             }
         }
-        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
     }
 
     public int getOrientationFromAppTokensLocked() {
@@ -3220,16 +3231,23 @@
         while (pos >= 0) {
             AppWindowToken wtoken = mAppTokens.get(pos);
             pos--;
+
+            if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
+
             // if we're about to tear down this window and not seek for
             // the behind activity, don't use it for orientation
             if (!findingBehind
                     && (!wtoken.hidden && wtoken.hiddenRequested)) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+                        + " -- going to hide");
                 continue;
             }
 
             if (!haveGroup) {
                 // We ignore any hidden applications on the top.
                 if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+                            + " -- hidden on top");
                     continue;
                 }
                 haveGroup = true;
@@ -3243,6 +3261,8 @@
                 // user's orientation.
                 if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
                         && lastFullscreen) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                            + " -- end of group, return " + lastOrientation);
                     return lastOrientation;
                 }
             }
@@ -3253,16 +3273,21 @@
             lastFullscreen = wtoken.appFullscreen;
             if (lastFullscreen
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                        + " -- full screen, return " + or);
                 return or;
             }
             // If this application has requested an explicit orientation,
             // then use it.
             if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                        + " -- explicitly set, return " + or);
                 return or;
             }
             findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
         }
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
         return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
 
@@ -3335,15 +3360,6 @@
      * android.os.IBinder)
      */
     boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
-        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
-            // If the display is frozen, some activities may be in the middle
-            // of restarting, and thus have removed their old window.  If the
-            // window has the flag to hide the lock screen, then the lock screen
-            // can re-appear and inflict its own orientation on us.  Keep the
-            // orientation stable until this all settles down.
-            return false;
-        }
-
         boolean changed = false;
         long ident = Binder.clearCallingIdentity();
         try {
@@ -8939,9 +8955,10 @@
                     pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
                     pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
             pw.print("  mRotation="); pw.print(mRotation);
-                    pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation);
                     pw.print(" mRequestedRotation="); pw.print(mRequestedRotation);
                     pw.print(" mAltOrientation="); pw.println(mAltOrientation);
+            pw.print("  mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
+                    pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
             pw.print("  mDeferredRotation="); pw.print(mDeferredRotation);
                     pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags);
             pw.print("  mAnimationPending="); pw.print(mAnimationPending);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 881882f1..7c5084f 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,7 +56,7 @@
 // The exponent used to calculate the pointer speed scaling factor.
 // The scaling factor is calculated as 2 ^ (speed * exponent),
 // where the speed ranges from -7 to + 7 and is supplied by the user.
-static const float POINTER_SPEED_EXPONENT = 1.0f / 3;
+static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
 
 static struct {
     jmethodID notifyConfigurationChanged;
@@ -76,10 +76,10 @@
     jmethodID getKeyRepeatTimeout;
     jmethodID getKeyRepeatDelay;
     jmethodID getMaxEventsPerSecond;
-    jmethodID getTapTimeout;
+    jmethodID getHoverTapTimeout;
+    jmethodID getHoverTapSlop;
     jmethodID getDoubleTapTimeout;
     jmethodID getLongPressTimeout;
-    jmethodID getTouchSlop;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
 } gCallbacksClassInfo;
@@ -410,32 +410,32 @@
         env->DeleteLocalRef(excludedDeviceNames);
     }
 
-    jint tapTimeout = env->CallIntMethod(mCallbacksObj,
-            gCallbacksClassInfo.getTapTimeout);
-    if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) {
+    jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getHoverTapTimeout);
+    if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
         jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj,
                 gCallbacksClassInfo.getDoubleTapTimeout);
         if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
             jint longPressTimeout = env->CallIntMethod(mCallbacksObj,
                     gCallbacksClassInfo.getLongPressTimeout);
             if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
-                outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout);
+                outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout);
 
                 // We must ensure that the tap-drag interval is significantly shorter than
                 // the long-press timeout because the tap is held down for the entire duration
                 // of the double-tap timeout.
                 jint tapDragInterval = max(min(longPressTimeout - 100,
-                        doubleTapTimeout), tapTimeout);
+                        doubleTapTimeout), hoverTapTimeout);
                 outConfig->pointerGestureTapDragInterval =
                         milliseconds_to_nanoseconds(tapDragInterval);
             }
         }
     }
 
-    jint touchSlop = env->CallIntMethod(mCallbacksObj,
-            gCallbacksClassInfo.getTouchSlop);
-    if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
-        outConfig->pointerGestureTapSlop = touchSlop;
+    jint hoverTapSlop = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getHoverTapSlop);
+    if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) {
+        outConfig->pointerGestureTapSlop = hoverTapSlop;
     }
 
     { // acquire lock
@@ -1394,8 +1394,11 @@
     GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz,
             "getKeyRepeatDelay", "()I");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, clazz,
-            "getTapTimeout", "()I");
+    GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz,
+            "getHoverTapTimeout", "()I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz,
+            "getHoverTapSlop", "()I");
 
     GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz,
             "getDoubleTapTimeout", "()I");
@@ -1403,9 +1406,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz,
             "getLongPressTimeout", "()I");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, clazz,
-            "getTouchSlop", "()I");
-
     GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz,
             "getMaxEventsPerSecond", "()I");
 
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 3543275..5608402 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import android.util.Log;
+
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.gsm.SmsCbHeader;
 
@@ -58,10 +60,13 @@
         try {
             return new SmsCbMessage(pdu);
         } catch (IllegalArgumentException e) {
+            Log.w(LOG_TAG, "Failed parsing SMS-CB pdu", e);
             return null;
         }
     }
 
+    private static final String LOG_TAG = "SMSCB";
+
     /**
      * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
@@ -80,6 +85,8 @@
 
     private static final char CARRIAGE_RETURN = 0x0d;
 
+    private static final int PDU_BODY_PAGE_LENGTH = 82;
+
     private SmsCbHeader mHeader;
 
     private String mLanguage;
@@ -149,6 +156,13 @@
         return mHeader.updateNumber;
     }
 
+    /**
+     * Parse and unpack the body text according to the encoding in the DCS.
+     * After completing successfully this method will have assigned the body
+     * text into mBody, and optionally the language code into mLanguage
+     *
+     * @param pdu The pdu
+     */
     private void parseBody(byte[] pdu) {
         int encoding;
         boolean hasLanguageIndicator = false;
@@ -221,28 +235,81 @@
                 break;
         }
 
+        if (mHeader.format == SmsCbHeader.FORMAT_UMTS) {
+            // Payload may contain multiple pages
+            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+
+            if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
+                    * nrPages) {
+                throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
+                        + nrPages + " pages");
+            }
+
+            StringBuilder sb = new StringBuilder();
+
+            for (int i = 0; i < nrPages; i++) {
+                // Each page is 82 bytes followed by a length octet indicating
+                // the number of useful octets within those 82
+                int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
+                int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
+
+                if (length > PDU_BODY_PAGE_LENGTH) {
+                    throw new IllegalArgumentException("Page length " + length
+                            + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
+                }
+
+                sb.append(unpackBody(pdu, encoding, offset, length, hasLanguageIndicator));
+            }
+            mBody = sb.toString();
+        } else {
+            // Payload is one single page
+            int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+            int length = pdu.length - offset;
+
+            mBody = unpackBody(pdu, encoding, offset, length, hasLanguageIndicator);
+        }
+    }
+
+    /**
+     * Unpack body text from the pdu using the given encoding, position and
+     * length within the pdu
+     *
+     * @param pdu The pdu
+     * @param encoding The encoding, as derived from the DCS
+     * @param offset Position of the first byte to unpack
+     * @param length Number of bytes to unpack
+     * @param hasLanguageIndicator true if the body text is preceded by a
+     *            language indicator. If so, this method will as a side-effect
+     *            assign the extracted language code into mLanguage
+     * @return Body text
+     */
+    private String unpackBody(byte[] pdu, int encoding, int offset, int length,
+            boolean hasLanguageIndicator) {
+        String body = null;
+
         switch (encoding) {
             case SmsMessage.ENCODING_7BIT:
-                mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
-                        (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+                body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
 
-                if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
-                    mLanguage = mBody.substring(0, 2);
-                    mBody = mBody.substring(3);
+                if (hasLanguageIndicator && body != null && body.length() > 2) {
+                    // Language is two GSM characters followed by a CR.
+                    // The actual body text is offset by 3 characters.
+                    mLanguage = body.substring(0, 2);
+                    body = body.substring(3);
                 }
                 break;
 
             case SmsMessage.ENCODING_16BIT:
-                int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-
-                if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
-                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
-                            SmsCbHeader.PDU_HEADER_LENGTH, 2);
+                if (hasLanguageIndicator && pdu.length >= offset + 2) {
+                    // Language is two GSM characters.
+                    // The actual body text is offset by 2 bytes.
+                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
                     offset += 2;
+                    length -= 2;
                 }
 
                 try {
-                    mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+                    body = new String(pdu, offset, (length & 0xfffe), "utf-16");
                 } catch (UnsupportedEncodingException e) {
                     // Eeeek
                 }
@@ -252,16 +319,18 @@
                 break;
         }
 
-        if (mBody != null) {
+        if (body != null) {
             // Remove trailing carriage return
-            for (int i = mBody.length() - 1; i >= 0; i--) {
-                if (mBody.charAt(i) != CARRIAGE_RETURN) {
-                    mBody = mBody.substring(0, i + 1);
+            for (int i = body.length() - 1; i >= 0; i--) {
+                if (body.charAt(i) != CARRIAGE_RETURN) {
+                    body = body.substring(0, i + 1);
                     break;
                 }
             }
         } else {
-            mBody = "";
+            body = "";
         }
+
+        return body;
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8732e21..7272b08 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -469,6 +469,46 @@
         }
     }
 
+    /** Unknown network class. {@hide} */
+    public static final int NETWORK_CLASS_UNKNOWN = 0;
+    /** Class of broadly defined "2G" networks. {@hide} */
+    public static final int NETWORK_CLASS_2_G = 1;
+    /** Class of broadly defined "3G" networks. {@hide} */
+    public static final int NETWORK_CLASS_3_G = 2;
+    /** Class of broadly defined "4G" networks. {@hide} */
+    public static final int NETWORK_CLASS_4_G = 3;
+
+    /**
+     * Return general class of network type, such as "3G" or "4G". In cases
+     * where classification is contentious, this method is conservative.
+     *
+     * @hide
+     */
+    public static int getNetworkClass(int networkType) {
+        switch (networkType) {
+            case NETWORK_TYPE_GPRS:
+            case NETWORK_TYPE_EDGE:
+            case NETWORK_TYPE_CDMA:
+            case NETWORK_TYPE_1xRTT:
+            case NETWORK_TYPE_IDEN:
+                return NETWORK_CLASS_2_G;
+            case NETWORK_TYPE_UMTS:
+            case NETWORK_TYPE_EVDO_0:
+            case NETWORK_TYPE_EVDO_A:
+            case NETWORK_TYPE_HSDPA:
+            case NETWORK_TYPE_HSUPA:
+            case NETWORK_TYPE_HSPA:
+            case NETWORK_TYPE_EVDO_B:
+            case NETWORK_TYPE_EHRPD:
+            case NETWORK_TYPE_HSPAP:
+                return NETWORK_CLASS_3_G;
+            case NETWORK_TYPE_LTE:
+                return NETWORK_CLASS_4_G;
+            default:
+                return NETWORK_CLASS_UNKNOWN;
+        }
+    }
+
     /**
      * Returns a string representation of the radio technology (network type)
      * currently in use on the device.
@@ -477,7 +517,12 @@
      * @hide pending API council review
      */
     public String getNetworkTypeName() {
-        switch (getNetworkType()) {
+        return getNetworkTypeName(getNetworkType());
+    }
+
+    /** {@hide} */
+    public static String getNetworkTypeName(int type) {
+        switch (type) {
             case NETWORK_TYPE_GPRS:
                 return "GPRS";
             case NETWORK_TYPE_EDGE:
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 0c4581b..8427d14 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -96,8 +96,10 @@
     protected Registrant mRestrictedStateRegistrant;
     protected Registrant mGsmBroadcastSmsRegistrant;
 
-    // Network Mode received from PhoneFactory
-    protected int mNetworkMode;
+    // Preferred network type received from PhoneFactory.
+    // This is used when establishing a connection to the
+    // vendor ril so it starts up in the correct mode.
+    protected int mPreferredNetworkType;
     // CDMA subscription received from PhoneFactory
     protected int mCdmaSubscription;
     // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone.
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 2c04b30..572bbaa 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -611,14 +611,14 @@
 
     //***** Constructors
 
-    public RIL(Context context, int networkMode, int cdmaSubscription) {
+    public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
         super(context);
         if (RILJ_LOGD) {
-            riljLog("RIL(context, networkMode=" + networkMode +
+            riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
                     " cdmaSubscription=" + cdmaSubscription + ")");
         }
         mCdmaSubscription  = cdmaSubscription;
-        mNetworkMode = networkMode;
+        mPreferredNetworkType = preferredNetworkType;
         mPhoneType = RILConstants.NO_PHONE;
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -1813,6 +1813,8 @@
         rr.mp.writeInt(1);
         rr.mp.writeInt(networkType);
 
+        mPreferredNetworkType = networkType;
+
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                 + " : " + networkType);
 
@@ -2222,7 +2224,7 @@
             case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret =  responseInts(p); break;
             case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret =  responseVoid(p); break;
             case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret =  responseInts(p); break;
+            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret =  responseGetPreferredNetworkType(p); break;
             case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
             case RIL_REQUEST_SET_LOCATION_UPDATES: ret =  responseVoid(p); break;
             case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret =  responseVoid(p); break;
@@ -2737,7 +2739,7 @@
 
                 // Initial conditions
                 setRadioPower(false, null);
-                setPreferredNetworkType(mNetworkMode, null);
+                setPreferredNetworkType(mPreferredNetworkType, null);
                 setCdmaSubscriptionSource(mCdmaSubscription, null);
                 notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
                 break;
@@ -3161,6 +3163,18 @@
        return response;
     }
 
+    private Object responseGetPreferredNetworkType(Parcel p) {
+       int [] response = (int[]) responseInts(p);
+
+       if (response.length >= 1) {
+           // Since this is the response for getPreferredNetworkType
+           // we'll assume that it should be the value we want the
+           // vendor ril to take if we reestablish a connection to it.
+           mPreferredNetworkType = response[0];
+       }
+       return response;
+    }
+
     private Object responseGmsBroadcastConfig(Parcel p) {
         int num;
         ArrayList<SmsBroadcastConfigInfo> response;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 5f27cfc..0945a38 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -17,8 +17,31 @@
 package com.android.internal.telephony.gsm;
 
 public class SmsCbHeader {
+    /**
+     * Length of SMS-CB header
+     */
     public static final int PDU_HEADER_LENGTH = 6;
 
+    /**
+     * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
+     */
+    public static final int FORMAT_GSM = 1;
+
+    /**
+     * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
+     */
+    public static final int FORMAT_UMTS = 2;
+
+    /**
+     * Message type value as defined in 3gpp TS 25.324, section 11.1.
+     */
+    private static final int MESSAGE_TYPE_CBS_MESSAGE = 1;
+
+    /**
+     * Length of GSM pdus
+     */
+    private static final int PDU_LENGTH_GSM = 88;
+
     public final int geographicalScope;
 
     public final int messageCode;
@@ -33,27 +56,55 @@
 
     public final int nrOfPages;
 
+    public final int format;
+
     public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
         if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
             throw new IllegalArgumentException("Illegal PDU");
         }
 
-        geographicalScope = (pdu[0] & 0xc0) >> 6;
-        messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
-        updateNumber = pdu[1] & 0x0f;
-        messageIdentifier = (pdu[2] << 8) | pdu[3];
-        dataCodingScheme = pdu[4];
+        if (pdu.length <= PDU_LENGTH_GSM) {
+            // GSM pdus are no more than 88 bytes
+            format = FORMAT_GSM;
+            geographicalScope = (pdu[0] & 0xc0) >> 6;
+            messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+            updateNumber = pdu[1] & 0x0f;
+            messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
+            dataCodingScheme = pdu[4] & 0xff;
 
-        // Check for invalid page parameter
-        int pageIndex = (pdu[5] & 0xf0) >> 4;
-        int nrOfPages = pdu[5] & 0x0f;
+            // Check for invalid page parameter
+            int pageIndex = (pdu[5] & 0xf0) >> 4;
+            int nrOfPages = pdu[5] & 0x0f;
 
-        if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+            if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+                pageIndex = 1;
+                nrOfPages = 1;
+            }
+
+            this.pageIndex = pageIndex;
+            this.nrOfPages = nrOfPages;
+        } else {
+            // UMTS pdus are always at least 90 bytes since the payload includes
+            // a number-of-pages octet and also one length octet per page
+            format = FORMAT_UMTS;
+
+            int messageType = pdu[0];
+
+            if (messageType != MESSAGE_TYPE_CBS_MESSAGE) {
+                throw new IllegalArgumentException("Unsupported message type " + messageType);
+            }
+
+            messageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
+            geographicalScope = (pdu[3] & 0xc0) >> 6;
+            messageCode = ((pdu[3] & 0x3f) << 4) | ((pdu[4] & 0xf0) >> 4);
+            updateNumber = pdu[4] & 0x0f;
+            dataCodingScheme = pdu[5] & 0xff;
+
+            // We will always consider a UMTS message as having one single page
+            // since there's only one instance of the header, even though the
+            // actual payload may contain several pages.
             pageIndex = 1;
             nrOfPages = 1;
         }
-
-        this.pageIndex = pageIndex;
-        this.nrOfPages = nrOfPages;
     }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
index 7136ea0..b131a01 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -69,6 +69,36 @@
         doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
     }
 
+    public void testGetGeographicalScopeUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected geographical scope decoded",
+                SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE, msg.getGeographicalScope());
+    }
+
     public void testGetMessageBody7Bit() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -92,6 +122,83 @@
                 msg.getMessageBody());
     }
 
+    public void testGetMessageBody7BitUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+    }
+
+    public void testGetMessageBody7BitMultipageUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x02,
+
+                (byte)0xC6, (byte)0xB4, (byte)0x7C, (byte)0x4E, (byte)0x07, (byte)0xC1,
+                (byte)0xC3, (byte)0xE7, (byte)0xF2, (byte)0xAA, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+                (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+                (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x0A,
+
+                (byte)0xD3, (byte)0xF2, (byte)0xF8, (byte)0xED, (byte)0x26, (byte)0x83,
+                (byte)0xE0, (byte)0xE1, (byte)0x73, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+                (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+                (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x0A
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected multipage 7-bit string decoded",
+                "First page+Second page",
+                msg.getMessageBody());
+    }
+
     public void testGetMessageBody7BitFull() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -117,6 +224,38 @@
                 msg.getMessageBody());
     }
 
+    public void testGetMessageBody7BitFullUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5, (byte)0xB4,
+                (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+                (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40,
+                (byte)0x63, (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3,
+                (byte)0xCB, (byte)0xF2, (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76,
+                (byte)0x9F, (byte)0x59, (byte)0xA0, (byte)0x76, (byte)0x39, (byte)0xEC,
+                (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20, (byte)0x3A, (byte)0xBA,
+                (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73, (byte)0x90,
+                (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+                (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02,
+
+                (byte)0x52
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals(
+                "Unexpected 7-bit string decoded",
+                "A GSM default alphabet message being exactly 93 characters long, " +
+                "meaning there is no padding!",
+                msg.getMessageBody());
+    }
+
     public void testGetMessageBody7BitWithLanguage() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
@@ -167,6 +306,38 @@
         assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
     }
 
+    public void testGetMessageBody7BitWithLanguageInBodyUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x10,
+
+                (byte)0x01,
+
+                (byte)0x73, (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E,
+                (byte)0x9B, (byte)0x20, (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE,
+                (byte)0xB3, (byte)0xE9, (byte)0xA0, (byte)0x30, (byte)0x1B, (byte)0x8E,
+                (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74, (byte)0x50, (byte)0xBB,
+                (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65, (byte)0xD0,
+                (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+                (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41,
+                (byte)0xF2, (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83,
+                (byte)0xE0, (byte)0x61, (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E,
+                (byte)0x37, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x37
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+    }
+
     public void testGetMessageBody8Bit() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
@@ -210,6 +381,81 @@
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
     }
 
+    public void testGetMessageBodyUcs2Umts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+                (byte)0x01,
+
+                (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+                (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x63,
+                (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+                (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D,
+
+                (byte)0x4E
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+    }
+
+    public void testGetMessageBodyUcs2MultipageUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+                (byte)0x02,
+
+                (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41,
+                (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+                (byte)0x06,
+
+                (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42,
+                (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+                (byte)0x06
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected multipage UCS2 string decoded",
+                "AAABBB", msg.getMessageBody());
+    }
+
     public void testGetMessageBodyUcs2WithLanguageInBody() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
@@ -234,6 +480,37 @@
         assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
     }
 
+    public void testGetMessageBodyUcs2WithLanguageInBodyUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x11,
+
+                (byte)0x01,
+
+                (byte)0x78, (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53,
+                (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D,
+                (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D,
+
+                (byte)0x50
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+    }
+
     public void testGetMessageIdentifier() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -256,6 +533,35 @@
         assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
     }
 
+    public void testGetMessageIdentifierUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+    }
+
     public void testGetMessageCode() {
         byte[] pdu = {
                 (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -278,6 +584,35 @@
         assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
     }
 
+    public void testGetMessageCodeUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+    }
+
     public void testGetUpdateNumber() {
         byte[] pdu = {
                 (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -299,4 +634,33 @@
 
         assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
     }
+
+    public void testGetUpdateNumberUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+    }
 }
diff --git a/tests/BiDiTests/res/layout/frame_layout_ltr.xml b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
index bd324e3..61fd06e 100644
--- a/tests/BiDiTests/res/layout/frame_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr"
+    android:layoutDirection="ltr"
     android:background="#FF000000">
 
         <FrameLayout
@@ -52,28 +52,28 @@
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
diff --git a/tests/BiDiTests/res/layout/frame_layout_rtl.xml b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
index 814f5e1..598b41a 100644
--- a/tests/BiDiTests/res/layout/frame_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl"
+    android:layoutDirection="rtl"
     android:background="#FF000000">
 
         <FrameLayout
@@ -52,28 +52,28 @@
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
index c5a8d47..d4386f2 100644
--- a/tests/BiDiTests/res/layout/linear_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
@@ -51,7 +51,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
index 1494fec..9d07263 100644
--- a/tests/BiDiTests/res/layout/linear_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
@@ -51,7 +51,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr.xml b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
index 53ae7c3..d789707 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr"
+    android:layoutDirection="ltr"
     android:background="#FF000000">
 
         <RelativeLayout
@@ -52,28 +52,28 @@
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
index 93d1b4b..a13ef8b 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="ltr">
+            android:layoutDirection="ltr">
 
         <TextView android:id="@+id/label_2"
                 android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="inherit">
+            android:layoutDirection="inherit">
 
         <TextView android:id="@+id/label_3"
                 android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="rtl">
+            android:layoutDirection="rtl">
 
         <TextView android:id="@+id/label_4"
                 android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl.xml b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
index 4b87752..5808924 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_rtl"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl"
+    android:layoutDirection="rtl"
     android:background="#FF000000">
 
         <RelativeLayout
@@ -52,28 +52,28 @@
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
index 8418e72..1a6b3d5 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="ltr">
+            android:layoutDirection="ltr">
 
         <TextView android:id="@+id/label_2"
                 android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="inherit">
+            android:layoutDirection="inherit">
 
         <TextView android:id="@+id/label_3"
                 android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="rtl">
+            android:layoutDirection="rtl">
 
         <TextView android:id="@+id/label_4"
                 android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/table_layout_ltr.xml b/tests/BiDiTests/res/layout/table_layout_ltr.xml
index f44de8e..8e1891e 100644
--- a/tests/BiDiTests/res/layout/table_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/table_layout_ltr.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/layout/table_layout_rtl.xml b/tests/BiDiTests/res/layout/table_layout_rtl.xml
index 84270ba..bd664e4 100644
--- a/tests/BiDiTests/res/layout/table_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/table_layout_rtl.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 2aeb9e1..d067e7d 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -19,9 +19,9 @@
     <string name="button3_text">Button3</string>
     <string name="button4_text">Button4</string>
     <string name="button_right_text">Right</string>
-    <string name="button_after_text">After</string>
+    <string name="button_after_text">End</string>
     <string name="button_left_text">Left</string>
-    <string name="button_before_text">Before</string>
+    <string name="button_before_text">Start</string>
     <string name="button_requestlayout_text">Request Layout</string>
     <string name="textview_text">This is a text for a TextView</string>
     <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
new file mode 100755
index 0000000..f9d6172
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
index 5377f12..3ba6ba4 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
@@ -113,16 +113,20 @@
     private Allocation mTexOpaque;
     private Allocation mTexTransparent;
     private Allocation mTexChecker;
+    private Allocation mTexGlobe;
 
     private Mesh m10by10Mesh;
     private Mesh m100by100Mesh;
     private Mesh mWbyHMesh;
     private Mesh mTorus;
+    private Mesh mSingleMesh;
 
     Font mFontSans;
     Font mFontSerif;
     private Allocation mTextAlloc;
 
+    private ScriptField_TexAllocs_s mTextureAllocs;
+
     private ScriptC_rsbench mScript;
 
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
@@ -253,6 +257,27 @@
         return tmb.create(true);
     }
 
+    /**
+     * Create a mesh with a single quad for the given width and height.
+     */
+    private Mesh getSingleMesh(float width, float height) {
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+        float xOffset = width/2;
+        float yOffset = height/2;
+        tmb.setTexture(0, 0);
+        tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset);
+        tmb.setTexture(1, 0);
+        tmb.addVertex(xOffset, -1.0f * yOffset);
+        tmb.setTexture(1, 1);
+        tmb.addVertex(xOffset, yOffset);
+        tmb.setTexture(0, 1);
+        tmb.addVertex(-1.0f * xOffset, yOffset);
+        tmb.addTriangle(0, 3, 1);
+        tmb.addTriangle(1, 3, 2);
+        return tmb.create(true);
+    }
+
     private void initProgramStore() {
         // Use stock the stock program store object
         mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
@@ -392,11 +417,13 @@
         mTexOpaque = loadTextureRGB(R.drawable.data);
         mTexTransparent = loadTextureARGB(R.drawable.leaf);
         mTexChecker = loadTextureRGB(R.drawable.checker);
+        mTexGlobe = loadTextureRGB(R.drawable.globe);
 
         mScript.set_gTexTorus(mTexTorus);
         mScript.set_gTexOpaque(mTexOpaque);
         mScript.set_gTexTransparent(mTexTransparent);
         mScript.set_gTexChecker(mTexChecker);
+        mScript.set_gTexGlobe(mTexGlobe);
     }
 
     private void initFonts() {
@@ -418,6 +445,8 @@
         mScript.set_g100by100Mesh(m100by100Mesh);
         mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4);
         mScript.set_gWbyHMesh(mWbyHMesh);
+        mSingleMesh = getSingleMesh(50, 50);
+        mScript.set_gSingleMesh(mSingleMesh);
 
         FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
         FileA3D.IndexEntry entry = model.getIndexEntry(0);
@@ -517,6 +546,16 @@
                                            Allocation.USAGE_GRAPHICS_RENDER_TARGET);
         mScript.set_gRenderBufferDepth(offscreen);
 
+
+        mTextureAllocs = new ScriptField_TexAllocs_s(mRS, 100);
+        for (int i = 0; i < 100; i++) {
+            ScriptField_TexAllocs_s.Item texElem = new ScriptField_TexAllocs_s.Item();
+            texElem.texture = loadTextureRGB(R.drawable.globe);
+            mTextureAllocs.set(texElem, i, false);
+        }
+        mTextureAllocs.copyAll();
+        mScript.bind_gTexList100(mTextureAllocs);
+
         mRS.bindRootScript(mScript);
     }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index e7f5cd4..b6572fb 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -23,7 +23,7 @@
 const int RS_MSG_TEST_DONE = 100;
 const int RS_MSG_RESULTS_READY = 101;
 
-const int gMaxModes = 25;
+const int gMaxModes = 27;
 int gMaxLoops;
 
 // Allocation to send test names back to java
@@ -44,11 +44,19 @@
 rs_allocation gTexTorus;
 rs_allocation gTexTransparent;
 rs_allocation gTexChecker;
+rs_allocation gTexGlobe;
+
+typedef struct TexAllocs_s {
+    rs_allocation texture;
+} TexAllocs;
+
+TexAllocs *gTexList100;
 
 rs_mesh g10by10Mesh;
 rs_mesh g100by100Mesh;
 rs_mesh gWbyHMesh;
 rs_mesh gTorusMesh;
+rs_mesh gSingleMesh;
 
 rs_font gFontSans;
 rs_font gFontSerif;
@@ -142,7 +150,7 @@
 }
 
 static void bindProgramVertexOrtho() {
-    // Default vertex sahder
+    // Default vertex shader
     rsgBindProgramVertex(gProgVertex);
     // Setup the projection matrix
     rs_matrix4x4 proj;
@@ -198,6 +206,33 @@
     }
 }
 
+// Display sample images in a mesh with different texture
+static void displayMeshWithMultiTexture(int meshMode) {
+    bindProgramVertexOrtho();
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+
+    int meshCount = (int)pow(10.0f, (float)(meshMode + 1));
+
+    float yPos = 0;
+    for (int y = 0; y < meshCount; y++) {
+        yPos = (y + 1) * 50;
+        float xPos = 0;
+        for (int x = 0; x < meshCount; x++) {
+            xPos = (x + 1) * 50;
+            rs_matrix4x4 matrix;
+            rsMatrixLoadTranslate(&matrix, xPos, yPos, 0);
+            rsgProgramVertexLoadModelMatrix(&matrix);
+            int i = (x + y * meshCount) % 100;
+            rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].texture);
+            rsgDrawMesh(gSingleMesh);
+        }
+    }
+}
+
 static float gTorusRotation = 0;
 static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
     if (buffer == 0) {
@@ -497,6 +532,8 @@
     "Geo test 25.6k heavy fragment heavy vertex",
     "Geo test 51.2k heavy fragment heavy vertex",
     "Geo test 204.8k small tries heavy fragment heavy vertex",
+    "Mesh with 10 by 10 texture",
+    "Mesh with 100 by 100 texture",
 };
 
 void getTestName(int testIndex) {
@@ -589,6 +626,12 @@
     case 24:
         displayPixelLightSamples(8, true);
         break;
+    case 25:
+        displayMeshWithMultiTexture(0);
+        break;
+    case 26:
+        displayMeshWithMultiTexture(1);
+        break;
     }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 922cd4c..8e3ed93 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -425,7 +425,7 @@
         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
 
         // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
+        currentTx.concatenate(matrixTx);
 
         // give it to the graphics2D as a new matrix replacing all previous transform
         snapshot.setTransform(currentTx);
@@ -717,7 +717,7 @@
     /*package*/ static void native_drawCircle(int nativeCanvas,
             float cx, float cy, float radius, int paint) {
         native_drawOval(nativeCanvas,
-                new RectF(cx - radius, cy - radius, radius*2, radius*2),
+                new RectF(cx - radius, cy - radius, radius, radius),
                 paint);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
new file mode 100644
index 0000000..afbe97c0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.os;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Delegate overriding selected methods of android.os.HandlerThread
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class HandlerThread_Delegate {
+
+    private static Map<BridgeContext, List<HandlerThread>> sThreads =
+            new HashMap<BridgeContext, List<HandlerThread>>();
+
+    public static void cleanUp(BridgeContext context) {
+        List<HandlerThread> list = sThreads.get(context);
+        if (list != null) {
+            for (HandlerThread thread : list) {
+                thread.quit();
+            }
+
+            list.clear();
+            sThreads.remove(context);
+        }
+    }
+
+    // -------- Delegate methods
+
+    @LayoutlibDelegate
+    /*package*/ static void run(HandlerThread theThread) {
+        // record the thread so that it can be quit() on clean up.
+        BridgeContext context = RenderAction.getCurrentContext();
+        List<HandlerThread> list = sThreads.get(context);
+        if (list == null) {
+            list = new ArrayList<HandlerThread>();
+            sThreads.put(context, list);
+        }
+
+        list.add(theThread);
+
+        // ---- START DEFAULT IMPLEMENTATION.
+
+        theThread.mTid = Process.myTid();
+        Looper.prepare();
+        synchronized (theThread) {
+            theThread.mLooper = Looper.myLooper();
+            theThread.notifyAll();
+        }
+        Process.setThreadPriority(theThread.mPriority);
+        theThread.onLooperPrepared();
+        Looper.loop();
+        theThread.mTid = -1;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 0f3cf57..3ef3288 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -22,7 +22,10 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
+import android.util.Xml;
 
 import java.io.IOException;
 
@@ -35,6 +38,8 @@
  */
 public class LayoutInflater_Delegate {
 
+    public static boolean sIsInInclude = false;
+
     /**
      * Recursive method used to descend down the xml hierarchy and instantiate
      * views, instantiate their children, and then call onFinishInflate().
@@ -94,4 +99,128 @@
             }
         }
     }
+
+    @LayoutlibDelegate
+    public static void parseInclude(
+            LayoutInflater thisInflater,
+            XmlPullParser parser, View parent, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        int type;
+
+        if (parent instanceof ViewGroup) {
+            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+            if (layout == 0) {
+                final String value = attrs.getAttributeValue(null, "layout");
+                if (value == null) {
+                    throw new InflateException("You must specifiy a layout in the"
+                            + " include tag: <include layout=\"@layout/layoutID\" />");
+                } else {
+                    throw new InflateException("You must specifiy a valid layout "
+                            + "reference. The layout ID " + value + " is not valid.");
+                }
+            } else {
+                final XmlResourceParser childParser =
+                    thisInflater.getContext().getResources().getLayout(layout);
+
+                try {
+                    final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+
+                    while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+                            type != XmlPullParser.END_DOCUMENT) {
+                        // Empty.
+                    }
+
+                    if (type != XmlPullParser.START_TAG) {
+                        throw new InflateException(childParser.getPositionDescription() +
+                                ": No start tag found!");
+                    }
+
+                    final String childName = childParser.getName();
+
+                    if (LayoutInflater.TAG_MERGE.equals(childName)) {
+                        // Inflate all children.
+                        thisInflater.rInflate(childParser, parent, childAttrs, false);
+                    } else {
+                        final View view = thisInflater.createViewFromTag(parent, childName, childAttrs);
+                        final ViewGroup group = (ViewGroup) parent;
+
+                        // We try to load the layout params set in the <include /> tag. If
+                        // they don't exist, we will rely on the layout params set in the
+                        // included XML file.
+                        // During a layoutparams generation, a runtime exception is thrown
+                        // if either layout_width or layout_height is missing. We catch
+                        // this exception and set localParams accordingly: true means we
+                        // successfully loaded layout params from the <include /> tag,
+                        // false means we need to rely on the included layout params.
+                        ViewGroup.LayoutParams params = null;
+                        try {
+                            // ---- START CHANGES
+                            sIsInInclude = true;
+                            // ---- END CHANGES
+
+                            params = group.generateLayoutParams(attrs);
+
+                        } catch (RuntimeException e) {
+                            // ---- START CHANGES
+                            sIsInInclude = false;
+                            // ---- END CHANGES
+
+                            params = group.generateLayoutParams(childAttrs);
+                        } finally {
+                            // ---- START CHANGES
+                            sIsInInclude = false;
+                            // ---- END CHANGES
+
+                            if (params != null) {
+                                view.setLayoutParams(params);
+                            }
+                        }
+
+                        // Inflate all children.
+                        thisInflater.rInflate(childParser, view, childAttrs, true);
+
+                        // Attempt to override the included layout's android:id with the
+                        // one set on the <include /> tag itself.
+                        TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
+                            com.android.internal.R.styleable.View, 0, 0);
+                        int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
+                        // While we're at it, let's try to override android:visibility.
+                        int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
+                        a.recycle();
+
+                        if (id != View.NO_ID) {
+                            view.setId(id);
+                        }
+
+                        switch (visibility) {
+                            case 0:
+                                view.setVisibility(View.VISIBLE);
+                                break;
+                            case 1:
+                                view.setVisibility(View.INVISIBLE);
+                                break;
+                            case 2:
+                                view.setVisibility(View.GONE);
+                                break;
+                        }
+
+                        group.addView(view);
+                    }
+                } finally {
+                    childParser.close();
+                }
+            }
+        } else {
+            throw new InflateException("<include /> can only be used inside of a ViewGroup");
+        }
+
+        final int currentDepth = parser.getDepth();
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+    }
+
+
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 144ec42..3ba3257 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -196,7 +196,7 @@
                 Capability.UNBOUND_RENDERING,
                 Capability.CUSTOM_BACKGROUND_COLOR,
                 Capability.RENDER,
-                //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
+                Capability.LAYOUT_ONLY,
                 Capability.EMBEDDED_LAYOUT,
                 Capability.VIEW_MANIPULATION,
                 Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e536028..4fa924d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -71,7 +71,6 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
@@ -342,7 +341,7 @@
                 try {
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(xml));
+                    parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
 
                     // set the resource ref to have correct view cookies
                     mBridgeInflater.setResourceReference(resource);
@@ -514,14 +513,13 @@
         BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
                 isPlatformFile);
 
-        // resolve the defStyleAttr value into a IStyleResourceValue
-        StyleResourceValue defStyleValues = null;
-
         // look for a custom style.
         String customStyle = null;
         if (set != null) {
             customStyle = set.getAttributeValue(null /* namespace*/, "style");
         }
+
+        StyleResourceValue customStyleValues = null;
         if (customStyle != null) {
             ResourceValue item = mRenderResources.findResValue(customStyle,
                     false /*forceFrameworkOnly*/);
@@ -530,75 +528,76 @@
             item = mRenderResources.resolveResValue(item);
 
             if (item instanceof StyleResourceValue) {
-                defStyleValues = (StyleResourceValue)item;
+                customStyleValues = (StyleResourceValue)item;
             }
         }
 
-        if (defStyleValues == null) {
-            if (defStyleAttr != 0) {
-                // get the name from the int.
-                String defStyleName = searchAttr(defStyleAttr);
+        // resolve the defStyleAttr value into a IStyleResourceValue
+        StyleResourceValue defStyleValues = null;
 
-                if (defaultPropMap != null) {
-                    defaultPropMap.put("style", defStyleName);
+        if (defStyleAttr != 0) {
+            // get the name from the int.
+            String defStyleName = searchAttr(defStyleAttr);
+
+            if (defaultPropMap != null) {
+                defaultPropMap.put("style", defStyleName);
+            }
+
+            // look for the style in the current theme, and its parent:
+            ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+
+            if (item != null) {
+                // item is a reference to a style entry. Search for it.
+                item = mRenderResources.findResValue(item.getValue(),
+                        false /*forceFrameworkOnly*/);
+
+                if (item instanceof StyleResourceValue) {
+                    defStyleValues = (StyleResourceValue)item;
                 }
+            } else {
+                Bridge.getLog().error(null,
+                        String.format(
+                                "Failed to find style '%s' in current theme", defStyleName),
+                        null /*data*/);
+            }
+        } else if (defStyleRes != 0) {
+            Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+            if (value == null) {
+                value = mProjectCallback.resolveResourceId(defStyleRes);
+            }
 
-                // look for the style in the current theme, and its parent:
-                ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
-
-                if (item != null) {
-                    // item is a reference to a style entry. Search for it.
-                    item = mRenderResources.findResValue(item.getValue(),
-                            false /*forceFrameworkOnly*/);
-
-                    if (item instanceof StyleResourceValue) {
-                        defStyleValues = (StyleResourceValue)item;
-                    }
-                } else {
-                    Bridge.getLog().error(null,
-                            String.format(
-                                    "Failed to find style '%s' in current theme", defStyleName),
-                            null /*data*/);
-                }
-            } else if (defStyleRes != 0) {
-                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
-                if (value == null) {
-                    value = mProjectCallback.resolveResourceId(defStyleRes);
-                }
-
-                if (value != null) {
-                    if (value.getFirst() == ResourceType.STYLE) {
-                        // look for the style in the current theme, and its parent:
-                        ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
-                        if (item != null) {
-                            if (item instanceof StyleResourceValue) {
-                                if (defaultPropMap != null) {
-                                    defaultPropMap.put("style", item.getName());
-                                }
-
-                                defStyleValues = (StyleResourceValue)item;
+            if (value != null) {
+                if (value.getFirst() == ResourceType.STYLE) {
+                    // look for the style in the current theme, and its parent:
+                    ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+                    if (item != null) {
+                        if (item instanceof StyleResourceValue) {
+                            if (defaultPropMap != null) {
+                                defaultPropMap.put("style", item.getName());
                             }
-                        } else {
-                            Bridge.getLog().error(null,
-                                    String.format(
-                                            "Style with id 0x%x (resolved to '%s') does not exist.",
-                                            defStyleRes, value.getSecond()),
-                                    null /*data*/);
+
+                            defStyleValues = (StyleResourceValue)item;
                         }
                     } else {
                         Bridge.getLog().error(null,
                                 String.format(
-                                        "Resouce id 0x%x is not of type STYLE (instead %s)",
-                                        defStyleRes, value.getFirst().toString()),
+                                        "Style with id 0x%x (resolved to '%s') does not exist.",
+                                        defStyleRes, value.getSecond()),
                                 null /*data*/);
                     }
                 } else {
                     Bridge.getLog().error(null,
                             String.format(
-                                    "Failed to find style with id 0x%x in current theme",
-                                    defStyleRes),
+                                    "Resouce id 0x%x is not of type STYLE (instead %s)",
+                                    defStyleRes, value.getFirst().toString()),
                             null /*data*/);
                 }
+            } else {
+                Bridge.getLog().error(null,
+                        String.format(
+                                "Failed to find style with id 0x%x in current theme",
+                                defStyleRes),
+                        null /*data*/);
             }
         }
 
@@ -623,8 +622,13 @@
                 if (value == null) {
                     ResourceValue resValue = null;
 
-                    // look for the value in the defStyle first (and its parent if needed)
-                    if (defStyleValues != null) {
+                    // look for the value in the custom style first (and its parent if needed)
+                    if (customStyleValues != null) {
+                        resValue = mRenderResources.findItemInStyle(customStyleValues, name);
+                    }
+
+                    // then look for the value in the default Style (and its parent if needed)
+                    if (resValue == null && defStyleValues != null) {
                         resValue = mRenderResources.findItemInStyle(defStyleValues, name);
                     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index d6bbebd..7c90a31 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -36,7 +36,7 @@
 import android.view.ViewGroup;
 
 import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
 
 /**
  * Custom implementation of {@link LayoutInflater} to handle custom views.
@@ -177,7 +177,7 @@
                     try {
                         KXmlParser parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(f));
+                        parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
 
                         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                                 parser, bridgeContext, false);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 273e493..345f053 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -46,7 +46,6 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.InputStream;
 
 /**
@@ -244,7 +243,7 @@
                         // give that to our XmlBlockParser
                         parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(xml));
+                        parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
                     }
                 }
 
@@ -282,7 +281,7 @@
                     // give that to our XmlBlockParser
                     parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(xml));
+                    parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
 
                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                 }
@@ -501,7 +500,7 @@
                     try {
                         KXmlParser parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(f));
+                        parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                         return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                     } catch (XmlPullParserException e) {
@@ -536,7 +535,7 @@
         try {
             KXmlParser parser = new KXmlParser();
             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-            parser.setInput(new FileReader(f));
+            parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
             return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
         } catch (XmlPullParserException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index b9f769f..d4600a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -36,10 +36,11 @@
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
 import android.view.ViewGroup.LayoutParams;
 
 import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
 import java.util.Arrays;
 import java.util.Map;
 
@@ -315,7 +316,7 @@
             try {
                 KXmlParser parser = new KXmlParser();
                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                parser.setInput(new FileReader(f));
+                parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                         parser, mContext, resValue.isFramework());
@@ -471,40 +472,23 @@
      */
     @Override
     public int getDimensionPixelSize(int index, int defValue) {
-        if (mResourceData[index] == null) {
+        try {
+            return getDimension(index);
+        } catch (RuntimeException e) {
+            if (mResourceData[index] != null) {
+                String s = mResourceData[index].getValue();
+
+                if (s != null) {
+                    // looks like we were unable to resolve the dimension value
+                    Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                            String.format(
+                                "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+                                s, mNames[index]), null /*data*/);
+                }
+            }
+
             return defValue;
         }
-
-        String s = mResourceData[index].getValue();
-
-        if (s == null) {
-            return defValue;
-        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
-                s.equals(BridgeConstants.FILL_PARENT)) {
-            return LayoutParams.MATCH_PARENT;
-        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
-            return LayoutParams.WRAP_CONTENT;
-        } else if (RenderResources.REFERENCE_NULL.equals(s)) {
-            return defValue;
-        }
-
-        if (ResourceHelper.stringToFloat(s, mValue)) {
-            float f = mValue.getDimension(mBridgeResources.mMetrics);
-
-            final int res = (int)(f+0.5f);
-            if (res != 0) return res;
-            if (f == 0) return 0;
-            if (f > 0) return 1;
-            return defValue; // this is basically unreachable.
-        }
-
-        // looks like we were unable to resolve the dimension value
-        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
-                String.format(
-                    "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
-                    s, mNames[index]), null /*data*/);
-
-        return defValue;
     }
 
     /**
@@ -521,7 +505,20 @@
      */
     @Override
     public int getLayoutDimension(int index, String name) {
-        return getDimensionPixelSize(index, 0);
+        try {
+            // this will throw an exception
+            return getDimension(index);
+        } catch (RuntimeException e) {
+
+            if (LayoutInflater_Delegate.sIsInInclude) {
+                throw new RuntimeException();
+            }
+
+            Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                    "You must supply a " + name + " attribute.", null);
+
+            return 0;
+        }
     }
 
     @Override
@@ -529,6 +526,36 @@
         return getDimensionPixelSize(index, defValue);
     }
 
+    private int getDimension(int index) {
+        if (mResourceData[index] == null) {
+            throw new RuntimeException();
+        }
+
+        String s = mResourceData[index].getValue();
+
+        if (s == null) {
+            throw new RuntimeException();
+        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+                s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.MATCH_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+            throw new RuntimeException();
+        }
+
+        if (ResourceHelper.stringToFloat(s, mValue)) {
+            float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+            final int res = (int)(f+0.5f);
+            if (res != 0) return res;
+            if (f == 0) return 0;
+            if (f > 0) return 1;
+        }
+
+        throw new RuntimeException();
+    }
+
     /**
      * Retrieve a fractional unit attribute at <var>index</var>.
      *
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 0c4b0d3..060e6ee 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -73,7 +73,7 @@
         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
         parser.setInput(
                 getClass().getResourceAsStream(layoutPath),
-                "UTF8");
+                "UTF8"); //$NON-NLS-1$
 
         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                 parser, (BridgeContext) context, false /*platformFile*/);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 8e80c21..6194f5d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -29,6 +29,7 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.ResourceType;
 
+import android.os.HandlerThread_Delegate;
 import android.util.DisplayMetrics;
 
 import java.util.concurrent.TimeUnit;
@@ -228,6 +229,10 @@
     private void tearDown() {
         // Make sure to remove static references, otherwise we could not unload the lib
         mContext.disposeResources();
+
+        // quit HandlerThread created during this session.
+        HandlerThread_Delegate.cleanUp(sCurrentContext);
+
         sCurrentContext = null;
 
         Bridge.setLog(null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 96ab30f..e5efa4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -44,7 +44,6 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -124,7 +123,7 @@
                     // providing an XmlPullParser
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(f));
+                    parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                     BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                             parser, context, resValue.isFramework());
@@ -206,7 +205,7 @@
                     // let the framework inflate the Drawable from the XML file.
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(f));
+                    parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                     BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                             parser, context, value.isFramework());
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 3252fb49..70d5446 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -44,7 +44,7 @@
 
         InputStream input = this.getClass().getClassLoader().getResourceAsStream(
             "com/android/layoutlib/testdata/layout1.xml");
-        parser.setInput(input, null /*encoding*/);
+        parser.setInput(input, "UTF-8"); //$NON-NLS-1$
 
         assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index eff6bbc..5c60318 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -99,8 +99,10 @@
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.graphics.BitmapFactory#finishDecode",
         "android.os.Handler#sendMessageAtTime",
+        "android.os.HandlerThread#run",
         "android.os.Build#getString",
         "android.view.LayoutInflater#rInflate",
+        "android.view.LayoutInflater#parseInclude",
         "android.view.View#isInEditMode",
         "com.android.internal.util.XmlUtils#convertValueToInt",
         // TODO: comment out once DelegateClass is working