Merge "Import revised translations."
diff --git a/api/current.xml b/api/current.xml
index 22d82ce..2b10d42 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -10025,6 +10025,39 @@
  visibility="public"
 >
 </field>
+<field name="textEditSuggestionItemLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843626"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="textEditSuggestionsBottomWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843624"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="textEditSuggestionsTopWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843625"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textFilterEnabled"
  type="int"
  transient="false"
@@ -10146,6 +10179,17 @@
  visibility="public"
 >
 </field>
+<field name="textSuggestionsWindowStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843623"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textViewStyle"
  type="int"
  transient="false"
@@ -213125,6 +213169,17 @@
  visibility="public"
 >
 </method>
+<method name="getModifiers"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getNumber"
  return="char"
  abstract="false"
@@ -267841,7 +267896,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index fa55520..8a9bef0 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -603,7 +603,7 @@
      */
     public boolean setAudioState(BluetoothDevice device, int state) {
         if (DBG) log("setAudioState");
-        if (mService != null && isEnabled()) {
+        if (mService != null && !isDisabled()) {
             try {
                 return mService.setAudioState(device, state);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -622,7 +622,7 @@
      */
     public int getAudioState(BluetoothDevice device) {
         if (DBG) log("getAudioState");
-        if (mService != null && isEnabled()) {
+        if (mService != null && !isDisabled()) {
             try {
                 return mService.getAudioState(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -705,6 +705,11 @@
        return false;
     }
 
+    private boolean isDisabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+       return false;
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
        if (device == null) return false;
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7bdd1b9..afc2722 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1908,6 +1908,21 @@
             "android.intent.action.HDMI_AUDIO_PLUG";
 
     /**
+     * <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
+     * <ul>
+     *   <li><em>state</em> - A boolean value indicating whether the settings is on or off.</li>
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ADVANCED_SETTINGS_CHANGED
+            = "android.intent.action.ADVANCED_SETTINGS";
+
+    /**
      * Broadcast Action: An outgoing call is about to be placed.
      *
      * <p>The Intent will have the following extra value:
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 9c36b12..f6a114c 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -78,6 +78,14 @@
             this.prefixLength == linkAddress.prefixLength;
     }
 
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == address) ? 0 : address.hashCode()) + prefixLength;
+    }
+
     /**
      * Returns the InetAddress for this address.
      */
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 81d62a0..e88292f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -19,11 +19,9 @@
 import android.net.ProxyProperties;
 import android.os.Parcelable;
 import android.os.Parcel;
-import android.util.Log;
+import android.text.TextUtils;
 
 import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -141,7 +139,7 @@
         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
 
         String linkAddresses = "LinkAddresses: [";
-        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString();
+        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
         linkAddresses += "] ";
 
         String dns = "DnsAddresses: [";
@@ -156,6 +154,67 @@
         return ifaceName + linkAddresses + gateways + dns + proxy;
     }
 
+
+    @Override
+    /**
+     * Compares this {@code LinkProperties} instance against the target
+     * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
+     * all their fields are equal in values.
+     *
+     * For collection fields, such as mDnses, containsAll() is used to check
+     * if two collections contains the same elements, independent of order.
+     * There are two thoughts regarding containsAll()
+     * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
+     * 2. Worst case performance is O(n^2).
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof LinkProperties)) return false;
+
+        boolean sameAddresses;
+        boolean sameDnses;
+        boolean sameGateways;
+
+        LinkProperties target = (LinkProperties) obj;
+
+        Collection<InetAddress> targetAddresses = target.getAddresses();
+        Collection<InetAddress> sourceAddresses = getAddresses();
+        sameAddresses = (sourceAddresses.size() == targetAddresses.size()) ?
+                sourceAddresses.containsAll(targetAddresses) : false;
+
+        Collection<InetAddress> targetDnses = target.getDnses();
+        sameDnses = (mDnses.size() == targetDnses.size()) ?
+                mDnses.containsAll(targetDnses) : false;
+
+        Collection<InetAddress> targetGateways = target.getGateways();
+        sameGateways = (mGateways.size() == targetGateways.size()) ?
+                mGateways.containsAll(targetGateways) : false;
+
+        return
+            sameAddresses && sameDnses && sameGateways
+            && TextUtils.equals(getInterfaceName(), target.getInterfaceName())
+            && (getHttpProxy() == null ? target.getHttpProxy() == null :
+                getHttpProxy().equals(target.getHttpProxy()));
+    }
+
+    @Override
+    /**
+     * generate hashcode based on significant fields
+     * Equal objects must produce the same hash code, while unequal objects
+     * may have the same hash codes.
+     */
+    public int hashCode() {
+        return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+                + mLinkAddresses.size() * 31
+                + mDnses.size() * 37
+                + mGateways.size() * 41
+                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
+    }
+
     /**
      * Implement the Parcelable interface.
      * @hide
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index cbe4445..44dbec1 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -163,6 +163,16 @@
         return 0;
     }
 
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == mHost) ? 0 : mHost.hashCode())
+        + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+        + mPort;
+    }
+
     /**
      * Implement the Parcelable interface.
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c75b0fe..8a19456 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1103,6 +1103,18 @@
         public static final int END_BUTTON_BEHAVIOR_DEFAULT = END_BUTTON_BEHAVIOR_SLEEP;
 
         /**
+         * Is advanced settings mode turned on. 0 == no, 1 == yes
+         * @hide
+         */
+        public static final String ADVANCED_SETTINGS = "advanced_settings";
+
+        /**
+         * ADVANCED_SETTINGS default value.
+         * @hide
+         */
+        public static final int ADVANCED_SETTINGS_DEFAULT = 0;
+
+        /**
          * Whether Airplane Mode is on.
          */
         public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 132c346..ca2212c 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -83,19 +83,6 @@
                     onBluetoothDisable();
                     break;
                 }
-            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
-                int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-                                                   BluetoothDevice.ERROR);
-                switch(bondState) {
-                case BluetoothDevice.BOND_BONDED:
-                    if (getPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
-                        setPriority(device, BluetoothA2dp.PRIORITY_ON);
-                    }
-                    break;
-                case BluetoothDevice.BOND_NONE:
-                    setPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
-                    break;
-                }
             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
                 synchronized (this) {
                     if (mAudioDevices.containsKey(device)) {
@@ -158,7 +145,6 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
         mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index 2304a70..a36cd24 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -18,6 +18,9 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
@@ -68,6 +71,8 @@
     private final Context mContext;
     private final BluetoothService mService;
     private final BluetoothInputProfileHandler mBluetoothInputProfileHandler;
+    private BluetoothA2dp mA2dpProxy;
+    private BluetoothHeadset mHeadsetProxy;
 
     BluetoothBondState(Context context, BluetoothService service) {
         mContext = context;
@@ -126,14 +131,15 @@
 
         if (state == BluetoothDevice.BOND_BONDED) {
             mService.addProfileState(address);
+        } else if (state == BluetoothDevice.BOND_BONDING) {
+            if (mA2dpProxy == null || mHeadsetProxy == null) {
+                getProfileProxy();
+            }
         } else if (state == BluetoothDevice.BOND_NONE) {
             mService.removeProfileState(address);
         }
 
-        // HID is handled by BluetoothService, other profiles
-        // will be handled by their respective services.
-        mBluetoothInputProfileHandler.setInitialInputDevicePriority(
-            mService.getRemoteDevice(address), state);
+        setProfilePriorities(address, state);
 
         if (DBG) {
             Log.d(TAG, address + " bond state " + oldState + " -> " + state
@@ -261,6 +267,52 @@
         mPinAttempt.put(address, new Integer(newAttempt));
     }
 
+    private void getProfileProxy() {
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (mA2dpProxy == null) {
+            bluetoothAdapter.getProfileProxy(mContext, mProfileServiceListener,
+                                             BluetoothProfile.A2DP);
+        }
+
+        if (mHeadsetProxy == null) {
+            bluetoothAdapter.getProfileProxy(mContext, mProfileServiceListener,
+                                             BluetoothProfile.HEADSET);
+        }
+    }
+
+    private void closeProfileProxy() {
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (mA2dpProxy != null) {
+            bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dpProxy);
+        }
+
+        if (mHeadsetProxy != null) {
+            bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetProxy);
+        }
+    }
+
+    private BluetoothProfile.ServiceListener mProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.A2DP) {
+                mA2dpProxy = (BluetoothA2dp) proxy;
+            } else if (profile == BluetoothProfile.HEADSET) {
+                mHeadsetProxy = (BluetoothHeadset) proxy;
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.A2DP) {
+                mA2dpProxy = null;
+            } else if (profile == BluetoothProfile.HEADSET) {
+                mHeadsetProxy = null;
+            }
+        }
+    };
+
     private void copyAutoPairingData() {
         FileInputStream in = null;
         FileOutputStream out = null;
@@ -365,4 +417,30 @@
             }
         }
     }
+
+    // Set service priority of Hid, A2DP and Headset profiles depending on
+    // the bond state change
+    private void setProfilePriorities(String address, int state) {
+        BluetoothDevice remoteDevice = mService.getRemoteDevice(address);
+        // HID is handled by BluetoothService
+        mBluetoothInputProfileHandler.setInitialInputDevicePriority(remoteDevice, state);
+
+        // Set service priority of A2DP and Headset
+        // We used to do the priority change in the 2 services after the broadcast
+        //   intent reach them. But that left a small time gap that could reject
+        //   incoming connection due to undefined priorities.
+        if (state == BluetoothDevice.BOND_BONDED) {
+            if (mA2dpProxy.getPriority(remoteDevice) == BluetoothProfile.PRIORITY_UNDEFINED) {
+                mA2dpProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_ON);
+            }
+
+            if (mHeadsetProxy.getPriority(remoteDevice) == BluetoothProfile.PRIORITY_UNDEFINED) {
+                mHeadsetProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_ON);
+            }
+        } else if (state == BluetoothDevice.BOND_NONE) {
+            mA2dpProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_UNDEFINED);
+            mHeadsetProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_UNDEFINED);
+        }
+    }
+
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 28541fe..66f37f2 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -645,8 +645,21 @@
                     }
     
                     attachInfo.mIgnoreDirtyState = false;
-    
+
+                    final long swapBuffersStartTime;
+                    if (ViewDebug.DEBUG_LATENCY) {
+                        swapBuffersStartTime = System.nanoTime();
+                    }
+
                     sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+
+                    if (ViewDebug.DEBUG_LATENCY) {
+                        long now = System.nanoTime();
+                        Log.d(LOG_TAG, "Latency: Spent "
+                                + ((now - swapBuffersStartTime) * 0.000001f)
+                                + "ms waiting for eglSwapBuffers()");
+                    }
+
                     checkEglErrors();
                 }
             }
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 87e7ea7..01ddcc9 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -106,6 +106,13 @@
      */
     public abstract void setTainted(boolean tainted);
 
+    /**
+     * Returns the time (in ns) when this specific event was generated.
+     * The value is in nanosecond precision but it may not have nanosecond accuracy.
+     * @hide
+     */
+    public abstract long getEventTimeNano();
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 4320160..13d8809 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1807,12 +1807,33 @@
      * @see #META_CAPS_LOCK_ON
      * @see #META_NUM_LOCK_ON
      * @see #META_SCROLL_LOCK_ON
+     * @see #getModifiers
      */
     public final int getMetaState() {
         return mMetaState;
     }
 
     /**
+     * Returns the state of the modifier keys.
+     * <p>
+     * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK},
+     * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are
+     * not considered modifier keys.  Consequently, this function specifically masks out
+     * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}.
+     * </p><p>
+     * The value returned consists of the meta state (from {@link #getMetaState})
+     * normalized using {@link #normalizeMetaState(int)} and then masked with
+     * {@link #getModifierMetaStateMask} so that only valid modifier bits are retained.
+     * </p>
+     *
+     * @return An integer in which each bit set to 1 represents a pressed modifier key.
+     * @see #getMetaState
+     */
+    public final int getModifiers() {
+        return normalizeMetaState(mMetaState) & META_MODIFIER_MASK;
+    }
+
+    /**
      * Returns the flags for this key event.
      *
      * @see #FLAG_WOKE_HERE
@@ -2319,6 +2340,12 @@
         return mEventTime;
     }
 
+    /** @hide */
+    @Override
+    public final long getEventTimeNano() {
+        return mEventTime * 1000000L;
+    }
+
     /**
      * Renamed to {@link #getDeviceId}.
      * 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index c19a107..89736d6 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -141,6 +141,22 @@
     public static final boolean DEBUG_DRAG = false;
 
     /**
+     * Enables logging of factors that affect the latency and responsiveness of an application.
+     *
+     * Logs the relative difference between the time an event was created and the time it
+     * was delivered.
+     *
+     * Logs the time spent waiting for Surface.lockCanvas() or eglSwapBuffers().
+     * This is time that the event loop spends blocked and unresponsive.  Ideally, drawing
+     * and animations should be perfectly synchronized with VSYNC so that swap buffers
+     * is instantaneous.
+     *
+     * Logs the time spent in ViewRoot.performTraversals() or ViewRoot.draw().
+     * @hide
+     */
+    public static final boolean DEBUG_LATENCY = false;
+
+    /**
      * <p>Enables or disables views consistency check. Even when this property is enabled,
      * view consistency checks happen only if {@link android.util.Config#DEBUG} is set
      * to true. The value of this property can be configured externally in one of the
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 2f9d501..a899b46 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -186,6 +186,8 @@
     final Rect mVisRect; // used to retrieve visible rect of focused view.
 
     boolean mTraversalScheduled;
+    long mLastTraversalFinishedTimeNanos;
+    long mLastDrawDurationNanos;
     boolean mWillDrawSoon;
     boolean mLayoutRequested;
     boolean mFirst;
@@ -671,6 +673,14 @@
     public void scheduleTraversals() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
+
+            if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
+                final long now = System.nanoTime();
+                Log.d(TAG, "Latency: Scheduled traversal, it has been "
+                        + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+                        + "ms since the last traversal finished.");
+            }
+
             sendEmptyMessage(DO_TRAVERSAL);
         }
     }
@@ -1389,8 +1399,18 @@
 
         if (!cancelDraw && !newSurface) {
             mFullRedrawNeeded = false;
+
+            final long drawStartTime;
+            if (ViewDebug.DEBUG_LATENCY) {
+                drawStartTime = System.nanoTime();
+            }
+
             draw(fullRedrawNeeded);
 
+            if (ViewDebug.DEBUG_LATENCY) {
+                mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
+            }
+
             if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
                     || mReportNextDraw) {
                 if (LOCAL_LOGV) {
@@ -1601,8 +1621,20 @@
                 int right = dirty.right;
                 int bottom = dirty.bottom;
 
+                final long lockCanvasStartTime;
+                if (ViewDebug.DEBUG_LATENCY) {
+                    lockCanvasStartTime = System.nanoTime();
+                }
+
                 canvas = surface.lockCanvas(dirty);
 
+                if (ViewDebug.DEBUG_LATENCY) {
+                    long now = System.nanoTime();
+                    Log.d(TAG, "Latency: Spent "
+                            + ((now - lockCanvasStartTime) * 0.000001f)
+                            + "ms waiting for surface.lockCanvas()");
+                }
+
                 if (left != dirty.left || top != dirty.top || right != dirty.right ||
                         bottom != dirty.bottom) {
                     mAttachInfo.mIgnoreDirtyState = true;
@@ -2011,8 +2043,24 @@
                 Debug.startMethodTracing("ViewRoot");
             }
 
+            final long traversalStartTime;
+            if (ViewDebug.DEBUG_LATENCY) {
+                traversalStartTime = System.nanoTime();
+                mLastDrawDurationNanos = 0;
+            }
+
             performTraversals();
 
+            if (ViewDebug.DEBUG_LATENCY) {
+                long now = System.nanoTime();
+                Log.d(TAG, "Latency: Spent "
+                        + ((now - traversalStartTime) * 0.000001f)
+                        + "ms in performTraversals(), with "
+                        + (mLastDrawDurationNanos * 0.000001f)
+                        + "ms of that time in draw()");
+                mLastTraversalFinishedTimeNanos = now;
+            }
+
             if (mProfile) {
                 Debug.stopMethodTracing();
                 mProfile = false;
@@ -2180,25 +2228,68 @@
         }
     }
     
-    private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
+    private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
         if (mFinishedCallback != null) {
             Slog.w(TAG, "Received a new input event from the input queue but there is "
                     + "already an unfinished input event in progress.");
         }
 
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventReceiveTimeNanos = System.nanoTime();
+            mInputEventDeliverTimeNanos = 0;
+            mInputEventDeliverPostImeTimeNanos = 0;
+        }
+
         mFinishedCallback = finishedCallback;
     }
 
-    private void finishInputEvent(boolean handled) {
+    private void finishInputEvent(InputEvent event, boolean handled) {
         if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
 
-        if (mFinishedCallback != null) {
-            mFinishedCallback.finished(handled);
-            mFinishedCallback = null;
-        } else {
+        if (mFinishedCallback == null) {
             Slog.w(TAG, "Attempted to tell the input queue that the current input event "
                     + "is finished but there is no input event actually in progress.");
+            return;
         }
+
+        if (ViewDebug.DEBUG_LATENCY) {
+            final long now = System.nanoTime();
+            final long eventTime = event.getEventTimeNano();
+            final StringBuilder msg = new StringBuilder();
+            msg.append("Latency: Spent ");
+            msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f);
+            msg.append("ms processing ");
+            if (event instanceof KeyEvent) {
+                final KeyEvent  keyEvent = (KeyEvent)event;
+                msg.append("key event, action=");
+                msg.append(KeyEvent.actionToString(keyEvent.getAction()));
+            } else {
+                final MotionEvent motionEvent = (MotionEvent)event;
+                msg.append("motion event, action=");
+                msg.append(MotionEvent.actionToString(motionEvent.getAction()));
+                msg.append(", historySize=");
+                msg.append(motionEvent.getHistorySize());
+            }
+            msg.append(", handled=");
+            msg.append(handled);
+            msg.append(", received at +");
+            msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f);
+            if (mInputEventDeliverTimeNanos != 0) {
+                msg.append("ms, delivered at +");
+                msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f);
+            }
+            if (mInputEventDeliverPostImeTimeNanos != 0) {
+                msg.append("ms, delivered post IME at +");
+                msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f);
+            }
+            msg.append("ms, finished at +");
+            msg.append((now - eventTime) * 0.000001f);
+            msg.append("ms.");
+            Log.d(TAG, msg.toString());
+        }
+
+        mFinishedCallback.finished(handled);
+        mFinishedCallback = null;
     }
     
     /**
@@ -2323,6 +2414,10 @@
     }
 
     private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventDeliverTimeNanos = System.nanoTime();
+        }
+
         if (mInputEventConsistencyVerifier != null) {
             if (event.isTouchEvent()) {
                 mInputEventConsistencyVerifier.onTouchEvent(event, 0);
@@ -2425,7 +2520,7 @@
     private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
         event.recycle();
         if (sendDone) {
-            finishInputEvent(handled);
+            finishInputEvent(event, handled);
         }
         if (LOCAL_LOGV || WATCH_POINTER) {
             if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -2435,6 +2530,10 @@
     }
 
     private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventDeliverTimeNanos = System.nanoTime();
+        }
+
         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
 
         if (mInputEventConsistencyVerifier != null) {
@@ -2569,6 +2668,10 @@
     }
 
     private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventDeliverTimeNanos = System.nanoTime();
+        }
+
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
         }
@@ -2808,6 +2911,10 @@
     }
 
     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventDeliverTimeNanos = System.nanoTime();
+        }
+
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
         }
@@ -2858,6 +2965,10 @@
     }
 
     private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
+        if (ViewDebug.DEBUG_LATENCY) {
+            mInputEventDeliverPostImeTimeNanos = System.nanoTime();
+        }
+
         // If the view went away, then the event will not be handled.
         if (mView == null || !mAdded) {
             finishKeyEvent(event, sendDone, false);
@@ -2971,7 +3082,7 @@
 
     private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
         if (sendDone) {
-            finishInputEvent(handled);
+            finishInputEvent(event, handled);
         }
     }
 
@@ -3262,16 +3373,19 @@
         sendMessage(msg);
     }
     
+    private long mInputEventReceiveTimeNanos;
+    private long mInputEventDeliverTimeNanos;
+    private long mInputEventDeliverPostImeTimeNanos;
     private InputQueue.FinishedCallback mFinishedCallback;
     
     private final InputHandler mInputHandler = new InputHandler() {
         public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
-            startInputEvent(finishedCallback);
+            startInputEvent(event, finishedCallback);
             dispatchKey(event, true);
         }
 
         public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
-            startInputEvent(finishedCallback);
+            startInputEvent(event, finishedCallback);
             dispatchMotion(event, true);
         }
     };
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 3d15968..0918683 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -114,13 +114,6 @@
         return mVideoSurfaceView;
     }
 
-    @Override
-    public void start() {
-        if (getAutostart()) {
-            super.start();
-        }
-    }
-
     HTML5VideoFullScreen(Context context, int videoLayerId, int position,
             boolean autoStart) {
         mVideoSurfaceView = new VideoSurfaceView(context);
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 14157c2..060c0bb 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -232,8 +232,9 @@
         }
 
         public static void onPrepared() {
-            // The VideoView will decide whether to really kick off to play.
-            mHTML5VideoView.start();
+            if (!mHTML5VideoView.isFullScreenMode() || mHTML5VideoView.getAutostart()) {
+                mHTML5VideoView.start();
+            }
             if (mBaseLayer != 0) {
                 setBaseLayer(mBaseLayer);
             }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e0783f3..f55608c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2382,6 +2382,8 @@
      */
     public void setTitleBarGravity(int gravity) {
         mTitleGravity = gravity;
+        // force refresh
+        invalidate();
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 86dfb9b..dc10624 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -43,7 +43,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -88,6 +87,7 @@
 import android.text.style.UpdateAppearance;
 import android.text.util.Linkify;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.FloatMath;
 import android.util.Log;
 import android.util.TypedValue;
@@ -311,6 +311,10 @@
     private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
     private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
 
+    private int mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout;
+    private int mTextEditSuggestionItemLayout;
+    private SuggestionsPopupWindow mSuggestionsPopupWindow;
+
     private int mCursorDrawableRes;
     private final Drawable[] mCursorDrawable = new Drawable[2];
     private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
@@ -779,6 +783,18 @@
                 mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0);
                 break;
 
+            case com.android.internal.R.styleable.TextView_textEditSuggestionsBottomWindowLayout:
+                mTextEditSuggestionsBottomWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textEditSuggestionsTopWindowLayout:
+                mTextEditSuggestionsTopWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
+                mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
+                break;
+
             case com.android.internal.R.styleable.TextView_textIsSelectable:
                 mTextIsSelectable = a.getBoolean(attr, false);
                 break;
@@ -7343,6 +7359,7 @@
                     startSelectionActionMode();
                 } else {
                     stopSelectionActionMode();
+                    hideSuggestions();
                     if (hasInsertionController() && !selectAllGotFocus && mText.length() > 0) {
                         getInsertionController().show();
                     }
@@ -7776,7 +7793,7 @@
                 // Cases where the text ends with a '.' and we select from the end of the line
                 // (right after the dot), or when we select from the space character in "aaa, bbb".
                 continue;
-            }              
+            }
             if (type == Character.SURROGATE) { // Two Character codepoint
                 end = start - 1; // Recheck as a pair when scanning forward
                 continue;
@@ -8221,6 +8238,201 @@
         return ((minOffset >= selectionStart) && (maxOffset < selectionEnd));
     }
 
+    private class SuggestionsPopupWindow implements OnClickListener {
+        private static final int MAX_NUMBER_SUGGESTIONS = 5;
+        private static final long NO_SUGGESTIONS = -1L;
+        private final PopupWindow mContainer;
+        private final ViewGroup[] mSuggestionViews = new ViewGroup[2];
+        private final int[] mSuggestionViewLayouts = new int[] {
+                mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout};
+
+        public SuggestionsPopupWindow() {
+            mContainer = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSuggestionsWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
+            mContainer.setClippingEnabled(false);
+            mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+
+            mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+            mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        private ViewGroup getViewGroup(boolean under) {
+            final int viewIndex = under ? 0 : 1;
+            ViewGroup viewGroup = mSuggestionViews[viewIndex];
+
+            if (viewGroup == null) {
+                final int layout = mSuggestionViewLayouts[viewIndex];
+                LayoutInflater inflater = (LayoutInflater) TextView.this.mContext.
+                        getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+                if (inflater == null) {
+                    throw new IllegalArgumentException(
+                            "Unable to create TextEdit suggestion window inflater");
+                }
+
+                View view = inflater.inflate(layout, null);
+
+                if (! (view instanceof ViewGroup)) {
+                    throw new IllegalArgumentException(
+                            "Inflated TextEdit suggestion window is not a ViewGroup: " + view);
+                }
+
+                viewGroup = (ViewGroup) view;
+
+                // Inflate the suggestion items once and for all.
+                for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+                    View childView = inflater.inflate(mTextEditSuggestionItemLayout, viewGroup,
+                            false);
+
+                    if (! (childView instanceof TextView)) {
+                        throw new IllegalArgumentException(
+                               "Inflated TextEdit suggestion item is not a TextView: " + childView);
+                    }
+
+                    viewGroup.addView(childView);
+                    childView.setOnClickListener(this);
+                }
+
+                mSuggestionViews[viewIndex] = viewGroup;
+            }
+
+            return viewGroup;
+        }
+
+        public void show() {
+            if (!(mText instanceof Editable)) return;
+
+            final int pos = TextView.this.getSelectionStart();
+            Spannable spannable = (Spannable)TextView.this.mText;
+            CorrectionSpan[] correctionSpans = spannable.getSpans(pos, pos, CorrectionSpan.class);
+            final int nbSpans = correctionSpans.length;
+
+            ViewGroup viewGroup = getViewGroup(true);
+            mContainer.setContentView(viewGroup);
+
+            int totalNbSuggestions = 0;
+            for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
+                CorrectionSpan correctionSpan = correctionSpans[spanIndex];
+                final int spanStart = spannable.getSpanStart(correctionSpan);
+                final int spanEnd = spannable.getSpanEnd(correctionSpan);
+                final Long spanRange = packRangeInLong(spanStart, spanEnd);
+
+                String[] suggestions = correctionSpan.getSuggestions();
+                int nbSuggestions = suggestions.length;
+                for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
+                    TextView textView = (TextView) viewGroup.getChildAt(totalNbSuggestions);
+                    textView.setText(suggestions[suggestionIndex]);
+                    textView.setTag(spanRange);
+
+                    totalNbSuggestions++;
+                    if (totalNbSuggestions == MAX_NUMBER_SUGGESTIONS) {
+                        spanIndex = nbSpans;
+                        break;
+                    }
+                }
+            }
+
+            if (totalNbSuggestions == 0) {
+                // TODO Replace by final text, use a dedicated layout, add a fade out timer...
+                TextView textView = (TextView) viewGroup.getChildAt(0);
+                textView.setText("No suggestions available");
+                textView.setTag(NO_SUGGESTIONS);
+                totalNbSuggestions++;
+            }
+
+            for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+                viewGroup.getChildAt(i).setVisibility(i < totalNbSuggestions ? VISIBLE : GONE);
+            }
+
+            final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+            viewGroup.measure(size, size);
+
+            positionAtCursor();
+        }
+
+        public void hide() {
+            mContainer.dismiss();
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view instanceof TextView) {
+                TextView textView = (TextView) view;
+                Long range = ((Long) view.getTag());
+                if (range != NO_SUGGESTIONS) {
+                    final int spanStart = extractRangeStartFromLong(range);
+                    final int spanEnd = extractRangeEndFromLong(range);
+                    ((Editable) mText).replace(spanStart, spanEnd, textView.getText());
+                }
+            }
+            hide();
+        }
+
+        void positionAtCursor() {
+            View contentView = mContainer.getContentView();
+            int width = contentView.getMeasuredWidth();
+            int height = contentView.getMeasuredHeight();
+            final int offset = TextView.this.getSelectionStart();
+            final int line = mLayout.getLineForOffset(offset);
+            final int lineBottom = mLayout.getLineBottom(line);
+            float primaryHorizontal = mLayout.getPrimaryHorizontal(offset);
+
+            final Rect bounds = sCursorControllerTempRect;
+            bounds.left = (int) (primaryHorizontal - width / 2.0f);
+            bounds.top = lineBottom;
+
+            bounds.right = bounds.left + width;
+            bounds.bottom = bounds.top + height;
+
+            convertFromViewportToContentCoordinates(bounds);
+
+            final int[] coords = mTempCoords;
+            TextView.this.getLocationInWindow(coords);
+            coords[0] += bounds.left;
+            coords[1] += bounds.top;
+
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            final int screenHeight = displayMetrics.heightPixels;
+
+            // Vertical clipping
+            if (coords[1] + height > screenHeight) {
+                // Try to position above current line instead
+                // TODO use top layout instead, reverse suggestion order,
+                // try full screen vertical down if it still does not fit. TBD with designers.
+
+                // Update dimensions from new view
+                contentView = mContainer.getContentView();
+                width = contentView.getMeasuredWidth();
+                height = contentView.getMeasuredHeight();
+
+                final int lineTop = mLayout.getLineTop(line);
+                final int lineHeight = lineBottom - lineTop;
+                coords[1] -= height + lineHeight;
+            }
+
+            // Horizontal clipping
+            coords[0] = Math.max(0, coords[0]);
+            coords[0] = Math.min(displayMetrics.widthPixels - width, coords[0]);
+
+            mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+        }
+    }
+
+    void showSuggestions() {
+        if (mSuggestionsPopupWindow == null) {
+            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+        }
+        hideControllers();
+        mSuggestionsPopupWindow.show();
+    }
+
+    void hideSuggestions() {
+        if (mSuggestionsPopupWindow != null) {
+            mSuggestionsPopupWindow.hide();
+        }
+    }
+
     /**
      * If provided, this ActionMode.Callback will be used to create the ActionMode when text
      * selection is initiated in this View.
@@ -8429,16 +8641,14 @@
         }
     }
 
-    private class PastePopupMenu implements OnClickListener {
+    private class PastePopupWindow implements OnClickListener {
         private final PopupWindow mContainer;
-        private int mPositionX;
-        private int mPositionY;
         private final View[] mPasteViews = new View[4];
         private final int[] mPasteViewLayouts = new int[] { 
                 mTextEditPasteWindowLayout,  mTextEditNoPasteWindowLayout, 
                 mTextEditSidePasteWindowLayout, mTextEditSideNoPasteWindowLayout };
         
-        public PastePopupMenu() {
+        public PastePopupWindow() {
             mContainer = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
             mContainer.setSplitTouchEnabled(true);
@@ -8521,14 +8731,10 @@
 
             convertFromViewportToContentCoordinates(bounds);
 
-            mPositionX = bounds.left;
-            mPositionY = bounds.top;
-
-
             final int[] coords = mTempCoords;
             TextView.this.getLocationInWindow(coords);
-            coords[0] += mPositionX;
-            coords[1] += mPositionY;
+            coords[0] += bounds.left;
+            coords[1] += bounds.top;
 
             final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
             if (coords[1] < 0) {
@@ -8873,7 +9079,7 @@
         void hideAssociatedPopupWindow() {
             // No associated popup window by default
         }
-        
+
         public void onDetached() {
             // Should be overriden to clean possible Runnable
         }
@@ -8883,10 +9089,10 @@
         private static final int DELAY_BEFORE_FADE_OUT = 4000;
         private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
 
-        // Used to detect taps on the insertion handle, which will affect the PastePopupMenu
+        // Used to detect taps on the insertion handle, which will affect the PastePopupWindow
         private long mTouchTimer;
         private float mDownPositionX, mDownPositionY;
-        private PastePopupMenu mPastePopupWindow;
+        private PastePopupWindow mPastePopupWindow;
         private Runnable mHider;
         private Runnable mPastePopupShower;
 
@@ -9026,7 +9232,7 @@
         void showAssociatedPopupWindow() {
             if (mPastePopupWindow == null) {
                 // Lazy initialisation: create when actually shown only.
-                mPastePopupWindow = new PastePopupMenu();
+                mPastePopupWindow = new PastePopupWindow();
             }
             mPastePopupWindow.show();
         }
@@ -9237,6 +9443,7 @@
             mEndHandle.show();
 
             hideInsertionPointCursorController();
+            hideSuggestions();
         }
 
         public void hide() {
@@ -9264,7 +9471,7 @@
                             final int deltaY = y - mPreviousTapPositionY;
                             final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
                             if (distanceSquared < mSquaredTouchSlopDistance) {
-                                startSelectionActionMode();
+                                showSuggestions();
                                 mDiscardNextActionUp = true;
                             }
                         }
@@ -9354,6 +9561,7 @@
     private void hideControllers() {
         hideInsertionPointCursorController();
         stopSelectionActionMode();
+        hideSuggestions();
     }
 
     /**
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
new file mode 100644
index 0000000..88be6e1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
new file mode 100644
index 0000000..41886eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
Binary files differ
diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml
new file mode 100644
index 0000000..a54cad2
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_item.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:paddingLeft="16dip"
+          android:paddingRight="16dip"
+          android:paddingTop="8dip"
+          android:paddingBottom="8dip"
+          android:layout_gravity="center"
+          android:textAppearance="?android:attr/textAppearanceMedium"
+          android:textColor="@android:color/black" />
+
diff --git a/core/res/res/layout/text_edit_suggestions_bottom_window.xml b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
new file mode 100644
index 0000000..588bfbd
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@android:drawable/text_edit_suggestions_bottom_window">
+
+</LinearLayout>
diff --git a/core/res/res/layout/text_edit_suggestions_top_window.xml b/core/res/res/layout/text_edit_suggestions_top_window.xml
new file mode 100644
index 0000000..67faa37
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestions_top_window.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@android:drawable/text_edit_suggestions_top_window">
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b7849bb..819ce58 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -525,6 +525,9 @@
         <!-- Reference to a style that will be used for the window containing a text
              selection anchor. -->
         <attr name="textSelectHandleWindowStyle" format="reference" />
+        <!-- Reference to a style that will be used for the window containing a list of possible
+             text suggestions in an EditText. -->
+        <attr name="textSuggestionsWindowStyle" format="reference" />
         <!-- Default ListPopupWindow style. -->
         <attr name="listPopupWindowStyle" format="reference" />
         <!-- Default PopupMenu style. -->
@@ -675,6 +678,15 @@
         <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
         <attr name="textEditSideNoPasteWindowLayout" format="reference" />
 
+        <!-- Layout of a the view that is used to create the text suggestions popup window in an
+             EditText. This window will be displayed below the text line. -->
+        <attr name="textEditSuggestionsBottomWindowLayout" format="reference" />
+        <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+             above the current line of text instead of below. -->
+        <attr name="textEditSuggestionsTopWindowLayout" format="reference" />
+        <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+        <attr name="textEditSuggestionItemLayout" format="reference" />
+
         <!-- Theme to use for dialogs spawned from this theme. -->
         <attr name="dialogTheme" format="reference" />
         <!-- Window decor layout to use in dialog mode with icons -->
@@ -2819,6 +2831,16 @@
         <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
         <attr name="textEditSideNoPasteWindowLayout" />
 
+        <!-- Layout of a the view that is used to create the text suggestions popup window in an
+             EditText. This window will be displayed below the text line. -->
+        <attr name="textEditSuggestionsBottomWindowLayout" />
+        <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+             above the current line of text instead of below. -->
+        <attr name="textEditSuggestionsTopWindowLayout" />
+        <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+        <attr name="textEditSuggestionItemLayout" />
+
+
         <!-- Reference to a drawable that will be drawn under the insertion cursor. -->
         <attr name="textCursorDrawable" />
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4109ae1..d5c374d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1659,4 +1659,9 @@
 
   <public type="style" name="Theme.Holo.Light.NoActionBar" />
 
+  <public type="attr" name="textSuggestionsWindowStyle" />
+  <public type="attr" name="textEditSuggestionsBottomWindowLayout" />
+  <public type="attr" name="textEditSuggestionsTopWindowLayout" />
+  <public type="attr" name="textEditSuggestionItemLayout" />
+
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f6a3979..bf4c6d7 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -426,6 +426,9 @@
         <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
         <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item>
         <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item>
+        <item name="android:textEditSuggestionsBottomWindowLayout">?android:attr/textEditSuggestionsBottomWindowLayout</item>
+        <item name="android:textEditSuggestionsTopWindowLayout">?android:attr/textEditSuggestionsTopWindowLayout</item>
+        <item name="android:textEditSuggestionItemLayout">?android:attr/textEditSuggestionItemLayout</item>
         <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item>
     </style>
     
@@ -1047,6 +1050,17 @@
         <item name="windowExitAnimation">@android:anim/fade_out</item>
     </style>
 
+    <!-- Style for the popup window that contains text suggestions. -->
+    <style name="Widget.TextSuggestions">
+        <item name="android:popupAnimationStyle">@android:style/Animation.TextSuggestions</item>
+    </style>
+
+    <!-- Animation effects when showing/hiding the text suggestions popup window. -->
+    <style name="Animation.TextSuggestions">
+        <item name="windowEnterAnimation">@android:anim/fade_in</item>
+        <item name="windowExitAnimation">@android:anim/fade_out</item>
+    </style>
+
     <style name="Widget.ActionBar">
         <item name="android:background">@android:drawable/action_bar_background</item>
         <item name="android:displayOptions">useLogo|showHome|showTitle</item>
@@ -1465,6 +1479,9 @@
     <style name="Widget.Holo.TextSelectHandle" parent="Widget.TextSelectHandle">
     </style>
 
+    <style name="Widget.Holo.TextSuggestions" parent="Widget.TextSuggestions">
+    </style>
+
     <style name="Widget.Holo.AbsListView" parent="Widget.AbsListView">
     </style>
 
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index fdeb229..b1e4f0f 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -180,6 +180,10 @@
         <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
         <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item>
         <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.TextSuggestions</item>
+        <item name="textEditSuggestionsBottomWindowLayout">@android:layout/text_edit_suggestions_bottom_window</item>
+        <item name="textEditSuggestionsTopWindowLayout">@android:layout/text_edit_suggestions_top_window</item>
+        <item name="textEditSuggestionItemLayout">@android:layout/text_edit_suggestion_item</item>
         <item name="textCursorDrawable">@null</item>
 
         <!-- Widget styles -->
@@ -917,6 +921,7 @@
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
         <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item>
 
         <!-- Widget styles -->
@@ -1201,6 +1206,7 @@
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+        <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
         <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item>
 
         <!-- Widget styles -->
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
new file mode 100644
index 0000000..50666b4
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.LinkProperties;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.net.InetAddress;
+
+public class LinkPropertiesTest extends TestCase {
+    private static String ADDRV4 = "75.208.6.1";
+    private static String ADDRV6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+    private static String DNS1 = "75.208.7.1";
+    private static String DNS2 = "69.78.7.1";
+    private static String GATEWAY1 = "75.208.8.1";
+    private static String GATEWAY2 = "69.78.8.1";
+    private static String NAME = "qmi0";
+
+    @SmallTest
+    public void testEqualsNull() {
+        LinkProperties source = new LinkProperties();
+        LinkProperties target = new LinkProperties();
+
+        assertFalse(source == target);
+        assertTrue(source.equals(target));
+        assertTrue(source.hashCode() == target.hashCode());
+    }
+
+    @SmallTest
+    public void testEqualsSameOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+
+            LinkProperties target = new LinkProperties();
+
+            // All fields are same
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+
+            target.clear();
+            // change Interface Name
+            target.setInterfaceName("qmi1");
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            // change link addresses
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // change dnses
+            target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // change gateway
+            target.addGateway(NetworkUtils.numericToInetAddress("75.208.8.2"));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            assertFalse(source.equals(target));
+
+        } catch (Exception e) {
+            throw new RuntimeException(e.toString());
+            //fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDifferentOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+
+            LinkProperties target = new LinkProperties();
+            // Exchange order
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDuplicated() {
+        try {
+            LinkProperties source = new LinkProperties();
+            // set 3 link addresses, eg, [A, A, B]
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            LinkProperties target = new LinkProperties();
+            // set 3 link addresses, eg, [A, B, B]
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+}
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index ef0a4e6..b5f6897 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -300,8 +300,7 @@
 key TAB {
     label:                              '\t'
     base:                               '\t'
-    ctrl:                               none
-    alt, meta:                          fallback APP_SWITCH
+    ctrl, alt, meta:                    none
 }
 
 key COMMA {
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 7f77b5f..e128a1c 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -50,6 +50,8 @@
 
 <p class="note"><strong>Note:</strong> Media codecs that are not guaranteed to be available on all Android platform versions are accordingly noted in parentheses&mdash;for example &quot;(Android 3.0+)&quot;.</p>
 
+<p class="table-caption" id="formats-table"><strong>Table 1.</strong> Core media format and codec support.</p>
+
 <table>
 <tbody>
 
@@ -218,81 +220,61 @@
 
 <h2 id="recommendations">Video Encoding Recommendations</h2>
 
-<p>Below are examples of video encoding profiles and parameters that the Android media framework supports for playback.</p>
+<p>Table 2, below, lists examples of video encoding profiles and parameters that the Android media framework supports for playback. In addition to these encoding parameter recommendations, a device's available video recording profiles can be used as a proxy for media playback capabilities. These profiles can be inspected using the {@link android.media.CamcorderProfile CamcorderProfile} class, which is available since API level 8.</p>
+
+<p class="table-caption" id="encoding-recommendations-table"><strong>Table 2.</strong> Examples of supported video encoding parameters.</p>
+
+<table>
+  <thead>
+  <tr>
+    <th>&nbsp;</th>
+    <th style="background-color:#f3f3f3;font-weight:normal">Lower quality</th>
+    <th style="background-color:#f3f3f3;font-weight:normal">Higher quality</th>
+  </tr>
+  </thead>
+  <tbody>
+  <tr>
+    <th>Video codec</th>
+    <td>H.264 Baseline Profile</th>
+    <td>H.264 Baseline Profile</th>
+  </tr>
+  <tr>
+    <th>Video resolution</th>
+    <td>176 x 144 px</th>
+    <td>480 x 360 px</th>
+  </tr>
+  <tr>
+    <th>Video frame rate</th>
+    <td>12 fps</th>
+    <td>30 fps</th>
+  </tr>
+  <tr>
+    <th>Video bitrate</th>
+    <td>56 Kbps</th>
+    <td>500 Kbps</th>
+  </tr>
+  <tr>
+    <th>Audio codec</th>
+    <td>AAC-LC</th>
+    <td>AAC-LC</th>
+  </tr>
+  <tr>
+    <th>Audio channels</th>
+    <td>1 (mono)</th>
+    <td>2 (stereo)</th>
+  </tr>
+  <tr>
+    <th>Audio bitrate</th>
+    <td>24 Kbps</th>
+    <td>128 Kbps</th>
+  </tr>
+  </tbody>
+</table>
+
+<p style="margin-top: 2em">For video content that is streamed over HTTP or RTSP, there are additional requirements:</p>
 
 <ul>
-  <li><strong>Lower quality video</strong><br>
-
-    <table style="margin-top: 4px">
-    <tbody>
-    <tr>
-      <th>Video codec</th>
-      <td>H.264 Baseline Profile</th>
-    </tr>
-    <tr>
-      <th>Video resolution</th>
-      <td>176 x 144 px</th>
-    </tr>
-    <tr>
-      <th>Video frame rate</th>
-      <td>12 fps</th>
-    </tr>
-    <tr>
-      <th>Video bitrate</th>
-      <td>56 Kbps</th>
-    </tr>
-    <tr>
-      <th>Audio codec</th>
-      <td>AAC-LC</th>
-    </tr>
-    <tr>
-      <th>Audio channels</th>
-      <td>1 (mono)</th>
-    </tr>
-    <tr>
-      <th>Audio bitrate</th>
-      <td>24 Kbps</th>
-    </tr>
-    </tbody>
-    </table>
-  </li>
-
-  <li><strong>Higher quality video</strong><br>
-
-    <table style="margin-top: 4px">
-    <tbody>
-    <tr>
-      <th>Video codec</th>
-      <td>H.264 Baseline Profile</th>
-    </tr>
-    <tr>
-      <th>Video resolution</th>
-      <td>480 x 360 px</th>
-    </tr>
-    <tr>
-      <th>Video frame rate</th>
-      <td>30 fps</th>
-    </tr>
-    <tr>
-      <th>Video bitrate</th>
-      <td>500 Kbps</th>
-    </tr>
-    <tr>
-      <th>Audio codec</th>
-      <td>AAC-LC</th>
-    </tr>
-    <tr>
-      <th>Audio channels</th>
-      <td>2 (stereo)</th>
-    </tr>
-    <tr>
-      <th>Audio bitrate</th>
-      <td>128 Kbps</th>
-    </tr>
-    </tbody>
-    </table>
-
-  </li>
+  <li>For 3GPP and MPEG-4 containers, the <code>moov</code> atom must precede any <code>mdat</code> atoms.</li>
+  <li>For 3GPP, MPEG-4, and WebM containers, audio and video samples corresponding to the same time offset may be no more than 500 KB apart.
+      To minimize this audio/video drift, consider interleaving audio and video in smaller chunk sizes.</li>
 </ul>
-
-<p>In addition to the encoding parameters above, a device's available video recording profiles can be used as a proxy for media playback capabilities. These profiles can be inspected using the {@link android.media.CamcorderProfile CamcorderProfile} class, which is available since API level 8.</p>
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 3362c7d..ea7f477 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -117,6 +117,7 @@
 	rsVertexArray.cpp \
 	driver/rsdBcc.cpp \
 	driver/rsdCore.cpp \
+	driver/rsdGL.cpp \
 	driver/rsdProgramRaster.cpp \
 	driver/rsdProgramStore.cpp
 
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 00d62ba..75f4d6b 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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,6 +16,7 @@
 
 #include "rsdCore.h"
 #include "rsdBcc.h"
+#include "rsdGL.h"
 #include "rsdProgramStore.h"
 #include "rsdProgramRaster.h"
 
@@ -37,6 +38,11 @@
 static void SetPriority(const Context *rsc, int32_t priority);
 
 static RsdHalFunctions FunctionTable = {
+    rsdGLInit,
+    rsdGLShutdown,
+    rsdGLSetSurface,
+    rsdGLSwap,
+
     Shutdown,
     NULL,
     SetPriority,
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index 02b2fbc..e37698b 100644
--- a/libs/rs/driver/rsdCore.h
+++ b/libs/rs/driver/rsdCore.h
@@ -23,6 +23,7 @@
 #include "rsMutex.h"
 #include "rsSignal.h"
 
+#include "rsdGL.h"
 
 typedef void (* InvokeFunc_t)(void);
 typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
@@ -45,6 +46,8 @@
     };
     Workers mWorkers;
     bool mExit;
+
+    RsdGL gl;
 } RsHal;
 
 
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
new file mode 100644
index 0000000..86dfa0f
--- /dev/null
+++ b/libs/rs/driver/rsdGL.cpp
@@ -0,0 +1,333 @@
+/*
+ * 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.
+ */
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/PixelFormat.h>
+#include <ui/EGLUtils.h>
+#include <ui/egl/android_natives.h>
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <cutils/properties.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+//#include <cutils/sched_policy.h>
+//#include <sys/syscall.h>
+#include <string.h>
+
+
+#include "rsdCore.h"
+#include "rsdGL.h"
+
+#include <malloc.h>
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+static int32_t gGLContextCount = 0;
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    if (returnVal != EGL_TRUE) {
+        fprintf(stderr, "%s() returned %d\n", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+                error);
+    }
+}
+
+static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+    struct {EGLint attribute; const char* name;} names[] = {
+    X(EGL_BUFFER_SIZE),
+    X(EGL_ALPHA_SIZE),
+    X(EGL_BLUE_SIZE),
+    X(EGL_GREEN_SIZE),
+    X(EGL_RED_SIZE),
+    X(EGL_DEPTH_SIZE),
+    X(EGL_STENCIL_SIZE),
+    X(EGL_CONFIG_CAVEAT),
+    X(EGL_CONFIG_ID),
+    X(EGL_LEVEL),
+    X(EGL_MAX_PBUFFER_HEIGHT),
+    X(EGL_MAX_PBUFFER_PIXELS),
+    X(EGL_MAX_PBUFFER_WIDTH),
+    X(EGL_NATIVE_RENDERABLE),
+    X(EGL_NATIVE_VISUAL_ID),
+    X(EGL_NATIVE_VISUAL_TYPE),
+    X(EGL_SAMPLES),
+    X(EGL_SAMPLE_BUFFERS),
+    X(EGL_SURFACE_TYPE),
+    X(EGL_TRANSPARENT_TYPE),
+    X(EGL_TRANSPARENT_RED_VALUE),
+    X(EGL_TRANSPARENT_GREEN_VALUE),
+    X(EGL_TRANSPARENT_BLUE_VALUE),
+    X(EGL_BIND_TO_TEXTURE_RGB),
+    X(EGL_BIND_TO_TEXTURE_RGBA),
+    X(EGL_MIN_SWAP_INTERVAL),
+    X(EGL_MAX_SWAP_INTERVAL),
+    X(EGL_LUMINANCE_SIZE),
+    X(EGL_ALPHA_MASK_SIZE),
+    X(EGL_COLOR_BUFFER_TYPE),
+    X(EGL_RENDERABLE_TYPE),
+    X(EGL_CONFORMANT),
+   };
+#undef X
+
+    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+        EGLint value = -1;
+        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+        EGLint error = eglGetError();
+        if (returnVal && error == EGL_SUCCESS) {
+            LOGV(" %s: %d (0x%x)", names[j].name, value, value);
+        }
+    }
+}
+
+static void DumpDebug(RsHal *dc) {
+    LOGE(" EGL ver %i %i", dc->gl.egl.majorVersion, dc->gl.egl.minorVersion);
+    LOGE(" EGL context %p  surface %p,  Display=%p", dc->gl.egl.context, dc->gl.egl.surface,
+         dc->gl.egl.display);
+    LOGE(" GL vendor: %s", dc->gl.gl.vendor);
+    LOGE(" GL renderer: %s", dc->gl.gl.renderer);
+    LOGE(" GL Version: %s", dc->gl.gl.version);
+    LOGE(" GL Extensions: %s", dc->gl.gl.extensions);
+    LOGE(" GL int Versions %i %i", dc->gl.gl.majorVersion, dc->gl.gl.minorVersion);
+
+    LOGV("MAX Textures %i, %i  %i", dc->gl.gl.maxVertexTextureUnits,
+         dc->gl.gl.maxFragmentTextureImageUnits, dc->gl.gl.maxTextureImageUnits);
+    LOGV("MAX Attribs %i", dc->gl.gl.maxVertexAttribs);
+    LOGV("MAX Uniforms %i, %i", dc->gl.gl.maxVertexUniformVectors,
+         dc->gl.gl.maxFragmentUniformVectors);
+    LOGV("MAX Varyings %i", dc->gl.gl.maxVaryingVectors);
+}
+
+void rsdGLShutdown(const Context *rsc) {
+    RsHal *dc = (RsHal *)rsc->mHal.drv;
+
+    LOGV("%p, deinitEGL", rsc);
+
+    if (dc->gl.egl.context != EGL_NO_CONTEXT) {
+        eglMakeCurrent(dc->gl.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglDestroySurface(dc->gl.egl.display, dc->gl.egl.surfaceDefault);
+        if (dc->gl.egl.surface != EGL_NO_SURFACE) {
+            eglDestroySurface(dc->gl.egl.display, dc->gl.egl.surface);
+        }
+        eglDestroyContext(dc->gl.egl.display, dc->gl.egl.context);
+        checkEglError("eglDestroyContext");
+    }
+
+    gGLContextCount--;
+    if (!gGLContextCount) {
+        eglTerminate(dc->gl.egl.display);
+    }
+}
+
+bool rsdGLInit(const Context *rsc) {
+    RsHal *dc = (RsHal *)rsc->mHal.drv;
+
+    dc->gl.egl.numConfigs = -1;
+    EGLint configAttribs[128];
+    EGLint *configAttribsPtr = configAttribs;
+    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+
+    memset(configAttribs, 0, sizeof(configAttribs));
+
+    configAttribsPtr[0] = EGL_SURFACE_TYPE;
+    configAttribsPtr[1] = EGL_WINDOW_BIT;
+    configAttribsPtr += 2;
+
+    configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
+    configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
+    configAttribsPtr += 2;
+
+    if (rsc->mUserSurfaceConfig.depthMin > 0) {
+        configAttribsPtr[0] = EGL_DEPTH_SIZE;
+        configAttribsPtr[1] = rsc->mUserSurfaceConfig.depthMin;
+        configAttribsPtr += 2;
+    }
+
+    if (rsc->mDev->mForceSW) {
+        configAttribsPtr[0] = EGL_CONFIG_CAVEAT;
+        configAttribsPtr[1] = EGL_SLOW_CONFIG;
+        configAttribsPtr += 2;
+    }
+
+    configAttribsPtr[0] = EGL_NONE;
+    rsAssert(configAttribsPtr < (configAttribs + (sizeof(configAttribs) / sizeof(EGLint))));
+
+    LOGV("%p initEGL start", rsc);
+    dc->gl.egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+
+    eglInitialize(dc->gl.egl.display, &dc->gl.egl.majorVersion, &dc->gl.egl.minorVersion);
+    checkEglError("eglInitialize");
+
+    PixelFormat pf = PIXEL_FORMAT_RGBA_8888;
+    if (rsc->mUserSurfaceConfig.alphaMin == 0) {
+        pf = PIXEL_FORMAT_RGBX_8888;
+    }
+
+    status_t err = EGLUtils::selectConfigForPixelFormat(dc->gl.egl.display, configAttribs,
+                                                        pf, &dc->gl.egl.config);
+    if (err) {
+       LOGE("%p, couldn't find an EGLConfig matching the screen format\n", rsc);
+    }
+    //if (props.mLogVisual) {
+        printEGLConfiguration(dc->gl.egl.display, dc->gl.egl.config);
+    //}
+
+    dc->gl.egl.context = eglCreateContext(dc->gl.egl.display, dc->gl.egl.config,
+                                          EGL_NO_CONTEXT, context_attribs2);
+    checkEglError("eglCreateContext");
+    if (dc->gl.egl.context == EGL_NO_CONTEXT) {
+        LOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", rsc);
+        return false;
+    }
+    gGLContextCount++;
+
+
+    EGLint pbuffer_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+    dc->gl.egl.surfaceDefault = eglCreatePbufferSurface(dc->gl.egl.display, dc->gl.egl.config,
+                                                        pbuffer_attribs);
+    checkEglError("eglCreatePbufferSurface");
+    if (dc->gl.egl.surfaceDefault == EGL_NO_SURFACE) {
+        LOGE("eglCreatePbufferSurface returned EGL_NO_SURFACE");
+        rsdGLShutdown(rsc);
+        return false;
+    }
+
+    EGLBoolean ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
+                                    dc->gl.egl.surfaceDefault, dc->gl.egl.context);
+    if (ret == EGL_FALSE) {
+        LOGE("eglMakeCurrent returned EGL_FALSE");
+        checkEglError("eglMakeCurrent", ret);
+        rsdGLShutdown(rsc);
+        return false;
+    }
+
+    dc->gl.gl.version = glGetString(GL_VERSION);
+    dc->gl.gl.vendor = glGetString(GL_VENDOR);
+    dc->gl.gl.renderer = glGetString(GL_RENDERER);
+    dc->gl.gl.extensions = glGetString(GL_EXTENSIONS);
+
+    //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
+    //LOGV("GL Version %s", mGL.mVersion);
+    //LOGV("GL Vendor %s", mGL.mVendor);
+    //LOGV("GL Renderer %s", mGL.mRenderer);
+    //LOGV("GL Extensions %s", mGL.mExtensions);
+
+    const char *verptr = NULL;
+    if (strlen((const char *)dc->gl.gl.version) > 9) {
+        if (!memcmp(dc->gl.gl.version, "OpenGL ES-CM", 12)) {
+            verptr = (const char *)dc->gl.gl.version + 12;
+        }
+        if (!memcmp(dc->gl.gl.version, "OpenGL ES ", 10)) {
+            verptr = (const char *)dc->gl.gl.version + 9;
+        }
+    }
+
+    if (!verptr) {
+        LOGE("Error, OpenGL ES Lite not supported");
+        rsdGLShutdown(rsc);
+        return false;
+    } else {
+        sscanf(verptr, " %i.%i", &dc->gl.gl.majorVersion, &dc->gl.gl.minorVersion);
+    }
+
+    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &dc->gl.gl.maxVertexAttribs);
+    glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &dc->gl.gl.maxVertexUniformVectors);
+    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxVertexTextureUnits);
+
+    glGetIntegerv(GL_MAX_VARYING_VECTORS, &dc->gl.gl.maxVaryingVectors);
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxTextureImageUnits);
+
+    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &dc->gl.gl.maxFragmentTextureImageUnits);
+    glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &dc->gl.gl.maxFragmentUniformVectors);
+
+    dc->gl.gl.OES_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
+                                                "GL_OES_texture_npot");
+    dc->gl.gl.GL_IMG_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions,
+                                                   "GL_IMG_texture_npot");
+    dc->gl.gl.GL_NV_texture_npot_2D_mipmap = NULL != strstr((const char *)dc->gl.gl.extensions,
+                                                            "GL_NV_texture_npot_2D_mipmap");
+    dc->gl.gl.EXT_texture_max_aniso = 1.0f;
+    bool hasAniso = NULL != strstr((const char *)dc->gl.gl.extensions,
+                                   "GL_EXT_texture_filter_anisotropic");
+    if (hasAniso) {
+        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &dc->gl.gl.EXT_texture_max_aniso);
+    }
+
+    DumpDebug(dc);
+
+    LOGV("initGLThread end %p", rsc);
+    return true;
+}
+
+
+bool rsdGLSetSurface(const Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur) {
+    RsHal *dc = (RsHal *)rsc->mHal.drv;
+
+    EGLBoolean ret;
+    // WAR: Some drivers fail to handle 0 size surfaces correcntly.
+    // Use the pbuffer to avoid this pitfall.
+    if ((dc->gl.egl.surface != NULL) || (w == 0) || (h == 0)) {
+        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surfaceDefault,
+                             dc->gl.egl.surfaceDefault, dc->gl.egl.context);
+        checkEglError("eglMakeCurrent", ret);
+
+        ret = eglDestroySurface(dc->gl.egl.display, dc->gl.egl.surface);
+        checkEglError("eglDestroySurface", ret);
+
+        dc->gl.egl.surface = NULL;
+        dc->gl.width = 1;
+        dc->gl.height = 1;
+    }
+
+    dc->gl.wndSurface = sur;
+    if (dc->gl.wndSurface != NULL) {
+        dc->gl.width = w;
+        dc->gl.height = h;
+
+        dc->gl.egl.surface = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
+                                                    dc->gl.wndSurface, NULL);
+        checkEglError("eglCreateWindowSurface");
+        if (dc->gl.egl.surface == EGL_NO_SURFACE) {
+            LOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
+        }
+
+        ret = eglMakeCurrent(dc->gl.egl.display, dc->gl.egl.surface,
+                             dc->gl.egl.surface, dc->gl.egl.context);
+        checkEglError("eglMakeCurrent", ret);
+    }
+    return true;
+}
+
+void rsdGLSwap(const android::renderscript::Context *rsc) {
+    RsHal *dc = (RsHal *)rsc->mHal.drv;
+    eglSwapBuffers(dc->gl.egl.display, dc->gl.egl.surface);
+}
+
diff --git a/libs/rs/driver/rsdGL.h b/libs/rs/driver/rsdGL.h
new file mode 100644
index 0000000..246931f
--- /dev/null
+++ b/libs/rs/driver/rsdGL.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RSD_GL_H
+#define RSD_GL_H
+
+#include <rs_hal.h>
+
+
+
+typedef void (* InvokeFunc_t)(void);
+typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
+
+typedef struct RsdGLRec {
+    struct {
+        EGLint numConfigs;
+        EGLint majorVersion;
+        EGLint minorVersion;
+        EGLConfig config;
+        EGLContext context;
+        EGLSurface surface;
+        EGLSurface surfaceDefault;
+        EGLDisplay display;
+    } egl;
+
+    struct {
+        const uint8_t * vendor;
+        const uint8_t * renderer;
+        const uint8_t * version;
+        const uint8_t * extensions;
+
+        uint32_t majorVersion;
+        uint32_t minorVersion;
+
+        int32_t maxVaryingVectors;
+        int32_t maxTextureImageUnits;
+
+        int32_t maxFragmentTextureImageUnits;
+        int32_t maxFragmentUniformVectors;
+
+        int32_t maxVertexAttribs;
+        int32_t maxVertexUniformVectors;
+        int32_t maxVertexTextureUnits;
+
+        bool OES_texture_npot;
+        bool GL_IMG_texture_npot;
+        bool GL_NV_texture_npot_2D_mipmap;
+        float EXT_texture_max_aniso;
+    } gl;
+
+    ANativeWindow *wndSurface;
+    uint32_t width;
+    uint32_t height;
+} RsdGL;
+
+
+
+bool rsdGLInit(const android::renderscript::Context *rsc);
+void rsdGLShutdown(const android::renderscript::Context *rsc);
+bool rsdGLSetSurface(const android::renderscript::Context *rsc,
+                     uint32_t w, uint32_t h, ANativeWindow *sur);
+void rsdGLSwap(const android::renderscript::Context *rsc);
+
+#endif
+
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index c9a7060..0f61789 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -19,7 +19,6 @@
 #include "rsThreadIO.h"
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/PixelFormat.h>
-#include <ui/EGLUtils.h>
 #include <ui/egl/android_natives.h>
 
 #include <sys/types.h>
@@ -42,188 +41,22 @@
 
 pthread_key_t Context::gThreadTLSKey = 0;
 uint32_t Context::gThreadTLSKeyCount = 0;
-uint32_t Context::gGLContextCount = 0;
 pthread_mutex_t Context::gInitMutex = PTHREAD_MUTEX_INITIALIZER;
 pthread_mutex_t Context::gLibMutex = PTHREAD_MUTEX_INITIALIZER;
 
-static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
-    if (returnVal != EGL_TRUE) {
-        fprintf(stderr, "%s() returned %d\n", op, returnVal);
-    }
-
-    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
-            = eglGetError()) {
-        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
-                error);
-    }
-}
-
-void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
-
-#define X(VAL) {VAL, #VAL}
-    struct {EGLint attribute; const char* name;} names[] = {
-    X(EGL_BUFFER_SIZE),
-    X(EGL_ALPHA_SIZE),
-    X(EGL_BLUE_SIZE),
-    X(EGL_GREEN_SIZE),
-    X(EGL_RED_SIZE),
-    X(EGL_DEPTH_SIZE),
-    X(EGL_STENCIL_SIZE),
-    X(EGL_CONFIG_CAVEAT),
-    X(EGL_CONFIG_ID),
-    X(EGL_LEVEL),
-    X(EGL_MAX_PBUFFER_HEIGHT),
-    X(EGL_MAX_PBUFFER_PIXELS),
-    X(EGL_MAX_PBUFFER_WIDTH),
-    X(EGL_NATIVE_RENDERABLE),
-    X(EGL_NATIVE_VISUAL_ID),
-    X(EGL_NATIVE_VISUAL_TYPE),
-    X(EGL_SAMPLES),
-    X(EGL_SAMPLE_BUFFERS),
-    X(EGL_SURFACE_TYPE),
-    X(EGL_TRANSPARENT_TYPE),
-    X(EGL_TRANSPARENT_RED_VALUE),
-    X(EGL_TRANSPARENT_GREEN_VALUE),
-    X(EGL_TRANSPARENT_BLUE_VALUE),
-    X(EGL_BIND_TO_TEXTURE_RGB),
-    X(EGL_BIND_TO_TEXTURE_RGBA),
-    X(EGL_MIN_SWAP_INTERVAL),
-    X(EGL_MAX_SWAP_INTERVAL),
-    X(EGL_LUMINANCE_SIZE),
-    X(EGL_ALPHA_MASK_SIZE),
-    X(EGL_COLOR_BUFFER_TYPE),
-    X(EGL_RENDERABLE_TYPE),
-    X(EGL_CONFORMANT),
-   };
-#undef X
-
-    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
-        EGLint value = -1;
-        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
-        EGLint error = eglGetError();
-        if (returnVal && error == EGL_SUCCESS) {
-            LOGV(" %s: %d (0x%x)", names[j].name, value, value);
-        }
-    }
-}
 
 
 bool Context::initGLThread() {
     pthread_mutex_lock(&gInitMutex);
     LOGV("initGLThread start %p", this);
 
-    mEGL.mNumConfigs = -1;
-    EGLint configAttribs[128];
-    EGLint *configAttribsPtr = configAttribs;
-    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
-
-    memset(configAttribs, 0, sizeof(configAttribs));
-
-    configAttribsPtr[0] = EGL_SURFACE_TYPE;
-    configAttribsPtr[1] = EGL_WINDOW_BIT;
-    configAttribsPtr += 2;
-
-    configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
-    configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
-    configAttribsPtr += 2;
-
-    if (mUserSurfaceConfig.depthMin > 0) {
-        configAttribsPtr[0] = EGL_DEPTH_SIZE;
-        configAttribsPtr[1] = mUserSurfaceConfig.depthMin;
-        configAttribsPtr += 2;
-    }
-
-    if (mDev->mForceSW) {
-        configAttribsPtr[0] = EGL_CONFIG_CAVEAT;
-        configAttribsPtr[1] = EGL_SLOW_CONFIG;
-        configAttribsPtr += 2;
-    }
-
-    configAttribsPtr[0] = EGL_NONE;
-    rsAssert(configAttribsPtr < (configAttribs + (sizeof(configAttribs) / sizeof(EGLint))));
-
-    LOGV("%p initEGL start", this);
-    mEGL.mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    checkEglError("eglGetDisplay");
-
-    eglInitialize(mEGL.mDisplay, &mEGL.mMajorVersion, &mEGL.mMinorVersion);
-    checkEglError("eglInitialize");
-
-#if 1
-    PixelFormat pf = PIXEL_FORMAT_RGBA_8888;
-    if (mUserSurfaceConfig.alphaMin == 0) {
-        pf = PIXEL_FORMAT_RGBX_8888;
-    }
-
-    status_t err = EGLUtils::selectConfigForPixelFormat(mEGL.mDisplay, configAttribs, pf, &mEGL.mConfig);
-    if (err) {
-       LOGE("%p, couldn't find an EGLConfig matching the screen format\n", this);
-    }
-    if (props.mLogVisual) {
-        printEGLConfiguration(mEGL.mDisplay, mEGL.mConfig);
-    }
-#else
-    eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs);
-#endif
-
-    mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2);
-    checkEglError("eglCreateContext");
-    if (mEGL.mContext == EGL_NO_CONTEXT) {
+    if (!mHal.funcs.initGraphics(this)) {
         pthread_mutex_unlock(&gInitMutex);
-        LOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", this);
-        return false;
-    }
-    gGLContextCount++;
-
-
-    EGLint pbuffer_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
-    mEGL.mSurfaceDefault = eglCreatePbufferSurface(mEGL.mDisplay, mEGL.mConfig, pbuffer_attribs);
-    checkEglError("eglCreatePbufferSurface");
-    if (mEGL.mSurfaceDefault == EGL_NO_SURFACE) {
-        LOGE("eglCreatePbufferSurface returned EGL_NO_SURFACE");
-        pthread_mutex_unlock(&gInitMutex);
-        deinitEGL();
+        LOGE("%p, initGraphics failed", this);
         return false;
     }
 
-    EGLBoolean ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext);
-    if (ret == EGL_FALSE) {
-        LOGE("eglMakeCurrent returned EGL_FALSE");
-        checkEglError("eglMakeCurrent", ret);
-        pthread_mutex_unlock(&gInitMutex);
-        deinitEGL();
-        return false;
-    }
-
-    mGL.mVersion = glGetString(GL_VERSION);
-    mGL.mVendor = glGetString(GL_VENDOR);
-    mGL.mRenderer = glGetString(GL_RENDERER);
-    mGL.mExtensions = glGetString(GL_EXTENSIONS);
-
-    //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
-    //LOGV("GL Version %s", mGL.mVersion);
-    //LOGV("GL Vendor %s", mGL.mVendor);
-    //LOGV("GL Renderer %s", mGL.mRenderer);
-    //LOGV("GL Extensions %s", mGL.mExtensions);
-
-    const char *verptr = NULL;
-    if (strlen((const char *)mGL.mVersion) > 9) {
-        if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
-            verptr = (const char *)mGL.mVersion + 12;
-        }
-        if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) {
-            verptr = (const char *)mGL.mVersion + 9;
-        }
-    }
-
-    if (!verptr) {
-        LOGE("Error, OpenGL ES Lite not supported");
-        pthread_mutex_unlock(&gInitMutex);
-        deinitEGL();
-        return false;
-    } else {
-        sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
-    }
+    const char * ext = (const char *)glGetString(GL_EXTENSIONS);
 
     glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mGL.mMaxVertexAttribs);
     glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &mGL.mMaxVertexUniformVectors);
@@ -235,11 +68,11 @@
     glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits);
     glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors);
 
-    mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot");
-    mGL.GL_IMG_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_IMG_texture_npot");
-    mGL.GL_NV_texture_npot_2D_mipmap = NULL != strstr((const char *)mGL.mExtensions, "GL_NV_texture_npot_2D_mipmap");
+    mGL.OES_texture_npot = NULL != strstr(ext, "GL_OES_texture_npot");
+    mGL.GL_IMG_texture_npot = NULL != strstr(ext, "GL_IMG_texture_npot");
+    mGL.GL_NV_texture_npot_2D_mipmap = NULL != strstr(ext, "GL_NV_texture_npot_2D_mipmap");
     mGL.EXT_texture_max_aniso = 1.0f;
-    bool hasAniso = NULL != strstr((const char *)mGL.mExtensions, "GL_EXT_texture_filter_anisotropic");
+    bool hasAniso = NULL != strstr(ext, "GL_EXT_texture_filter_anisotropic");
     if (hasAniso) {
         glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mGL.EXT_texture_max_aniso);
     }
@@ -251,21 +84,7 @@
 
 void Context::deinitEGL() {
     LOGV("%p, deinitEGL", this);
-
-    if (mEGL.mContext != EGL_NO_CONTEXT) {
-        eglMakeCurrent(mEGL.mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-        eglDestroySurface(mEGL.mDisplay, mEGL.mSurfaceDefault);
-        if (mEGL.mSurface != EGL_NO_SURFACE) {
-            eglDestroySurface(mEGL.mDisplay, mEGL.mSurface);
-        }
-        eglDestroyContext(mEGL.mDisplay, mEGL.mContext);
-        checkEglError("eglDestroyContext");
-    }
-
-    gGLContextCount--;
-    if (!gGLContextCount) {
-        eglTerminate(mEGL.mDisplay);
-    }
+    mHal.funcs.shutdownGraphics(this);
 }
 
 Context::PushState::PushState(Context *con) {
@@ -507,7 +326,7 @@
 
              mDraw = targetTime && !rsc->mPaused;
              rsc->timerSet(RS_TIMER_CLEAR_SWAP);
-             eglSwapBuffers(rsc->mEGL.mDisplay, rsc->mEGL.mSurface);
+             rsc->mHal.funcs.swap(rsc);
              rsc->timerFrame();
              rsc->timerSet(RS_TIMER_INTERNAL);
              rsc->timerPrint();
@@ -604,7 +423,6 @@
         memset(&mUserSurfaceConfig, 0, sizeof(mUserSurfaceConfig));
     }
 
-    memset(&mEGL, 0, sizeof(mEGL));
     memset(&mGL, 0, sizeof(mGL));
     mIsGraphicsContext = sc != NULL;
 
@@ -693,36 +511,13 @@
 
 void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur) {
     rsAssert(mIsGraphicsContext);
-
-    EGLBoolean ret;
-    // WAR: Some drivers fail to handle 0 size surfaces correcntly.
-    // Use the pbuffer to avoid this pitfall.
-    if ((mEGL.mSurface != NULL) || (w == 0) || (h == 0)) {
-        ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext);
-        checkEglError("eglMakeCurrent", ret);
-
-        ret = eglDestroySurface(mEGL.mDisplay, mEGL.mSurface);
-        checkEglError("eglDestroySurface", ret);
-
-        mEGL.mSurface = NULL;
-        mWidth = 1;
-        mHeight = 1;
-    }
+    mHal.funcs.setSurface(this, w, h, sur);
 
     mWndSurface = sur;
-    if (mWndSurface != NULL) {
-        mWidth = w;
-        mHeight = h;
+    mWidth = w;
+    mHeight = h;
 
-        mEGL.mSurface = eglCreateWindowSurface(mEGL.mDisplay, mEGL.mConfig, mWndSurface, NULL);
-        checkEglError("eglCreateWindowSurface");
-        if (mEGL.mSurface == EGL_NO_SURFACE) {
-            LOGE("eglCreateWindowSurface returned EGL_NO_SURFACE");
-        }
-
-        ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurface, mEGL.mSurface, mEGL.mContext);
-        checkEglError("eglMakeCurrent", ret);
-
+    if (mWidth && mHeight) {
         mStateVertex.updateSize(this);
     }
 }
@@ -887,21 +682,9 @@
     LOGE("RS Context debug %p", this);
     LOGE("RS Context debug");
 
-    LOGE(" EGL ver %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
-    LOGE(" EGL context %p  surface %p,  Display=%p", mEGL.mContext, mEGL.mSurface, mEGL.mDisplay);
-    LOGE(" GL vendor: %s", mGL.mVendor);
-    LOGE(" GL renderer: %s", mGL.mRenderer);
-    LOGE(" GL Version: %s", mGL.mVersion);
-    LOGE(" GL Extensions: %s", mGL.mExtensions);
-    LOGE(" GL int Versions %i %i", mGL.mMajorVersion, mGL.mMinorVersion);
     LOGE(" RS width %i, height %i", mWidth, mHeight);
     LOGE(" RS running %i, exit %i, paused %i", mRunning, mExit, mPaused);
     LOGE(" RS pThreadID %li, nativeThreadID %i", mThreadId, mNativeThreadId);
-
-    LOGV("MAX Textures %i, %i  %i", mGL.mMaxVertexTextureUnits, mGL.mMaxFragmentTextureImageUnits, mGL.mMaxTextureImageUnits);
-    LOGV("MAX Attribs %i", mGL.mMaxVertexAttribs);
-    LOGV("MAX Uniforms %i, %i", mGL.mMaxVertexUniformVectors, mGL.mMaxFragmentUniformVectors);
-    LOGV("MAX Varyings %i", mGL.mMaxVaryingVectors);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index eacfdf7..4dd186c 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -222,29 +222,10 @@
     uint32_t getDPI() const {return mDPI;}
     void setDPI(uint32_t dpi) {mDPI = dpi;}
 
-protected:
     Device *mDev;
+protected:
 
     struct {
-        EGLint mNumConfigs;
-        EGLint mMajorVersion;
-        EGLint mMinorVersion;
-        EGLConfig mConfig;
-        EGLContext mContext;
-        EGLSurface mSurface;
-        EGLSurface mSurfaceDefault;
-        EGLDisplay mDisplay;
-    } mEGL;
-
-    struct {
-        const uint8_t * mVendor;
-        const uint8_t * mRenderer;
-        const uint8_t * mVersion;
-        const uint8_t * mExtensions;
-
-        uint32_t mMajorVersion;
-        uint32_t mMinorVersion;
-
         int32_t mMaxVaryingVectors;
         int32_t mMaxTextureImageUnits;
 
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 93d7476..a4ca936 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -18,6 +18,7 @@
 #define RS_HAL_H
 
 #include <RenderScriptDefines.h>
+#include <ui/egl/android_natives.h>
 
 namespace android {
 namespace renderscript {
@@ -44,6 +45,11 @@
  * Script management functions
  */
 typedef struct {
+    bool (*initGraphics)(const Context *);
+    void (*shutdownGraphics)(const Context *);
+    bool (*setSurface)(const Context *, uint32_t w, uint32_t h, ANativeWindow *);
+    void (*swap)(const Context *);
+
     void (*shutdownDriver)(Context *);
     void (*getVersion)(unsigned int *major, unsigned int *minor);
     void (*setPriority)(const Context *, int32_t priority);
@@ -87,7 +93,6 @@
         void (*destroy)(const Context *rsc, Script *s);
     } script;
 
-
     struct {
         bool (*init)(const Context *rsc, const ProgramStore *ps);
         void (*setActive)(const Context *rsc, const ProgramStore *ps);
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 9d1b8b9..93d0d1f 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -27,8 +27,14 @@
 
 namespace android {
 
+#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1))
+#define MIN_HISTORY_DEPTH 20
+
 // Must be at least sizeof(InputMessage) + sufficient space for pointer data
-static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
+static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP(
+        sizeof(InputMessage) + MIN_HISTORY_DEPTH
+                * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)),
+        4096);
 
 // Signal sent by the producer to the consumer to inform it that a new message is
 // available to be consumed in the shared memory buffer.
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 27b37b7..a219623 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -34,11 +34,11 @@
 
 struct fields_t {
     jfieldID context;
-    jclass bitmapClazz;
+    jclass bitmapClazz;  // Must be a global ref
     jfieldID nativeBitmap;
     jmethodID createBitmapMethod;
     jmethodID createScaledBitmapMethod;
-    jclass configClazz;
+    jclass configClazz;  // Must be a global ref
     jmethodID createConfigMethod;
 };
 
@@ -120,33 +120,71 @@
     if (headers) {
         // Get the Map's entry Set.
         jclass mapClass = env->FindClass("java/util/Map");
+        if (mapClass == NULL) {
+            return;
+        }
 
         jmethodID entrySet =
             env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
+        if (entrySet == NULL) {
+            return;
+        }
 
         jobject set = env->CallObjectMethod(headers, entrySet);
+        if (set == NULL) {
+            return;
+        }
+
         // Obtain an iterator over the Set
         jclass setClass = env->FindClass("java/util/Set");
+        if (setClass == NULL) {
+            return;
+        }
 
         jmethodID iterator =
             env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
+        if (iterator == NULL) {
+            return;
+        }
 
         jobject iter = env->CallObjectMethod(set, iterator);
+        if (iter == NULL) {
+            return;
+        }
+
         // Get the Iterator method IDs
         jclass iteratorClass = env->FindClass("java/util/Iterator");
+        if (iteratorClass == NULL) {
+            return;
+        }
         jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+        if (hasNext == NULL) {
+            return;
+        }
 
         jmethodID next =
             env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
+        if (next == NULL) {
+            return;
+        }
 
         // Get the Entry class method IDs
         jclass entryClass = env->FindClass("java/util/Map$Entry");
+        if (entryClass == NULL) {
+            return;
+        }
 
         jmethodID getKey =
             env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
+        if (getKey == NULL) {
+            return;
+        }
 
         jmethodID getValue =
             env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
+        if (getValue == NULL) {
+            return;
+        }
 
         // Iterate over the entry Set
         while (env->CallBooleanMethod(iter, hasNext)) {
@@ -161,6 +199,7 @@
 
             const char* valueStr = env->GetStringUTFChars(value, NULL);
             if (!valueStr) {  // Out of memory
+                env->ReleaseStringUTFChars(key, keyStr);
                 return;
             }
 
@@ -171,14 +210,8 @@
             env->DeleteLocalRef(key);
             env->ReleaseStringUTFChars(value, valueStr);
             env->DeleteLocalRef(value);
-      }
+        }
 
-      env->DeleteLocalRef(entryClass);
-      env->DeleteLocalRef(iteratorClass);
-      env->DeleteLocalRef(iter);
-      env->DeleteLocalRef(setClass);
-      env->DeleteLocalRef(set);
-      env->DeleteLocalRef(mapClass);
     }
 
     process_media_retriever_call(
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 0f9cbec..ecbd288 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1,4 +1,4 @@
-/* //device/libs/android_runtime/android_media_MediaPlayer.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -185,45 +185,87 @@
         return;
     }
 
-    const char *pathStr = env->GetStringUTFChars(path, NULL);
-    if (pathStr == NULL) {  // Out of memory
-        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+    const char *tmp = env->GetStringUTFChars(path, NULL);
+    if (tmp == NULL) {  // Out of memory
         return;
     }
 
+    String8 pathStr(tmp);
+    env->ReleaseStringUTFChars(path, tmp);
+    tmp = NULL;
+
     // headers is a Map<String, String>.
     // We build a similar KeyedVector out of it.
     KeyedVector<String8, String8> headersVector;
     if (headers) {
         // Get the Map's entry Set.
         jclass mapClass = env->FindClass("java/util/Map");
+        if (mapClass == NULL) {
+            return;
+        }
 
         jmethodID entrySet =
             env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
+        if (entrySet == NULL) {
+            return;
+        }
 
         jobject set = env->CallObjectMethod(headers, entrySet);
+        if (set == NULL) {
+            return;
+        }
+
         // Obtain an iterator over the Set
         jclass setClass = env->FindClass("java/util/Set");
+        if (setClass == NULL) {
+            return;
+        }
 
         jmethodID iterator =
             env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
+        if (iterator == NULL) {
+            return;
+        }
 
         jobject iter = env->CallObjectMethod(set, iterator);
+        if (iter == NULL) {
+            return;
+        }
+
         // Get the Iterator method IDs
         jclass iteratorClass = env->FindClass("java/util/Iterator");
+        if (iteratorClass == NULL) {
+            return;
+        }
+
         jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+        if (hasNext == NULL) {
+            return;
+        }
 
         jmethodID next =
             env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
+        if (next == NULL) {
+            return;
+        }
 
         // Get the Entry class method IDs
         jclass entryClass = env->FindClass("java/util/Map$Entry");
+        if (entryClass == NULL) {
+            return;
+        }
 
         jmethodID getKey =
             env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
+        if (getKey == NULL) {
+            return;
+        }
 
         jmethodID getValue =
             env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
+        if (getValue == NULL) {
+            return;
+        }
 
         // Iterate over the entry Set
         while (env->CallBooleanMethod(iter, hasNext)) {
@@ -233,15 +275,12 @@
 
             const char* keyStr = env->GetStringUTFChars(key, NULL);
             if (!keyStr) {  // Out of memory
-                jniThrowException(
-                        env, "java/lang/RuntimeException", "Out of memory");
                 return;
             }
 
             const char* valueStr = env->GetStringUTFChars(value, NULL);
             if (!valueStr) {  // Out of memory
-                jniThrowException(
-                        env, "java/lang/RuntimeException", "Out of memory");
+                env->ReleaseStringUTFChars(key, keyStr);
                 return;
             }
 
@@ -252,25 +291,16 @@
             env->DeleteLocalRef(key);
             env->ReleaseStringUTFChars(value, valueStr);
             env->DeleteLocalRef(value);
-      }
+        }
 
-      env->DeleteLocalRef(entryClass);
-      env->DeleteLocalRef(iteratorClass);
-      env->DeleteLocalRef(iter);
-      env->DeleteLocalRef(setClass);
-      env->DeleteLocalRef(set);
-      env->DeleteLocalRef(mapClass);
     }
 
     LOGV("setDataSource: path %s", pathStr);
     status_t opStatus =
         mp->setDataSource(
-                String8(pathStr),
+                pathStr,
                 headers ? &headersVector : NULL);
 
-    // Make sure that local ref is released before a potential exception
-    env->ReleaseStringUTFChars(path, pathStr);
-
     process_media_player_call(
             env, thiz, opStatus, "java/io/IOException",
             "setDataSource failed." );
@@ -628,62 +658,49 @@
 
     clazz = env->FindClass("android/media/MediaPlayer");
     if (clazz == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaPlayer");
         return;
     }
 
     fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
     if (fields.context == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mNativeContext");
         return;
     }
 
     fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.post_event == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.postEventFromNative");
         return;
     }
 
     fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
     if (fields.surface == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mSurface");
         return;
     }
 
     jclass surface = env->FindClass("android/view/Surface");
     if (surface == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
         return;
     }
 
     fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I");
     if (fields.surface_native == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "Can't find Surface." ANDROID_VIEW_SURFACE_JNI_ID);
         return;
     }
 
     fields.surfaceTexture = env->GetFieldID(clazz, "mSurfaceTexture",
             "Landroid/graphics/SurfaceTexture;");
     if (fields.surfaceTexture == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "Can't find MediaPlayer.mSurfaceTexture");
         return;
     }
 
     jclass surfaceTexture = env->FindClass("android/graphics/SurfaceTexture");
     if (surfaceTexture == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "Can't find android/graphics/SurfaceTexture");
         return;
     }
 
     fields.surfaceTexture_native = env->GetFieldID(surfaceTexture,
             ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "I");
     if (fields.surfaceTexture_native == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "Can't find SurfaceTexture." ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
         return;
     }
 
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 9d7bf2c..06058dc 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -213,7 +213,6 @@
 
     const char *pathStr = env->GetStringUTFChars(path, NULL);
     if (pathStr == NULL) {  // Out of memory
-        jniThrowException(env, kRunTimeException, "Out of memory");
         return;
     }
 
@@ -243,15 +242,14 @@
 
     const char *pathStr = env->GetStringUTFChars(path, NULL);
     if (pathStr == NULL) {  // Out of memory
-        jniThrowException(env, kRunTimeException, "Out of memory");
         return;
     }
 
     const char *mimeTypeStr =
         (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
     if (mimeType && mimeTypeStr == NULL) {  // Out of memory
+        // ReleaseStringUTFChars can be called with an exception pending.
         env->ReleaseStringUTFChars(path, pathStr);
-        jniThrowException(env, kRunTimeException, "Out of memory");
         return;
     }
 
@@ -281,7 +279,6 @@
     }
     const char *localeStr = env->GetStringUTFChars(locale, NULL);
     if (localeStr == NULL) {  // Out of memory
-        jniThrowException(env, kRunTimeException, "Out of memory");
         return;
     }
     mp->setLocale(localeStr);
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 17d39e3..2f88fd1 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -427,6 +427,9 @@
                 jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0);
                 if (stringValue) {
                     const char* str = env->GetStringUTFChars(stringValue, NULL);
+                    if (str == NULL) {
+                        return MTP_RESPONSE_GENERAL_ERROR;
+                    }
                     packet.putString(str);
                     env->ReleaseStringUTFChars(stringValue, str);
                 } else {
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index f5fcb4e..40bbaa3 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -110,6 +110,10 @@
 #ifdef HAVE_ANDROID_OS
     LOGD("open\n");
     const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+    if (deviceNameStr == NULL) {
+        return false;
+    }
+
     MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
     env->ReleaseStringUTFChars(deviceName, deviceNameStr);
 
@@ -426,12 +430,16 @@
     MtpDevice* device = get_device_from_object(env, thiz);
     if (device) {
         const char *destPathStr = env->GetStringUTFChars(dest_path, NULL);
+        if (destPathStr == NULL) {
+            return false;
+        }
+
         bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664);
         env->ReleaseStringUTFChars(dest_path, destPathStr);
         return result;
     }
 #endif
-    return NULL;
+    return false;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index c55189f..84c2c7e 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -97,7 +97,7 @@
     void removeStorage(MtpStorageID id) {
         MtpStorage* storage = mServer->getStorage(id);
         if (storage) {
-            for (int i = 0; i < mStorageList.size(); i++) {
+            for (size_t i = 0; i < mStorageList.size(); i++) {
                 if (mStorageList[i] == storage) {
                     mStorageList.removeAt(i);
                     break;
@@ -122,7 +122,7 @@
                     (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
 
             mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
-            for (int i = 0; i < mStorageList.size(); i++) {
+            for (size_t i = 0; i < mStorageList.size(); i++) {
                 mServer->addStorage(mStorageList[i]);
             }
         } else {
@@ -247,13 +247,17 @@
         jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
 
         const char *pathStr = env->GetStringUTFChars(path, NULL);
-        const char *descriptionStr = env->GetStringUTFChars(description, NULL);
-
-        MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace);
-        thread->addStorage(storage);
-
-        env->ReleaseStringUTFChars(path, pathStr);
-        env->ReleaseStringUTFChars(description, descriptionStr);
+        if (pathStr != NULL) {
+            const char *descriptionStr = env->GetStringUTFChars(description, NULL);
+            if (descriptionStr != NULL) {
+                MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace);
+                thread->addStorage(storage);
+                env->ReleaseStringUTFChars(path, pathStr);
+                env->ReleaseStringUTFChars(description, descriptionStr);
+            } else {
+                env->ReleaseStringUTFChars(path, pathStr);
+            }
+        }
     } else {
         LOGE("MtpThread is null in add_storage");
     }
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index aadeba5..a043329 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -170,7 +170,6 @@
     LOGV("Destructor %p", this);
 
     if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
-        setEnabled(false);
         if (mIEffect != NULL) {
             mIEffect->disconnect();
             mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png
index 84ac927..ade3716 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png
Binary files differ
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 768c0cd..5f84547 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -626,7 +626,7 @@
         }
 
         if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
-            showRecentAppsDialog(0);
+            showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
         } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_ACTIVITY) {
             try {
                 Intent intent = new Intent();
@@ -643,16 +643,24 @@
     }
 
     /**
-     * Create (if necessary) and launch the recent apps dialog
+     * Create (if necessary) and launch the recent apps dialog, or hide it if it is
+     * already shown.
      */
-    void showRecentAppsDialog(final int initialModifiers) {
+    void showOrHideRecentAppsDialog(final int heldModifiers, final boolean dismissIfShown) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mRecentAppsDialog == null) {
-                    mRecentAppsDialog = new RecentApplicationsDialog(mContext, initialModifiers);
+                    mRecentAppsDialog = new RecentApplicationsDialog(mContext);
                 }
-                mRecentAppsDialog.show();
+                if (mRecentAppsDialog.isShowing()) {
+                    if (dismissIfShown) {
+                        mRecentAppsDialog.dismiss();
+                    }
+                } else {
+                    mRecentAppsDialog.setHeldModifiers(heldModifiers);
+                    mRecentAppsDialog.show();
+                }
             }
         });
     }
@@ -1388,7 +1396,7 @@
             return false;
         } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
             if (down && repeatCount == 0) {
-                showRecentAppsDialog(event.getMetaState() & KeyEvent.getModifierMetaStateMask());
+                showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
             }
             return true;
         }
@@ -1430,6 +1438,7 @@
     /** {@inheritDoc} */
     @Override
     public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+        // Note: This method is only called if the initial down was unhandled.
         if (DEBUG_FALLBACK) {
             Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
                     + ", flags=" + event.getFlags()
@@ -1441,28 +1450,44 @@
         }
 
         if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-            // Invoke shortcuts using Meta as a fallback.
             final KeyCharacterMap kcm = event.getKeyCharacterMap();
             final int keyCode = event.getKeyCode();
             final int metaState = event.getMetaState();
-            if ((metaState & KeyEvent.META_META_ON) != 0) {
-                Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
-                        metaState & ~(KeyEvent.META_META_ON
-                                | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
-                if (shortcutIntent != null) {
-                    shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    try {
-                        mContext.startActivity(shortcutIntent);
-                    } catch (ActivityNotFoundException ex) {
-                        Slog.w(TAG, "Dropping shortcut key combination because "
-                                + "the activity to which it is registered was not found: "
-                                + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
+            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.getRepeatCount() == 0;
+
+            if (initialDown) {
+                // Invoke shortcuts using Meta as a fallback.
+                if ((metaState & KeyEvent.META_META_ON) != 0) {
+                    Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
+                            metaState & ~(KeyEvent.META_META_ON
+                                    | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
+                    if (shortcutIntent != null) {
+                        shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        try {
+                            mContext.startActivity(shortcutIntent);
+                        } catch (ActivityNotFoundException ex) {
+                            Slog.w(TAG, "Dropping shortcut key combination because "
+                                    + "the activity to which it is registered was not found: "
+                                    + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
+                        }
+                        return null;
                     }
-                    return null;
+                }
+
+                // Display task switcher for ALT-TAB or Meta-TAB.
+                if (keyCode == KeyEvent.KEYCODE_TAB) {
+                    final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+                    if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
+                            || KeyEvent.metaStateHasModifiers(
+                                    shiftlessModifiers, KeyEvent.META_META_ON)) {
+                        showOrHideRecentAppsDialog(shiftlessModifiers, false /*dismissIfShown*/);
+                        return null;
+                    }
                 }
             }
 
-            // Check for fallback actions.
+            // Check for fallback actions specified by the key character map.
             if (getFallbackAction(kcm, keyCode, metaState, mFallbackAction)) {
                 if (DEBUG_FALLBACK) {
                     Slog.d(TAG, "Fallback: keyCode=" + mFallbackAction.keyCode
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index c4b7822..aa00fbdd 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -71,12 +71,11 @@
         }
     };
 
-    private int mInitialModifiers;
+    private int mHeldModifiers;
 
-    public RecentApplicationsDialog(Context context, int initialModifiers) {
+    public RecentApplicationsDialog(Context context) {
         super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
 
-        mInitialModifiers = initialModifiers;
     }
 
     /**
@@ -125,9 +124,20 @@
         }
     }
 
+    /**
+     * Sets the modifier keys that are being held to keep the dialog open, or 0 if none.
+     * Used to make the recent apps dialog automatically dismiss itself when the modifiers
+     * all go up.
+     * @param heldModifiers The held key modifiers, such as {@link KeyEvent#META_ALT_ON}.
+     * Should exclude shift.
+     */
+    public void setHeldModifiers(int heldModifiers) {
+        mHeldModifiers = heldModifiers;
+    }
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_APP_SWITCH || keyCode == KeyEvent.KEYCODE_TAB) {
+        if (keyCode == KeyEvent.KEYCODE_TAB) {
             // Ignore all meta keys other than SHIFT.  The app switch key could be a
             // fallback action chorded with ALT, META or even CTRL depending on the key map.
             // DPad navigation is handled by the ViewRoot elsewhere.
@@ -166,7 +176,7 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mInitialModifiers != 0 && event.hasNoModifiers()) {
+        if (mHeldModifiers != 0 && (event.getModifiers() & mHeldModifiers) == 0) {
             final int numIcons = mIcons.length;
             RecentTag tag = null;
             for (int i = 0; i < numIcons; i++) {
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 9a9d9e5..ff4b11a 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -539,7 +539,24 @@
                                 (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                 iev.type, iev.code, iev.value);
 
+#ifdef HAVE_POSIX_CLOCKS
+                        // Use the time specified in the event instead of the current time
+                        // so that downstream code can get more accurate estimates of
+                        // event dispatch latency from the time the event is enqueued onto
+                        // the evdev client buffer.
+                        //
+                        // The event's timestamp fortuitously uses the same monotonic clock
+                        // time base as the rest of Android.  The kernel event device driver
+                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
+                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
+                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
+                        // system call that also queries ktime_get_ts().
+                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+                                + nsecs_t(iev.time.tv_usec) * 1000LL;
+                        LOGV("event time %lld, now %lld", event->when, now);
+#else
                         event->when = now;
+#endif
                         event->deviceId = deviceId;
                         event->type = iev.type;
                         event->scanCode = iev.code;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 253d070..46de933 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -79,6 +79,22 @@
 // before considering it stale and dropping it.
 const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
 
+// Motion samples that are received within this amount of time are simply coalesced
+// when batched instead of being appended.  This is done because some drivers update
+// the location of pointers one at a time instead of all at once.
+// For example, when there are 10 fingers down, the input dispatcher may receive 10
+// samples in quick succession with only one finger's location changed in each sample.
+//
+// This value effectively imposes an upper bound on the touch sampling rate.
+// Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct
+// samples to become available 5-20ms apart but individual finger reports can trickle
+// in over a period of 2-4ms or so.
+//
+// Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough,
+// a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce
+// significant quantization noise on current hardware.
+const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz
+
 
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -2505,25 +2521,43 @@
                     continue;
                 }
 
-                if (motionEntry->action != action
-                        || motionEntry->pointerCount != pointerCount
-                        || motionEntry->isInjected()) {
+                if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
                     // Last motion event in the queue for this device and source is
                     // not compatible for appending new samples.  Stop here.
                     goto NoBatchingOrStreaming;
                 }
 
-                // The last motion event is a move and is compatible for appending.
                 // Do the batching magic.
-                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
-#if DEBUG_BATCHING
-                LOGD("Appended motion sample onto batch for most recent "
-                        "motion event for this device in the inbound queue.");
-#endif
+                batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+                        "most recent motion event for this device and source in the inbound queue");
                 mLock.unlock();
                 return; // done!
             }
 
+            // BATCHING ONTO PENDING EVENT CASE
+            //
+            // Try to append a move sample to the currently pending event, if there is one.
+            // We can do this as long as we are still waiting to find the targets for the
+            // event.  Once the targets are locked-in we can only do streaming.
+            if (mPendingEvent
+                    && (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid)
+                    && mPendingEvent->type == EventEntry::TYPE_MOTION) {
+                MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
+                if (motionEntry->deviceId == deviceId && motionEntry->source == source) {
+                    if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
+                        // Pending motion event is for this device and source but it is
+                        // not compatible for appending new samples.  Stop here.
+                        goto NoBatchingOrStreaming;
+                    }
+
+                    // Do the batching magic.
+                    batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+                            "pending motion event");
+                    mLock.unlock();
+                    return; // done!
+                }
+            }
+
             // STREAMING CASE
             //
             // There is no pending motion event (of any kind) for this device in the inbound queue.
@@ -2601,7 +2635,7 @@
                     mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
 #if DEBUG_BATCHING
                     LOGD("Appended motion sample onto batch for most recently dispatched "
-                            "motion event for this device in the outbound queues.  "
+                            "motion event for this device and source in the outbound queues.  "
                             "Attempting to stream the motion sample.");
 #endif
                     nsecs_t currentTime = now();
@@ -2632,6 +2666,36 @@
     }
 }
 
+void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime,
+        int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) {
+    // Combine meta states.
+    entry->metaState |= metaState;
+
+    // Coalesce this sample if not enough time has elapsed since the last sample was
+    // initially appended to the batch.
+    MotionSample* lastSample = entry->lastSample;
+    long interval = eventTime - lastSample->eventTimeBeforeCoalescing;
+    if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) {
+        uint32_t pointerCount = entry->pointerCount;
+        for (uint32_t i = 0; i < pointerCount; i++) {
+            lastSample->pointerCoords[i].copyFrom(pointerCoords[i]);
+        }
+        lastSample->eventTime = eventTime;
+#if DEBUG_BATCHING
+        LOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart",
+                eventDescription, interval * 0.000001f);
+#endif
+        return;
+    }
+
+    // Append the sample.
+    mAllocator.appendMotionSample(entry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+    LOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart",
+            eventDescription, interval * 0.000001f);
+#endif
+}
+
 void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
         uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
@@ -3727,6 +3791,7 @@
     entry->downTime = downTime;
     entry->pointerCount = pointerCount;
     entry->firstSample.eventTime = eventTime;
+    entry->firstSample.eventTimeBeforeCoalescing = eventTime;
     entry->firstSample.next = NULL;
     entry->lastSample = & entry->firstSample;
     for (uint32_t i = 0; i < pointerCount; i++) {
@@ -3836,6 +3901,7 @@
         nsecs_t eventTime, const PointerCoords* pointerCoords) {
     MotionSample* sample = mMotionSamplePool.alloc();
     sample->eventTime = eventTime;
+    sample->eventTimeBeforeCoalescing = eventTime;
     uint32_t pointerCount = motionEntry->pointerCount;
     for (uint32_t i = 0; i < pointerCount; i++) {
         sample->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -3865,6 +3931,21 @@
     return count;
 }
 
+bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount,
+        const int32_t* pointerIds) const {
+    if (this->action != action
+            || this->pointerCount != pointerCount
+            || this->isInjected()) {
+        return false;
+    }
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (this->pointerIds[i] != pointerIds[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
 
 // --- InputDispatcher::InputState ---
 
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 162e606..af0153b 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -409,7 +409,7 @@
 
         bool dispatchInProgress; // initially false, set to true while dispatching
 
-        inline bool isInjected() { return injectionState != NULL; }
+        inline bool isInjected() const { return injectionState != NULL; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -439,7 +439,8 @@
     struct MotionSample {
         MotionSample* next;
 
-        nsecs_t eventTime;
+        nsecs_t eventTime; // may be updated during coalescing
+        nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing
         PointerCoords pointerCoords[MAX_POINTERS];
     };
 
@@ -461,6 +462,10 @@
         MotionSample* lastSample;
 
         uint32_t countSamples() const;
+
+        // Checks whether we can append samples, assuming the device id and source are the same.
+        bool canAppendSamples(int32_t action, uint32_t pointerCount,
+                const int32_t* pointerIds) const;
     };
 
     // Tracks the progress of dispatching a particular event to a particular connection.
@@ -802,6 +807,11 @@
     void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
             nsecs_t* nextWakeupTime);
 
+    // Batches a new sample onto a motion entry.
+    // Assumes that the we have already checked that we can append samples.
+    void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState,
+            const PointerCoords* pointerCoords, const char* eventDescription);
+
     // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
     bool enqueueInboundEventLocked(EventEntry* entry);
 
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 47599c8..1aff9a2 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -390,7 +390,7 @@
         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
 
-        if (true) {
+        if (false) {
             Slog.d(TAG, "level:" + mBatteryLevel +
                     " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
                     " health:" + mBatteryHealth +  " present:" + mBatteryPresent +
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 9e1e0c6..99217b3 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -1441,6 +1441,12 @@
     void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response);
 
     /**
+     *  Requests to get the CDMA subscription srouce
+     * @param response is callback message
+     */
+    void getCdmaSubscriptionSource(Message response);
+
+    /**
      *  Set the TTY mode
      *
      * @param ttyMode one of the following:
diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java
index fda1e47..a883e8e 100644
--- a/telephony/java/com/android/internal/telephony/DataCallState.java
+++ b/telephony/java/com/android/internal/telephony/DataCallState.java
@@ -17,11 +17,26 @@
 
 package com.android.internal.telephony;
 
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.telephony.DataConnection.FailCause;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
 /**
  * This is RIL_Data_Call_Response_v5 from ril.h
  * TODO: Rename to DataCallResponse.
  */
 public class DataCallState {
+    private final boolean DBG = true;
+    private final String LOG_TAG = "GSM";
+
     public int version = 0;
     public int status = 0;
     public int cid = 0;
@@ -32,6 +47,29 @@
     public String [] dnses = new String[0];
     public String[] gateways = new String[0];
 
+    /**
+     * Class returned by onSetupConnectionCompleted.
+     */
+    protected enum SetupResult {
+        SUCCESS,
+        ERR_BadCommand,
+        ERR_UnacceptableParameter,
+        ERR_GetLastErrorFromRil,
+        ERR_Stale,
+        ERR_RilError;
+
+        public FailCause mFailCause;
+
+        SetupResult() {
+            mFailCause = FailCause.fromInt(0);
+        }
+
+        @Override
+        public String toString() {
+            return name() + "  SetupResult.mFailCause=" + mFailCause;
+        }
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
@@ -63,4 +101,127 @@
         sb.append("]}");
         return sb.toString();
     }
+
+    public SetupResult setLinkProperties(LinkProperties linkProperties,
+            boolean okToUseSystemPropertyDns) {
+        SetupResult result;
+
+        // Start with clean network properties and if we have
+        // a failure we'll clear again at the bottom of this code.
+        if (linkProperties == null)
+            linkProperties = new LinkProperties();
+        else
+            linkProperties.clear();
+
+        if (status == FailCause.NONE.getErrorCode()) {
+            String propertyPrefix = "net." + ifname + ".";
+
+            try {
+                // set interface name
+                linkProperties.setInterfaceName(ifname);
+
+                // set link addresses
+                if (addresses != null && addresses.length > 0) {
+                    for (String addr : addresses) {
+                        LinkAddress la;
+                        int addrPrefixLen;
+
+                        String [] ap = addr.split("/");
+                        if (ap.length == 2) {
+                            addr = ap[0];
+                            addrPrefixLen = Integer.parseInt(ap[1]);
+                        } else {
+                            addrPrefixLen = 0;
+                        }
+                        InetAddress ia;
+                        try {
+                            ia = NetworkUtils.numericToInetAddress(addr);
+                        } catch (IllegalArgumentException e) {
+                            throw new UnknownHostException("Non-numeric ip addr=" + addr);
+                        }
+                        if (addrPrefixLen == 0) {
+                            // Assume point to point
+                            addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
+                        }
+                        if (DBG) Log.d(LOG_TAG, "addr/pl=" + addr + "/" + addrPrefixLen);
+                        la = new LinkAddress(ia, addrPrefixLen);
+                        linkProperties.addLinkAddress(la);
+                    }
+                } else {
+                    throw new UnknownHostException("no address for ifname=" + ifname);
+                }
+
+                // set dns servers
+                if (dnses != null && dnses.length > 0) {
+                    for (String addr : dnses) {
+                        InetAddress ia;
+                        try {
+                            ia = NetworkUtils.numericToInetAddress(addr);
+                        } catch (IllegalArgumentException e) {
+                            throw new UnknownHostException("Non-numeric dns addr=" + addr);
+                        }
+                        linkProperties.addDns(ia);
+                    }
+                } else if (okToUseSystemPropertyDns){
+                    String dnsServers[] = new String[2];
+                    dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+                    dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+                    for (String dnsAddr : dnsServers) {
+                            InetAddress ia;
+                            try {
+                                ia = NetworkUtils.numericToInetAddress(dnsAddr);
+                            } catch (IllegalArgumentException e) {
+                                throw new UnknownHostException("Non-numeric dns addr="
+                                            + dnsAddr);
+                            }
+                            linkProperties.addDns(ia);
+                    }
+                } else {
+                    throw new UnknownHostException("Empty dns response and no system default dns");
+                }
+
+                // set gateways
+                if ((gateways == null) || (gateways.length == 0)) {
+                    String sysGateways = SystemProperties.get(propertyPrefix + "gw");
+                    if (sysGateways != null) {
+                        gateways = sysGateways.split(" ");
+                    } else {
+                        gateways = new String[0];
+                    }
+                }
+                for (String addr : gateways) {
+                    InetAddress ia;
+                    try {
+                        ia = NetworkUtils.numericToInetAddress(addr);
+                    } catch (IllegalArgumentException e) {
+                        throw new UnknownHostException("Non-numeric gateway addr=" + addr);
+                    }
+                    linkProperties.addGateway(ia);
+                }
+
+                result = SetupResult.SUCCESS;
+            } catch (UnknownHostException e) {
+                Log.d(LOG_TAG, "onSetupCompleted: UnknownHostException " + e);
+                e.printStackTrace();
+                result = SetupResult.ERR_UnacceptableParameter;
+            }
+        } else {
+            if (version < 4) {
+                result = SetupResult.ERR_GetLastErrorFromRil;
+            } else {
+                result = SetupResult.ERR_RilError;
+            }
+        }
+
+        // An error occurred so clear properties
+        if (result != SetupResult.SUCCESS) {
+            if(DBG) Log.d(LOG_TAG,
+                    "onSetupConnectionCompleted with an error, clearing LinkProperties");
+            linkProperties.clear();
+        }
+
+        return result;
+    }
 }
+
+
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index e21e951..aafe5ef 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -62,29 +62,6 @@
     protected static int mCount;
 
     /**
-     * Class returned by onSetupConnectionCompleted.
-     */
-    protected enum SetupResult {
-        SUCCESS,
-        ERR_BadCommand,
-        ERR_UnacceptableParameter,
-        ERR_GetLastErrorFromRil,
-        ERR_Stale,
-        ERR_RilError;
-
-        public FailCause mFailCause;
-
-        SetupResult() {
-            mFailCause = FailCause.fromInt(0);
-        }
-
-        @Override
-        public String toString() {
-            return name() + "  SetupResult.mFailCause=" + mFailCause;
-        }
-    }
-
-    /**
      * Used internally for saving connecting parameters.
      */
     protected static class ConnectionParams {
@@ -445,10 +422,10 @@
      * @param ar is the result
      * @return SetupResult.
      */
-    private SetupResult onSetupConnectionCompleted(AsyncResult ar) {
+    private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
         DataCallState response = (DataCallState) ar.result;
         ConnectionParams cp = (ConnectionParams) ar.userObj;
-        SetupResult result;
+        DataCallState.SetupResult result;
 
         if (ar.exception != null) {
             if (DBG) {
@@ -459,148 +436,35 @@
             if (ar.exception instanceof CommandException
                     && ((CommandException) (ar.exception)).getCommandError()
                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
-                result = SetupResult.ERR_BadCommand;
+                result = DataCallState.SetupResult.ERR_BadCommand;
                 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
             } else if ((response == null) || (response.version < 4)) {
-                result = SetupResult.ERR_GetLastErrorFromRil;
+                result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
             } else {
-                result = SetupResult.ERR_RilError;
+                result = DataCallState.SetupResult.ERR_RilError;
                 result.mFailCause = FailCause.fromInt(response.status);
             }
         } else if (cp.tag != mTag) {
             if (DBG) {
                 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
             }
-            result = SetupResult.ERR_Stale;
+            result = DataCallState.SetupResult.ERR_Stale;
         } else {
             log("onSetupConnectionCompleted received DataCallState: " + response);
 
-            // Start with clean network properties and if we have
-            // a failure we'll clear again at the bottom of this code.
-            LinkProperties linkProperties = new LinkProperties();
-            if (response.status == FailCause.NONE.getErrorCode()) {
-                String propertyPrefix = "net." + response.ifname + ".";
+            // Check if system property dns usable
+            boolean okToUseSystemPropertyDns = false;
+            String propertyPrefix = "net." + response.ifname + ".";
+            String dnsServers[] = new String[2];
+            dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+            dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+            okToUseSystemPropertyDns = isDnsOk(dnsServers);
 
-                try {
-                    cid = response.cid;
-                    linkProperties.setInterfaceName(response.ifname);
-                    if (response.addresses != null && response.addresses.length > 0) {
-                        for (String addr : response.addresses) {
-                            LinkAddress la;
-                            int addrPrefixLen;
-
-                            String [] ap = addr.split("/");
-                            if (ap.length == 2) {
-                                addr = ap[0];
-                                addrPrefixLen = Integer.parseInt(ap[1]);
-                            } else {
-                                addrPrefixLen = 0;
-                            }
-                            InetAddress ia;
-                            try {
-                                ia = NetworkUtils.numericToInetAddress(addr);
-                            } catch (IllegalArgumentException e) {
-                                EventLogTags.writeBadIpAddress(addr);
-                                throw new UnknownHostException("Non-numeric ip addr=" + addr);
-                            }
-                            if (addrPrefixLen == 0) {
-                                // Assume point to point
-                                addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
-                            }
-                            if (DBG) log("addr/pl=" + addr + "/" + addrPrefixLen);
-                            la = new LinkAddress(ia, addrPrefixLen);
-                            linkProperties.addLinkAddress(la);
-                        }
-                    } else {
-                        EventLogTags.writeBadIpAddress("no address for ifname=" + response.ifname);
-                        throw new UnknownHostException("no address for ifname=" + response.ifname);
-                    }
-                    if (response.dnses != null && response.dnses.length > 0) {
-                        for (String addr : response.dnses) {
-                            InetAddress ia;
-                            try {
-                                ia = NetworkUtils.numericToInetAddress(addr);
-                            } catch (IllegalArgumentException e) {
-                                EventLogTags.writePdpBadDnsAddress("dns=" + addr); 
-                                throw new UnknownHostException("Non-numeric dns addr=" + addr);
-                            }
-                            linkProperties.addDns(ia);
-                        }
-                        result = SetupResult.SUCCESS;
-                    } else {
-                        String dnsServers[] = new String[2];
-                        dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
-                        dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
-                        if (isDnsOk(dnsServers)) {
-                            for (String dnsAddr : dnsServers) {
-                                InetAddress ia;
-                                try {
-                                    ia = NetworkUtils.numericToInetAddress(dnsAddr);
-                                } catch (IllegalArgumentException e) {
-                                    EventLogTags.writePdpBadDnsAddress("dnsAddr=" + dnsAddr);
-                                    throw new UnknownHostException("Non-numeric dns addr="
-                                                + dnsAddr);
-                                }
-                                linkProperties.addDns(ia);
-                            }
-                            result = SetupResult.SUCCESS;
-                        } else {
-                            StringBuilder sb = new StringBuilder();
-                            for (String dnsAddr : dnsServers) {
-                                sb.append(dnsAddr);
-                                sb.append(" ");
-                            }
-                            EventLogTags.writePdpBadDnsAddress("Unacceptable dns addresses=" + sb);
-                            throw new UnknownHostException("Unacceptable dns addresses=" + sb);
-                        }
-                    }
-                    if ((response.gateways == null) || (response.gateways.length == 0)) {
-                        String gateways = SystemProperties.get(propertyPrefix + "gw");
-                        if (gateways != null) {
-                            response.gateways = gateways.split(" ");
-                        } else {
-                            response.gateways = new String[0];
-                        }
-                    }
-                    for (String addr : response.gateways) {
-                        InetAddress ia;
-                        try {
-                            ia = NetworkUtils.numericToInetAddress(addr);
-                        } catch (IllegalArgumentException e) {
-                            EventLogTags.writePdpBadDnsAddress("gateway=" + addr);
-                            throw new UnknownHostException("Non-numeric gateway addr=" + addr);
-                        }
-                        linkProperties.addGateway(ia);
-                    }
-                    result = SetupResult.SUCCESS;
-                } catch (UnknownHostException e) {
-                    log("onSetupCompleted: UnknownHostException " + e);
-                    e.printStackTrace();
-                    result = SetupResult.ERR_UnacceptableParameter;
-                }
-            } else {
-                if (response.version < 4) {
-                    result = SetupResult.ERR_GetLastErrorFromRil;
-                } else {
-                    result = SetupResult.ERR_RilError;
-                }
-            }
-
-            // An error occurred so clear properties
-            if (result != SetupResult.SUCCESS) {
-                log("onSetupConnectionCompleted with an error, clearing LinkProperties");
-                linkProperties.clear();
-            }
-            mLinkProperties = linkProperties;
+            // set link properties based on data call response
+            result = response.setLinkProperties(mLinkProperties,
+                    okToUseSystemPropertyDns);
         }
 
-        if (DBG) {
-            log("onSetupConnectionCompleted: DataConnection setup result='"
-                    + result + "' on cid=" + cid);
-            if (result == SetupResult.SUCCESS) {
-                log("onSetupConnectionCompleted: LinkProperties: " + mLinkProperties.toString());
-            }
-        }
         return result;
     }
 
@@ -746,7 +610,7 @@
                     ar = (AsyncResult) msg.obj;
                     cp = (ConnectionParams) ar.userObj;
 
-                    SetupResult result = onSetupConnectionCompleted(ar);
+                    DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
                     if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
                     switch (result) {
                         case SUCCESS:
@@ -780,7 +644,7 @@
                             // Request is stale, ignore.
                             break;
                         default:
-                            throw new RuntimeException("Unkown SetupResult, should not happen");
+                            throw new RuntimeException("Unknown SetupResult, should not happen");
                     }
                     retVal = true;
                     break;
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 1d47405..b0a265b 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -3634,12 +3634,12 @@
     /**
      * {@inheritDoc}
      */
-    public void getCdmaSubscriptionSource(int cdmaSubscription , Message response) {
+    @Override
+    public void getCdmaSubscriptionSource(Message response) {
         RILRequest rr = RILRequest.obtain(
                 RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, response);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + cdmaSubscription);
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
         send(rr);
     }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index f2ece7f..5208ccd 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -326,6 +326,10 @@
     public void reportStkServiceIsRunning(Message result) {
     }
 
+    @Override
+    public void getCdmaSubscriptionSource(Message response) {
+    }
+
     public void getGsmBroadcastConfig(Message response) {
     }
 
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index d9bd7e8..80de9f3 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1001,6 +1001,11 @@
         resultSuccess(result, null);
     }
 
+    @Override
+    public void getCdmaSubscriptionSource(Message result) {
+        unimplemented(result);
+    }
+
     private boolean isSimLocked() {
         if (mSimLockedState != SimLockState.NONE) {
             return true;