Merge change 22008 into eclair

* changes:
  When bringing up the media controller view, focus the play/pause button.
diff --git a/api/current.xml b/api/current.xml
index ec76edb..c5f5919 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -7489,6 +7489,28 @@
  visibility="public"
 >
 </field>
+<field name="supportsUploading"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843410"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="supportsUploading"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843410"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="syncable"
  type="int"
  transient="false"
@@ -8567,6 +8589,50 @@
  visibility="public"
 >
 </field>
+<field name="wallpaperActivityCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843412"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperActivityCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843413"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperActivityOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843410"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperActivityOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843411"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="webViewStyle"
  type="int"
  transient="false"
@@ -12087,6 +12153,17 @@
  visibility="public"
 >
 </field>
+<field name="Animation_InputMethod"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973910"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Animation_Toast"
  type="int"
  transient="false"
@@ -24946,6 +25023,235 @@
 </field>
 </class>
 </package>
+<package name="android.bluetooth"
+>
+<class name="BluetoothAdapter"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getRemoteDevice"
+ return="android.bluetooth.BluetoothDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="address" type="java.lang.String">
+</parameter>
+</method>
+<method name="listenUsingRfcommOn"
+ return="android.bluetooth.BluetoothServerSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="channel" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BluetoothDevice"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="createRfcommSocket"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="channel" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAddress"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="out" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+</class>
+<class name="BluetoothServerSocket"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<method name="accept"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="accept"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BluetoothSocket"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="connect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getInputStream"
+ return="java.io.InputStream"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getOutputStream"
+ return="java.io.OutputStream"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getRemoteDevice"
+ return="android.bluetooth.BluetoothDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+</package>
 <package name="android.content"
 >
 <class name="AbstractCursorEntityIterator"
@@ -29304,6 +29610,17 @@
  visibility="public"
 >
 </field>
+<field name="BLUETOOTH_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;bluetooth&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CLIPBOARD_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -35829,6 +36146,8 @@
 </parameter>
 <parameter name="userVisible" type="boolean">
 </parameter>
+<parameter name="supportsUploading" type="boolean">
+</parameter>
 </constructor>
 <constructor name="SyncAdapterType"
  type="android.content.SyncAdapterType"
@@ -35851,6 +36170,17 @@
  visibility="public"
 >
 </method>
+<method name="isUserVisible"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="newKey"
  return="android.content.SyncAdapterType"
  abstract="false"
@@ -35866,6 +36196,17 @@
 <parameter name="accountType" type="java.lang.String">
 </parameter>
 </method>
+<method name="supportsUploading"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="writeToParcel"
  return="void"
  abstract="false"
@@ -35911,7 +36252,7 @@
  visibility="public"
 >
 </field>
-<field name="userVisible"
+<field name="isKey"
  type="boolean"
  transient="false"
  volatile="false"
@@ -144974,6 +145315,8 @@
 >
 <parameter name="dirty" type="android.graphics.Rect">
 </parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
 <exception name="Surface.OutOfResourcesException" type="android.view.Surface.OutOfResourcesException">
 </exception>
 </method>
diff --git a/cmds/stagefright/JPEGSource.cpp b/cmds/stagefright/JPEGSource.cpp
index 338a3d5..a7994ed 100644
--- a/cmds/stagefright/JPEGSource.cpp
+++ b/cmds/stagefright/JPEGSource.cpp
@@ -60,6 +60,7 @@
       mHeight(0),
       mOffset(0) {
     CHECK_EQ(parseJPEG(), OK);
+    CHECK(mSource->getSize(&mSize) == OK);
 }
 
 JPEGSource::~JPEGSource() {
@@ -73,10 +74,6 @@
         return UNKNOWN_ERROR;
     }
 
-    if (mSource->getSize(&mSize) != OK) {
-        return UNKNOWN_ERROR;
-    }
-
     mGroup = new MediaBufferGroup;
     mGroup->add_buffer(new MediaBuffer(mSize));
 
@@ -105,6 +102,7 @@
     meta->setCString(kKeyMIMEType, "image/jpeg");
     meta->setInt32(kKeyWidth, mWidth);
     meta->setInt32(kKeyHeight, mHeight);
+    meta->setInt32(kKeyCompressedSize, mSize);
 
     return meta;
 }
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 56f6338..185e6ac 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -70,6 +70,7 @@
 
     long numIterationsLeft = gNumRepetitions;
     MediaSource::ReadOptions options;
+
     while (numIterationsLeft-- > 0) {
         MediaBuffer *buffer;
 
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index cced338..a4c141e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -322,6 +322,7 @@
             if (mInfo != null) {
                 Context theirContext = mContext.createPackageContext(
                         mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED);
+                mRemoteContext = theirContext;
                 LayoutInflater inflater = (LayoutInflater)
                         theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 inflater = inflater.cloneInContext(theirContext);
@@ -336,8 +337,8 @@
             exception = e;
         }
         
-        if (exception != null && LOGD) {
-            Log.w(TAG, "Error inflating AppWidget " + mInfo, exception);
+        if (exception != null) {
+            Log.w(TAG, "Error inflating AppWidget " + mInfo + ": " + exception.toString());
         }
         
         if (defaultView == null) {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 6e48b66..b531a50 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -213,31 +213,6 @@
         }
     }
 
-    /**
-     * Check class bits for possible A2DP Sink support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might be a A2DP Sink. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     * @return True if this device might be a A2DP sink
-     */
-    public static boolean doesClassMatchSink(int btClass) {
-        if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
-            return true;
-        }
-        // By the A2DP spec, sinks must indicate the RENDER service.
-        // However we found some that do not (Chordette). So lets also
-        // match on some other class bits.
-        switch (BluetoothClass.Device.getDevice(btClass)) {
-        case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
-        case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
-        case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
-        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-            return true;
-        default:
-            return false;
-        }
-    }
-
     /** Helper for converting a state to a string.
      * For debug use only - strings are not internationalized.
      * @hide
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d207540..6bd2a5a 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -27,34 +27,54 @@
 /**
  * Represents the local Bluetooth adapter.
  *
- * @hide
+ * <p>Use {@link android.content.Context#getSystemService} with {@link
+ * android.content.Context#BLUETOOTH_SERVICE} to get the default local
+ * Bluetooth adapter. On most Android devices there is only one local
+ * Bluetotoh adapter.
+ *
+ * <p>Use the {@link BluetoothDevice} class for operations on remote Bluetooth
+ * devices.
+ *
+ * <p>TODO: unhide more of this class
  */
 public final class BluetoothAdapter {
     private static final String TAG = "BluetoothAdapter";
 
+    /** @hide */
     public static final int BLUETOOTH_STATE_OFF = 0;
+    /** @hide */
     public static final int BLUETOOTH_STATE_TURNING_ON = 1;
+    /** @hide */
     public static final int BLUETOOTH_STATE_ON = 2;
+    /** @hide */
     public static final int BLUETOOTH_STATE_TURNING_OFF = 3;
 
     /** Inquiry scan and page scan are both off.
-     *  Device is neither discoverable nor connectable */
+     *  Device is neither discoverable nor connectable
+     *  @hide */
     public static final int SCAN_MODE_NONE = 0;
     /** Page scan is on, inquiry scan is off.
-     *  Device is connectable, but not discoverable */
+     *  Device is connectable, but not discoverable
+     *  @hide*/
     public static final int SCAN_MODE_CONNECTABLE = 1;
     /** Page scan and inquiry scan are on.
-     *  Device is connectable and discoverable */
+     *  Device is connectable and discoverable
+     *  @hide*/
     public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3;
 
+    /** @hide */
     public static final int RESULT_FAILURE = -1;
+    /** @hide */
     public static final int RESULT_SUCCESS = 0;
 
-    /* The user will be prompted to enter a pin */
+    /** The user will be prompted to enter a pin
+     * @hide */
     public static final int PAIRING_VARIANT_PIN = 0;
-    /* The user will be prompted to enter a passkey */
+    /** The user will be prompted to enter a passkey
+     * @hide */
     public static final int PAIRING_VARIANT_PASSKEY = 1;
-    /* The user will be prompted to confirm the passkey displayed on the screen */
+    /** The user will be prompted to confirm the passkey displayed on the screen
+     * @hide */
     public static final int PAIRING_VARIANT_CONFIRMATION = 2;
 
     private final IBluetooth mService;
@@ -71,9 +91,14 @@
     }
 
     /**
-     * Get the remote BluetoothDevice associated with the given MAC address.
-     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB".
+     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
+     * address.
+     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
+     * such as "00:11:22:33:AA:BB".
+     * <p>A {@link BluetoothDevice} will always be returned for a valid
+     * hardware address, even if this adapter has never seen that device.
      * @param address valid Bluetooth MAC address
+     * @throws IllegalArgumentException if address is invalid
      */
     public BluetoothDevice getRemoteDevice(String address) {
         return new BluetoothDevice(address);
@@ -83,6 +108,7 @@
      * Is Bluetooth currently turned on.
      *
      * @return true if Bluetooth enabled, false otherwise.
+     * @hide
      */
     public boolean isEnabled() {
         try {
@@ -95,6 +121,7 @@
      * Get the current state of Bluetooth.
      *
      * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR.
+     * @hide
      */
     public int getBluetoothState() {
         try {
@@ -112,6 +139,7 @@
      * @return false if we cannot enable the Bluetooth device. True does not
      * imply the device was enabled, it only implies that so far there were no
      * problems.
+     * @hide
      */
     public boolean enable() {
         try {
@@ -125,6 +153,7 @@
      * This turns off the underlying hardware.
      *
      * @return true if successful, false otherwise.
+     * @hide
      */
     public boolean disable() {
         try {
@@ -133,6 +162,7 @@
         return false;
     }
 
+    /** @hide */
     public String getAddress() {
         try {
             return mService.getAddress();
@@ -147,6 +177,7 @@
      * possible to retrieve the Bluetooth name when Bluetooth is enabled.
      *
      * @return the Bluetooth name, or null if there was a problem.
+     * @hide
      */
     public String getName() {
         try {
@@ -163,6 +194,7 @@
      *
      * @param name the name to set
      * @return     true, if the name was successfully set. False otherwise.
+     * @hide
      */
     public boolean setName(String name) {
         try {
@@ -175,6 +207,7 @@
      * Get the current scan mode.
      * Used to determine if the local device is connectable and/or discoverable
      * @return Scan mode, one of SCAN_MODE_* or an error code
+     * @hide
      */
     public int getScanMode() {
         try {
@@ -187,6 +220,7 @@
      * Set the current scan mode.
      * Used to make the local device connectable and/or discoverable
      * @param scanMode One of SCAN_MODE_*
+     * @hide
      */
     public void setScanMode(int scanMode) {
         try {
@@ -194,6 +228,7 @@
         } catch (RemoteException e) {Log.e(TAG, "", e);}
     }
 
+    /** @hide */
     public int getDiscoverableTimeout() {
         try {
             return mService.getDiscoverableTimeout();
@@ -201,12 +236,14 @@
         return -1;
     }
 
+    /** @hide */
     public void setDiscoverableTimeout(int timeout) {
         try {
             mService.setDiscoverableTimeout(timeout);
         } catch (RemoteException e) {Log.e(TAG, "", e);}
     }
 
+    /** @hide */
     public boolean startDiscovery() {
         try {
             return mService.startDiscovery();
@@ -214,12 +251,14 @@
         return false;
     }
 
+    /** @hide */
     public void cancelDiscovery() {
         try {
             mService.cancelDiscovery();
         } catch (RemoteException e) {Log.e(TAG, "", e);}
     }
 
+    /** @hide */
     public boolean isDiscovering() {
         try {
             return mService.isDiscovering();
@@ -248,6 +287,7 @@
      * returned.
      *
      * @return unmodifiable set of bonded devices, or null on error
+     * @hide
      */
     public Set<BluetoothDevice> getBondedDevices() {
         try {
@@ -257,17 +297,20 @@
     }
 
     /**
-     * Construct a listening, secure RFCOMM server socket.
-     * The remote device connecting to this socket will be authenticated and
+     * Create a listening, secure RFCOMM Bluetooth socket.
+     * <p>A remote device connecting to this socket will be authenticated and
      * communication on this socket will be encrypted.
-     * Call #accept to retrieve connections to this socket.
-     * @return An RFCOMM BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or
-     *                     insufficient permissions.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+     * connections to listening {@link BluetoothServerSocket}.
+     * <p>Valid RFCOMM channels are in range 1 to 30.
+     * @param channel RFCOMM channel to listen on
+     * @return a listening RFCOMM BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions, or channel in use.
      */
-    public BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
+    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
         BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, true, true, port);
+                BluetoothSocket.TYPE_RFCOMM, true, true, channel);
         try {
             socket.mSocket.bindListenNative();
         } catch (IOException e) {
@@ -285,6 +328,7 @@
      * @return An RFCOMM BluetoothServerSocket
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient permissions.
+     * @hide
      */
     public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
         BluetoothServerSocket socket = new BluetoothServerSocket(
@@ -306,6 +350,7 @@
      * @return A SCO BluetoothServerSocket
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient permissions.
+     * @hide
      */
     public static BluetoothServerSocket listenUsingScoOn() throws IOException {
         BluetoothServerSocket socket = new BluetoothServerSocket(
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 88ce18b..0061f10 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -46,6 +46,10 @@
     /** Indicates the Bluetooth API could not retrieve the class */
     public static final int ERROR = 0xFF000000;
 
+    public static final int PROFILE_HEADSET = 0;
+    public static final int PROFILE_A2DP = 1;
+    public static final int PROFILE_OPP = 2;
+
     /** Every Bluetooth device has zero or more service classes */
     public static class Service {
         public static final int BITMASK                 = 0xFFE000;
@@ -187,5 +191,74 @@
             return (btClass & Device.BITMASK);
         }
     }
+
+    /**
+     * Check class bits for possible bluetooth profile support.
+     * This is a simple heuristic that tries to guess if a device with the
+     * given class bits might support specified profile. It is not accurate for all
+     * devices. It tries to err on the side of false positives.
+     * @param btClass The class
+     * @param profile The profile to be checked
+     * @return True if this device might support specified profile.
+     */
+    public static boolean doesClassMatch(int btClass, int profile) {
+        if (profile == PROFILE_A2DP) {
+            if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
+                return true;
+            }
+            // By the A2DP spec, sinks must indicate the RENDER service.
+            // However we found some that do not (Chordette). So lets also
+            // match on some other class bits.
+            switch (BluetoothClass.Device.getDevice(btClass)) {
+                case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
+                case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
+                case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
+                case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+                    return true;
+                default:
+                    return false;
+            }
+        } else if (profile == PROFILE_HEADSET) {
+            // The render service class is required by the spec for HFP, so is a
+            // pretty good signal
+            if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
+                return true;
+            }
+            // Just in case they forgot the render service class
+            switch (BluetoothClass.Device.getDevice(btClass)) {
+                case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+                case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+                case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+                    return true;
+                default:
+                    return false;
+            }
+        } else if (profile == PROFILE_OPP) {
+            if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.OBJECT_TRANSFER)) {
+                return true;
+            }
+
+            switch (BluetoothClass.Device.getDevice(btClass)) {
+                case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
+                case BluetoothClass.Device.COMPUTER_DESKTOP:
+                case BluetoothClass.Device.COMPUTER_SERVER:
+                case BluetoothClass.Device.COMPUTER_LAPTOP:
+                case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
+                case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
+                case BluetoothClass.Device.COMPUTER_WEARABLE:
+                case BluetoothClass.Device.PHONE_UNCATEGORIZED:
+                case BluetoothClass.Device.PHONE_CELLULAR:
+                case BluetoothClass.Device.PHONE_CORDLESS:
+                case BluetoothClass.Device.PHONE_SMART:
+                case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
+                case BluetoothClass.Device.PHONE_ISDN:
+                    return true;
+                default:
+                    return false;
+            }
+        } else {
+            return false;
+        }
+    }
 }
 
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 27b2849..0a71961 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -30,41 +30,72 @@
 /**
  * Represents a remote Bluetooth device.
  *
- * TODO: unhide
- * @hide
+ * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link
+ * BluetoothDevice}.
+ *
+ * <p>This class is really just a thin wrapper for a Bluetooth hardware
+ * address. Objects of this class are immutable. Operations on this class
+ * are performed on the remote Bluetooth hardware address, using the
+ * {@link BluetoothAdapter} that was used to create this {@link
+ * BluetoothDevice}.
+ *
+ * TODO: unhide more of this class
  */
 public final class BluetoothDevice implements Parcelable {
     private static final String TAG = "BluetoothDevice";
 
     /** We do not have a link key for the remote device, and are therefore not
-     * bonded */
+     * bonded
+     * @hide*/
     public static final int BOND_NOT_BONDED = 0;
-    /** We have a link key for the remote device, and are probably bonded. */
+    /** We have a link key for the remote device, and are probably bonded.
+     *  @hide */
     public static final int BOND_BONDED = 1;
-    /** We are currently attempting bonding */
+    /** We are currently attempting bonding
+     *  @hide */
     public static final int BOND_BONDING = 2;
 
+    /** Ask device picker to show all kinds of BT devices.
+     *  @hide */
+    public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0;
+    /** Ask device picker to show BT devices that support AUDIO profiles.
+     *  @hide */
+    public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1;
+    /** Ask device picker to show BT devices that support Object Transfer.
+     *  @hide */
+    public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2;
+
     //TODO: Unify these result codes in BluetoothResult or BluetoothError
     /** A bond attempt failed because pins did not match, or remote device did
-     * not respond to pin request in time */
+     * not respond to pin request in time 
+     * @hide */
     public static final int UNBOND_REASON_AUTH_FAILED = 1;
     /** A bond attempt failed because the other side explicilty rejected
-     * bonding */
+     * bonding
+     * @hide */
     public static final int UNBOND_REASON_AUTH_REJECTED = 2;
-    /** A bond attempt failed because we canceled the bonding process */
+    /** A bond attempt failed because we canceled the bonding process
+     * @hide */
     public static final int UNBOND_REASON_AUTH_CANCELED = 3;
-    /** A bond attempt failed because we could not contact the remote device */
+    /** A bond attempt failed because we could not contact the remote device
+     * @hide */
     public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
-    /** A bond attempt failed because a discovery is in progress */
+    /** A bond attempt failed because a discovery is in progress
+     * @hide */
     public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
-    /** An existing bond was explicitly revoked */
+    /** An existing bond was explicitly revoked
+     * @hide */
     public static final int UNBOND_REASON_REMOVED = 6;
 
-    /* The user will be prompted to enter a pin */
+    //TODO: Remove duplicates between here and BluetoothAdapter
+    /** The user will be prompted to enter a pin
+     * @hide */
     public static final int PAIRING_VARIANT_PIN = 0;
-    /* The user will be prompted to enter a passkey */
+    /** The user will be prompted to enter a passkey
+     * @hide */
     public static final int PAIRING_VARIANT_PASSKEY = 1;
-    /* The user will be prompted to confirm the passkey displayed on the screen */
+    /** The user will be prompted to confirm the passkey displayed on the screen
+     * @hide */
     public static final int PAIRING_VARIANT_CONFIRMATION = 2;
 
     private static final int ADDRESS_LENGTH = 17;
@@ -113,15 +144,25 @@
         return mAddress.hashCode();
     }
 
+    /**
+     * Returns a string representation of this BluetoothDevice.
+     * <p>Currently this is the Bluetooth hardware address, for example
+     * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
+     * if you explicitly require the Bluetooth hardware address in case the
+     * {@link #toString} representation changes in the future.
+     * @return string representation of this BluetoothDevice
+     */
     @Override
     public String toString() {
         return mAddress;
     }
 
+    /** @hide */
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<BluetoothDevice> CREATOR =
             new Parcelable.Creator<BluetoothDevice>() {
         public BluetoothDevice createFromParcel(Parcel in) {
@@ -132,21 +173,29 @@
         }
     };
 
+    /** @hide */
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(mAddress);
     }
 
+    /**
+     * Returns the hardware address of this BluetoothDevice.
+     * <p> For example, "00:11:22:AA:BB:CC".
+     * @return Bluetooth hardware address as string
+     */
     public String getAddress() {
         return mAddress;
     }
 
     /**
-     * Get the friendly Bluetooth name of this remote device.
+     * Get the friendly Bluetooth name of the remote device.
      *
-     * This name is visible to remote Bluetooth devices. Currently it is only
-     * possible to retrieve the Bluetooth name when Bluetooth is enabled.
+     * <p>The local adapter will automatically retrieve remote names when
+     * performing a device scan, and will cache them. This method just returns
+     * the name for this device from the cache.
      *
      * @return the Bluetooth name, or null if there was a problem.
+     * @hide
      */
     public String getName() {
         try {
@@ -164,6 +213,7 @@
      * @param address the remote device Bluetooth address.
      * @return false If there was an immediate problem creating the bonding,
      *         true otherwise.
+     * @hide
      */
     public boolean createBond() {
         try {
@@ -174,6 +224,7 @@
 
     /**
      * Cancel an in-progress bonding request started with createBond.
+     * @hide
      */
     public boolean cancelBondProcess() {
         try {
@@ -188,6 +239,7 @@
      *
      * @return true if the device was disconnected, false otherwise and on
      *         error.
+     * @hide
      */
     public boolean removeBond() {
         try {
@@ -205,6 +257,7 @@
      *
      * @param address Bluetooth hardware address of the remote device to check.
      * @return Result code
+     * @hide
      */
     public int getBondState() {
         try {
@@ -213,6 +266,7 @@
         return BluetoothError.ERROR_IPC;
     }
 
+    /** @hide */
     public int getBluetoothClass() {
         try {
             return sService.getRemoteClass(mAddress);
@@ -220,6 +274,7 @@
         return BluetoothError.ERROR_IPC;
     }
 
+    /** @hide */
      public String[] getUuids() {
         try {
             return sService.getRemoteUuids(mAddress);
@@ -227,6 +282,7 @@
         return null;
     }
 
+    /** @hide */
     public int getServiceChannel(String uuid) {
          try {
              return sService.getRemoteServiceChannel(mAddress, uuid);
@@ -234,6 +290,7 @@
          return BluetoothError.ERROR_IPC;
     }
 
+    /** @hide */
     public boolean setPin(byte[] pin) {
         try {
             return sService.setPin(mAddress, pin);
@@ -241,6 +298,7 @@
         return false;
     }
 
+    /** @hide */
     public boolean setPasskey(int passkey) {
         try {
             return sService.setPasskey(mAddress, passkey);
@@ -248,6 +306,7 @@
         return false;
     }
 
+    /** @hide */
     public boolean setPairingConfirmation(boolean confirm) {
         try {
             return sService.setPairingConfirmation(mAddress, confirm);
@@ -255,6 +314,7 @@
         return false;
     }
 
+    /** @hide */
     public boolean cancelPairingUserInput() {
         try {
             return sService.cancelPairingUserInput(mAddress);
@@ -263,17 +323,20 @@
     }
 
     /**
-     * Construct a secure RFCOMM socket ready to start an outgoing connection.
-     * Call #connect on the returned #BluetoothSocket to begin the connection.
-     * The remote device will be authenticated and communication on this socket
-     * will be encrypted.
-     * @param port    remote port
-     * @return an RFCOMM BluetoothSocket
+     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
+     * outgoing connection to this remote device.
+     * <p>The remote device will be authenticated and communication on this
+     * socket will be encrypted.
+     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+     * connection.
+     * <p>Valid RFCOMM channels are in range 1 to 30.
+     * @param channel RFCOMM channel to connect to
+     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
      * @throws IOException on error, for example Bluetooth not available, or
-     *                     insufficient permissions.
+     *                     insufficient permissions
      */
-    public BluetoothSocket createRfcommSocket(int port) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, port);
+    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
+        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel);
     }
 
     /**
@@ -286,6 +349,7 @@
      * @return An RFCOMM BluetoothSocket
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient permissions.
+     * @hide
      */
     public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port);
@@ -297,6 +361,7 @@
      * @return a SCO BluetoothSocket
      * @throws IOException on error, for example Bluetooth not available, or
      *                     insufficient permissions.
+     * @hide
      */
     public BluetoothSocket createScoSocket() throws IOException {
         return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1);
@@ -309,6 +374,7 @@
      * @param pin pin as java String
      * @return the pin code as a UTF8 byte array, or null if it is an invalid
      *         Bluetooth pin.
+     * @hide
      */
     public static byte[] convertPinToBytes(String pin) {
         if (pin == null) {
@@ -327,7 +393,8 @@
         return pinBytes;
     }
 
-    /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */
+    /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0"
+     * @hide */
     public static boolean checkBluetoothAddress(String address) {
         if (address == null || address.length() != ADDRESS_LENGTH) {
             return false;
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 0e3d2bb..d31b6ae 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -356,30 +356,6 @@
         return -1;
     }
 
-    /**
-     * Check class bits for possible HSP or HFP support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might support HSP or HFP. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     * @return True if this device might support HSP or HFP.
-     */
-    public static boolean doesClassMatch(int btClass) {
-        // The render service class is required by the spec for HFP, so is a
-        // pretty good signal
-        if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
-            return true;
-        }
-        // Just in case they forgot the render service class
-        switch (BluetoothClass.Device.getDevice(btClass)) {
-        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
-        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
-        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-            return true;
-        default:
-            return false;
-        }
-    }
-
     private ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java
index 2a0de61..c39bc3d 100644
--- a/core/java/android/bluetooth/BluetoothIntent.java
+++ b/core/java/android/bluetooth/BluetoothIntent.java
@@ -62,6 +62,35 @@
     public static final String PASSKEY =
         "android.bluetooth.intent.PASSKEY";
 
+    public static final String DEVICE_PICKER_NEED_AUTH =
+        "android.bluetooth.intent.DEVICE_PICKER_NEED_AUTH";
+    public static final String DEVICE_PICKER_FILTER_TYPE =
+        "android.bluetooth.intent.DEVICE_PICKER_FILTER_TYPE";
+    public static final String DEVICE_PICKER_LAUNCH_PACKAGE =
+        "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_PACKAGE";
+    public static final String DEVICE_PICKER_LAUNCH_CLASS =
+        "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_CLASS";
+
+     /**
+     * Broadcast when one BT device is selected from BT device picker screen.
+     * Selected BT device address is contained in extra string "BluetoothIntent.ADDRESS".
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String DEVICE_PICKER_DEVICE_SELECTED =
+        "android.bluetooth.intent.action.DEVICE_SELECTED";
+
+    /**
+     * Broadcast when someone want to select one BT device from devices list.
+     * This intent contains below extra data:
+     * - BluetoothIntent.DEVICE_PICKER_NEED_AUTH (boolean): if need authentication
+     * - BluetoothIntent.DEVICE_PICKER_FILTER_TYPE (int): what kinds of device should be listed
+     * - BluetoothIntent.DEVICE_PICKER_LAUNCH_PACKAGE (string): where(which package) this intent come from
+     * - BluetoothIntent.DEVICE_PICKER_LAUNCH_CLASS (string): where(which class) this intent come from
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String DEVICE_PICKER_DEVICE_PICKER =
+        "android.bluetooth.intent.action.DEVICE_PICKER";
+
     /** Broadcast when the local Bluetooth device state changes, for example
      *  when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and
      *  BLUETOOTH_PREVIOUS_STATE. */
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 8be300b..e653c23 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -20,17 +20,31 @@
 import java.io.IOException;
 
 /**
- * Server (listening) Bluetooth Socket.
+ * A listening Bluetooth socket.
  *
- * Currently only supports RFCOMM sockets.
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. It will return a new, connected {@link BluetoothSocket} on an
+ * accepted connection. On the client side, use the same
+ * {@link BluetoothSocket} object to both intiate the outgoing connection,
+ * and to manage the connected socket.
  *
- * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
- * also known as the Serial Port Profile (SPP).
+ * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
+ * connection orientated, streaming transport over Bluetooth. It is also known
+ * as the Serial Port Profile (SPP).
  *
- * TODO: Consider exposing L2CAP sockets.
- * TODO: Clean up javadoc grammer and formatting.
- * TODO: Remove @hide
- * @hide
+ * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link
+ * BluetoothSocket} ready for an outgoing connection to a remote
+ * {@link BluetoothDevice}.
+ *
+ * <p>Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening
+ * {@link BluetoothServerSocket} ready for incoming connections to the local
+ * {@link BluetoothAdapter}.
+ *
+ * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the socket.
  */
 public final class BluetoothServerSocket implements Closeable {
     /*package*/ final BluetoothSocket mSocket;
@@ -51,11 +65,13 @@
 
     /**
      * Block until a connection is established.
-     * Returns a connected #BluetoothSocket. This server socket can be reused
-     * for subsequent incoming connections by calling #accept repeatedly.
-     * #close can be used to abort this call from another thread.
-     * @return A connected #BluetoothSocket
-     * @throws IOException On error, for example this call was aborted
+     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+     * <p>Once this call returns, it can be called again to accept subsequent
+     * incoming connections.
+     * <p>{@link #close} can be used to abort this call from another thread.
+     * @return a connected {@link BluetoothSocket}
+     * @throws IOException on error, for example this call was aborted, or
+     *                     timeout
      */
     public BluetoothSocket accept() throws IOException {
         return accept(-1);
@@ -63,11 +79,12 @@
 
     /**
      * Block until a connection is established, with timeout.
-     * Returns a connected #BluetoothSocket. This server socket can be reused
-     * for subsequent incoming connections by calling #accept repeatedly.
-     * #close can be used to abort this call from another thread.
-     * @return A connected #BluetoothSocket
-     * @throws IOException On error, for example this call was aborted, or
+     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+     * <p>Once this call returns, it can be called again to accept subsequent
+     * incoming connections.
+     * <p>{@link #close} can be used to abort this call from another thread.
+     * @return a connected {@link BluetoothSocket}
+     * @throws IOException on error, for example this call was aborted, or
      *                     timeout
      */
     public BluetoothSocket accept(int timeout) throws IOException {
@@ -75,8 +92,8 @@
     }
 
     /**
-     * Closes this socket.
-     * This will cause other blocking calls on this socket to immediately
+     * Immediately close this socket, and release all associated resources.
+     * <p>Causes blocked calls on this socket in other threads to immediately
      * throw an IOException.
      */
     public void close() throws IOException {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index dda2cef..eae0f37 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -22,20 +22,34 @@
 import java.io.OutputStream;
 
 /**
- * Represents a connected or connecting Bluetooth Socket.
+ * A connected or connecting Bluetooth socket.
  *
- * Currently only supports RFCOMM sockets.
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. It will return a new, connected {@link BluetoothSocket} on an
+ * accepted connection. On the client side, use the same
+ * {@link BluetoothSocket} object to both intiate the outgoing connection,
+ * and to manage the connected socket.
  *
- * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
- * also known as the Serial Port Profile (SPP).
+ * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
+ * connection orientated, streaming transport over Bluetooth. It is also known
+ * as the Serial Port Profile (SPP).
  *
- * TODO: Consider exposing L2CAP sockets.
- * TODO: Clean up javadoc grammer and formatting.
- * TODO: Remove @hide
- * @hide
+ * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link
+ * BluetoothSocket} ready for an outgoing connection to a remote
+ * {@link BluetoothDevice}.
+ *
+ * <p>Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening
+ * {@link BluetoothServerSocket} ready for incoming connections to the local
+ * {@link BluetoothAdapter}.
+ *
+ * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the socket.
  */
 public final class BluetoothSocket implements Closeable {
-    /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */
+    /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
     /*package*/ static final int TYPE_RFCOMM = 1;
     /*package*/ static final int TYPE_SCO = 2;
     /*package*/ static final int TYPE_L2CAP = 3;
@@ -99,6 +113,7 @@
         this(type, fd, auth, encrypt, new BluetoothDevice(address), port);
     }
 
+    /** @hide */
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -110,19 +125,19 @@
 
     /**
      * Attempt to connect to a remote device.
-     * This method will block until a connection is made or the connection
+     * <p>This method will block until a connection is made or the connection
      * fails. If this method returns without an exception then this socket
-     * is now connected. #close can be used to abort this call from another
-     * thread.
-     * @throws IOException On error, for example connection failure
+     * is now connected.
+     * <p>{@link #close} can be used to abort this call from another thread.
+     * @throws IOException on error, for example connection failure
      */
     public void connect() throws IOException {
         connectNative();
     }
 
     /**
-     * Closes this socket.
-     * This will cause other blocking calls on this socket to immediately
+     * Immediately close this socket, and release all associated resources.
+     * <p>Causes blocked calls on this socket in other threads to immediately
      * throw an IOException.
      */
     public void close() throws IOException {
@@ -130,9 +145,8 @@
     }
 
     /**
-     * Return the remote device we are connecting, or connected, to.
-     * @return remote device, or null if this socket has not yet attempted
-     *         or established a connection.
+     * Get the remote device this socket is connecting, or connected, to.
+     * @return remote device
      */
     public BluetoothDevice getRemoteDevice() {
         return mDevice;
@@ -140,7 +154,7 @@
 
     /**
      * Get the input stream associated with this socket.
-     * The input stream will be returned even if the socket is not yet
+     * <p>The input stream will be returned even if the socket is not yet
      * connected, but operations on that stream will throw IOException until
      * the associated socket is connected.
      * @return InputStream
@@ -151,7 +165,7 @@
 
     /**
      * Get the output stream associated with this socket.
-     * The output stream will be returned even if the socket is not yet
+     * <p>The output stream will be returned even if the socket is not yet
      * connected, but operations on that stream will throw IOException until
      * the associated socket is connected.
      * @return OutputStream
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8ab67c5..d86ab79 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1148,7 +1148,6 @@
      *
      * @see #getSystemService
      * @see android.bluetooth.BluetoothAdapter
-     * @hide
      */
     public static final String BLUETOOTH_SERVICE = "bluetooth";
     /**
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 93b61ec..25cbdb1 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -27,9 +27,12 @@
 public class SyncAdapterType implements Parcelable {
     public final String authority;
     public final String accountType;
-    public final boolean userVisible;
+    public final boolean isKey;
+    private final boolean userVisible;
+    private final boolean supportsUploading;
 
-    public SyncAdapterType(String authority, String accountType, boolean userVisible) {
+    public SyncAdapterType(String authority, String accountType, boolean userVisible, 
+            boolean supportsUploading) {
         if (TextUtils.isEmpty(authority)) {
             throw new IllegalArgumentException("the authority must not be empty: " + authority);
         }
@@ -39,17 +42,49 @@
         this.authority = authority;
         this.accountType = accountType;
         this.userVisible = userVisible;
+        this.supportsUploading = supportsUploading;
+        this.isKey = false;
+    }
+
+    private SyncAdapterType(String authority, String accountType) {
+        if (TextUtils.isEmpty(authority)) {
+            throw new IllegalArgumentException("the authority must not be empty: " + authority);
+        }
+        if (TextUtils.isEmpty(accountType)) {
+            throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+        }
+        this.authority = authority;
+        this.accountType = accountType;
+        this.userVisible = true;
+        this.supportsUploading = true;
+        this.isKey = true;
+    }
+
+    public boolean supportsUploading() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return supportsUploading;
+    }
+
+    public boolean isUserVisible() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return userVisible;
     }
 
     public static SyncAdapterType newKey(String authority, String accountType) {
-        return new SyncAdapterType(authority, accountType, true);
+        return new SyncAdapterType(authority, accountType);
     }
 
     public boolean equals(Object o) {
         if (o == this) return true;
         if (!(o instanceof SyncAdapterType)) return false;
         final SyncAdapterType other = (SyncAdapterType)o;
-        // don't include userVisible in the equality check
+        // don't include userVisible or supportsUploading in the equality check
         return authority.equals(other.authority) && accountType.equals(other.accountType);
     }
 
@@ -57,13 +92,22 @@
         int result = 17;
         result = 31 * result + authority.hashCode();
         result = 31 * result + accountType.hashCode();
-        // don't include userVisible in the hash
+        // don't include userVisible or supportsUploading  the hash
         return result;
     }
 
     public String toString() {
-        return "SyncAdapterType {name=" + authority + ", type=" + accountType
-                + ", userVisible=" + userVisible + "}";
+        if (isKey) {
+            return "SyncAdapterType Key {name=" + authority
+                    + ", type=" + accountType
+                    + "}";
+        } else {
+            return "SyncAdapterType {name=" + authority
+                    + ", type=" + accountType
+                    + ", userVisible=" + userVisible
+                    + ", supportsUploading=" + supportsUploading
+                    + "}";
+        }
     }
 
     public int describeContents() {
@@ -71,13 +115,22 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        if (isKey) {
+            throw new IllegalStateException("keys aren't parcelable");
+        }
+
         dest.writeString(authority);
         dest.writeString(accountType);
         dest.writeInt(userVisible ? 1 : 0);
+        dest.writeInt(supportsUploading ? 1 : 0);
     }
 
     public SyncAdapterType(Parcel source) {
-        this(source.readString(), source.readString(), source.readInt() != 0);
+        this(
+                source.readString(),
+                source.readString(),
+                source.readInt() != 0,
+                source.readInt() != 0);
     }
 
     public static final Creator<SyncAdapterType> CREATOR = new Creator<SyncAdapterType>() {
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index c27fd25..7d9f1de 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -49,7 +49,10 @@
             }
             final boolean userVisible =
                     sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_userVisible, true);
-            return new SyncAdapterType(authority, accountType, userVisible);
+            final boolean supportsUploading =
+                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_supportsUploading,
+                            true);
+            return new SyncAdapterType(authority, accountType, userVisible, supportsUploading);
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 34efc51..82cf23f 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -526,13 +526,6 @@
     public void scheduleSync(Account requestedAccount, String requestedAuthority,
             Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) {
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-        if (isLoggable) {
-            Log.v(TAG, "scheduleSync:"
-                    + " delay " + delay
-                    + ", account " + requestedAccount
-                    + ", authority " + requestedAuthority
-                    + ", extras " + ((extras == null) ? "(null)" : extras));
-        }
 
         if (!isSyncEnabled()) {
             if (isLoggable) {
@@ -617,13 +610,27 @@
                 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
                     continue;
                 }
-                if (mSyncAdapters.getServiceInfo(SyncAdapterType.newKey(authority, account.type))
-                        != null) {
+                final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+                        mSyncAdapters.getServiceInfo(
+                                SyncAdapterType.newKey(authority, account.type));
+                if (syncAdapterInfo != null) {
+                    if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
+                        continue;
+                    }
                     // make this an initialization sync if the isSyncable state is unknown
-                    Bundle extrasCopy = new Bundle(extras);
+                    Bundle extrasCopy = extras;
                     if (isSyncable < 0) {
+                        extrasCopy = new Bundle(extras);
                         extrasCopy.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
                     }
+                    if (isLoggable) {
+                        Log.v(TAG, "scheduleSync:"
+                                + " delay " + delay
+                                + ", source " + source
+                                + ", account " + account
+                                + ", authority " + authority
+                                + ", extras " + extrasCopy);
+                    }
                     scheduleSyncOperation(
                             new SyncOperation(account, source, authority, extrasCopy, delay));
                 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6ee92ce..1f640ea 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1992,8 +1992,9 @@
             req.flags = InputConnection.GET_TEXT_WITH_STYLES;
             req.hintMaxLines = 10;
             req.hintMaxChars = 10000;
-            mExtractedText = getCurrentInputConnection().getExtractedText(req,
-                    InputConnection.GET_EXTRACTED_TEXT_MONITOR);
+            InputConnection ic = getCurrentInputConnection();
+            mExtractedText = ic == null? null
+                    : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
             
             final EditorInfo ei = getCurrentInputEditorInfo();
             
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9e966cd..a141a2a 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1037,6 +1037,10 @@
                         me.getX(), me.getY(), me.getMetaState());
                 result = onModifiedTouchEvent(down);
                 down.recycle();
+                // If it's an up action, then deliver the up as well.
+                if (me.getAction() == MotionEvent.ACTION_UP) {
+                    result = onModifiedTouchEvent(me);
+                }
             } else {
                 // Send an up event for the last pointer
                 MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
@@ -1067,7 +1071,7 @@
         if (mGestureDetector.onTouchEvent(me)) {
             showPreview(NOT_A_KEY);
             mHandler.removeMessages(MSG_REPEAT);
-            mHandler.removeMessages(MSG_LONGPRESS);            
+            mHandler.removeMessages(MSG_LONGPRESS);
             return true;
         }
         
@@ -1116,7 +1120,7 @@
                         if (keyIndex == mCurrentKey) {
                             mCurrentKeyTime += eventTime - mLastMoveTime;
                             continueLongPress = true;
-                        } else {
+                        } else if (mRepeatKeyIndex == NOT_A_KEY) {
                             resetMultiTap();
                             mLastKey = mCurrentKey;
                             mLastCodeX = mLastX;
@@ -1127,10 +1131,6 @@
                             mCurrentKeyTime = 0;
                         }
                     }
-                    if (keyIndex != mRepeatKeyIndex) {
-                        mHandler.removeMessages(MSG_REPEAT);
-                        mRepeatKeyIndex = NOT_A_KEY;
-                    }
                 }
                 if (!continueLongPress) {
                     // Cancel old longpress
@@ -1141,7 +1141,7 @@
                         mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
                     }
                 }
-                showPreview(keyIndex);
+                showPreview(mCurrentKey);
                 break;
 
             case MotionEvent.ACTION_UP:
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 85ee1ca..f9effa2 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -143,8 +143,9 @@
                     boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
                             false);
                     if (DBG) Log.d(TAG, mApnType + " Received " + intent.getAction() +
-                            " broadcast - state = " + state + ", unavailable = " + unavailable +
-                            ", reason = " + (reason == null ? "(unspecified)" : reason));
+                            " broadcast - state = " + state + ", oldstate = " + mMobileDataState +
+                            ", unavailable = " + unavailable + ", reason = " +
+                            (reason == null ? "(unspecified)" : reason));
 
                     if (isApnTypeIncluded(apnTypeList)) {
                         if (mEnabled == false) {
@@ -152,10 +153,12 @@
                             // we should record the interface name if one's provided.  If the user
                             // turns on this network we will need the interfacename but won't get
                             // a fresh connected message - TODO fix this..
-                            if (mInterfaceName == null && state == Phone.DataState.CONNECTED) {
+                            if (state == Phone.DataState.CONNECTED) {
+                                if (DBG) Log.d(TAG, "replacing old mInterfaceName (" +
+                                        mInterfaceName + ") with " +
+                                        intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY) +
+                                        " for " + mApnType);
                                 mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
-                            } else if (state == Phone.DataState.DISCONNECTED) {
-                                mInterfaceName = null;
                             }
                             if (DBG) Log.d(TAG, "  dropped - mEnabled = false");
                             return;
@@ -179,6 +182,8 @@
                                 if (mInterfaceName != null) {
                                     NetworkUtils.resetConnections(mInterfaceName);
                                 }
+                                if (DBG) Log.d(TAG, "clearing mInterfaceName for "+ mApnType +
+                                        " as it DISCONNECTED");
                                 mInterfaceName = null;
                                 mDefaultGatewayAddr = 0;
                                 break;
@@ -301,6 +306,8 @@
         switch (setEnableApn(mApnType, true)) {
             case Phone.APN_ALREADY_ACTIVE:
                 mEnabled = true;
+                // need to set self to CONNECTING so the below message is handled.
+                mMobileDataState = Phone.DataState.CONNECTING;
                 //send out a connected message
                 Intent intent = new Intent(TelephonyIntents.
                         ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
@@ -316,6 +323,12 @@
                 // no need to do anything - we're already due some status update intents
                 break;
             case Phone.APN_REQUEST_FAILED:
+                if (mPhoneService == null && mApnType == Phone.APN_TYPE_DEFAULT) {
+                    // on startup we may try to talk to the phone before it's ready
+                    // just leave mEnabled as it is for the default apn.
+                    return false;
+                }
+                // else fall through
             case Phone.APN_TYPE_NOT_AVAILABLE:
                 mEnabled = false;
                 break;
@@ -406,10 +419,11 @@
      */
     @Override
     public boolean requestRouteToHost(int hostAddress) {
+        if (DBG) {
+            Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
+                    " for " + mApnType + "(" + mInterfaceName + ")");
+        }
         if (mInterfaceName != null && hostAddress != -1) {
-            if (DBG) {
-                Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress));
-            }
             return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0;
         } else {
             return false;
diff --git a/core/java/android/pim/ContactsAsyncHelper.java b/core/java/android/pim/ContactsAsyncHelper.java
index a21281e..342d208 100644
--- a/core/java/android/pim/ContactsAsyncHelper.java
+++ b/core/java/android/pim/ContactsAsyncHelper.java
@@ -27,8 +27,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
-import android.provider.Contacts;
-import android.provider.Contacts.People;
+import android.provider.ContactsContract.Contacts;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -39,35 +38,35 @@
  * Helper class for async access of images.
  */
 public class ContactsAsyncHelper extends Handler {
-    
+
     private static final boolean DBG = false;
     private static final String LOG_TAG = "ContactsAsyncHelper";
-    
+
     /**
      * Interface for a WorkerHandler result return.
      */
     public interface OnImageLoadCompleteListener {
         /**
          * Called when the image load is complete.
-         * 
+         *
          * @param imagePresent true if an image was found
-         */  
+         */
         public void onImageLoadComplete(int token, Object cookie, ImageView iView,
                 boolean imagePresent);
     }
-     
+
     // constants
     private static final int EVENT_LOAD_IMAGE = 1;
     private static final int DEFAULT_TOKEN = -1;
-    
+
     // static objects
     private static Handler sThreadHandler;
     private static ContactsAsyncHelper sInstance;
-    
+
     static {
         sInstance = new ContactsAsyncHelper();
     }
-    
+
     private static final class WorkerArgs {
         public Context context;
         public ImageView view;
@@ -78,12 +77,12 @@
         public OnImageLoadCompleteListener listener;
         public CallerInfo info;
     }
-    
+
     /**
-     * public inner class to help out the ContactsAsyncHelper callers 
-     * with tracking the state of the CallerInfo Queries and image 
+     * public inner class to help out the ContactsAsyncHelper callers
+     * with tracking the state of the CallerInfo Queries and image
      * loading.
-     * 
+     *
      * Logic contained herein is used to remove the race conditions
      * that exist as the CallerInfo queries run and mix with the image
      * loads, which then mix with the Phone state changes.
@@ -94,11 +93,11 @@
         public static final int DISPLAY_UNDEFINED = 0;
         public static final int DISPLAY_IMAGE = -1;
         public static final int DISPLAY_DEFAULT = -2;
-        
+
         // State of the image on the imageview.
         private CallerInfo mCurrentCallerInfo;
         private int displayMode;
-        
+
         public ImageTracker() {
             mCurrentCallerInfo = null;
             displayMode = DISPLAY_UNDEFINED;
@@ -107,17 +106,17 @@
         /**
          * Used to see if the requested call / connection has a
          * different caller attached to it than the one we currently
-         * have in the CallCard. 
+         * have in the CallCard.
          */
         public boolean isDifferentImageRequest(CallerInfo ci) {
             // note, since the connections are around for the lifetime of the
-            // call, and the CallerInfo-related items as well, we can 
+            // call, and the CallerInfo-related items as well, we can
             // definitely use a simple != comparison.
             return (mCurrentCallerInfo != ci);
         }
-        
+
         public boolean isDifferentImageRequest(Connection connection) {
-            // if the connection does not exist, see if the 
+            // if the connection does not exist, see if the
             // mCurrentCallerInfo is also null to match.
             if (connection == null) {
                 if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null");
@@ -133,57 +132,58 @@
             }
             return runQuery;
         }
-        
+
         /**
-         * Simple setter for the CallerInfo object. 
+         * Simple setter for the CallerInfo object.
          */
         public void setPhotoRequest(CallerInfo ci) {
-            mCurrentCallerInfo = ci; 
+            mCurrentCallerInfo = ci;
         }
-        
+
         /**
-         * Convenience method used to retrieve the URI 
-         * representing the Photo file recorded in the attached 
-         * CallerInfo Object. 
+         * Convenience method used to retrieve the URI
+         * representing the Photo file recorded in the attached
+         * CallerInfo Object.
          */
         public Uri getPhotoUri() {
             if (mCurrentCallerInfo != null) {
-                return ContentUris.withAppendedId(People.CONTENT_URI, 
+                return ContentUris.withAppendedId(Contacts.CONTENT_URI,
                         mCurrentCallerInfo.person_id);
             }
-            return null; 
+            return null;
         }
-        
+
         /**
-         * Simple setter for the Photo state. 
+         * Simple setter for the Photo state.
          */
         public void setPhotoState(int state) {
             displayMode = state;
         }
-        
+
         /**
-         * Simple getter for the Photo state. 
+         * Simple getter for the Photo state.
          */
         public int getPhotoState() {
             return displayMode;
         }
     }
-    
+
     /**
-     * Thread worker class that handles the task of opening the stream and loading 
+     * Thread worker class that handles the task of opening the stream and loading
      * the images.
      */
     private class WorkerHandler extends Handler {
         public WorkerHandler(Looper looper) {
             super(looper);
         }
-        
+
+        @Override
         public void handleMessage(Message msg) {
             WorkerArgs args = (WorkerArgs) msg.obj;
-            
+
             switch (msg.arg1) {
                 case EVENT_LOAD_IMAGE:
-                    InputStream inputStream = Contacts.People.openContactPhotoInputStream(
+                    InputStream inputStream = Contacts.openContactPhotoInputStream(
                             args.context.getContentResolver(), args.uri);
                     if (inputStream != null) {
                         args.result = Drawable.createFromStream(inputStream, args.uri.toString());
@@ -192,22 +192,22 @@
                                 " token: " + msg.what + " image URI: " + args.uri);
                     } else {
                         args.result = null;
-                        if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 + 
-                                " token: " + msg.what + " image URI: " + args.uri + 
+                        if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
+                                " token: " + msg.what + " image URI: " + args.uri +
                                 ", using default image.");
                     }
                     break;
                 default:
             }
-            
-            // send the reply to the enclosing class. 
+
+            // send the reply to the enclosing class.
             Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
             reply.arg1 = msg.arg1;
             reply.obj = msg.obj;
             reply.sendToTarget();
         }
     }
-    
+
     /**
      * Private constructor for static class
      */
@@ -216,14 +216,14 @@
         thread.start();
         sThreadHandler = new WorkerHandler(thread.getLooper());
     }
-    
+
     /**
      * Convenience method for calls that do not want to deal with listeners and tokens.
      */
-    public static final void updateImageViewWithContactPhotoAsync(Context context, 
+    public static final void updateImageViewWithContactPhotoAsync(Context context,
             ImageView imageView, Uri person, int placeholderImageResource) {
         // Added additional Cookie field in the callee.
-        updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context, 
+        updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
                 imageView, person, placeholderImageResource);
     }
 
@@ -231,24 +231,24 @@
      * Convenience method for calls that do not want to deal with listeners and tokens, but have
      * a CallerInfo object to cache the image to.
      */
-    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context, 
+    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
             ImageView imageView, Uri person, int placeholderImageResource) {
         // Added additional Cookie field in the callee.
-        updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context, 
+        updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
                 imageView, person, placeholderImageResource);
     }
 
-    
+
     /**
      * Start an image load, attach the result to the specified CallerInfo object.
      * Note, when the query is started, we make the ImageView INVISIBLE if the
      * placeholderImageResource value is -1.  When we're given a valid (!= -1)
      * placeholderImageResource value, we make sure the image is visible.
      */
-    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token, 
-            OnImageLoadCompleteListener listener, Object cookie, Context context, 
+    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
+            OnImageLoadCompleteListener listener, Object cookie, Context context,
             ImageView imageView, Uri person, int placeholderImageResource) {
-        
+
         // in case the source caller info is null, the URI will be null as well.
         // just update using the placeholder image in this case.
         if (person == null) {
@@ -257,10 +257,10 @@
             imageView.setImageResource(placeholderImageResource);
             return;
         }
-        
+
         // Added additional Cookie field in the callee to handle arguments
         // sent to the callback function.
-        
+
         // setup arguments
         WorkerArgs args = new WorkerArgs();
         args.cookie = cookie;
@@ -270,15 +270,15 @@
         args.defaultResource = placeholderImageResource;
         args.listener = listener;
         args.info = info;
-        
+
         // setup message arguments
         Message msg = sThreadHandler.obtainMessage(token);
         msg.arg1 = EVENT_LOAD_IMAGE;
         msg.obj = args;
-        
-        if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri + 
+
+        if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
                 ", displaying default image for now.");
-        
+
         // set the default image first, when the query is complete, we will
         // replace the image with the correct one.
         if (placeholderImageResource != -1) {
@@ -287,11 +287,11 @@
         } else {
             imageView.setVisibility(View.INVISIBLE);
         }
-        
+
         // notify the thread to begin working
         sThreadHandler.sendMessage(msg);
     }
-    
+
     /**
      * Called when loading is done.
      */
@@ -316,21 +316,21 @@
                     args.view.setVisibility(View.VISIBLE);
                     args.view.setImageResource(args.defaultResource);
                 }
-                
+
                 // Note that the data is cached.
                 if (args.info != null) {
                     args.info.isCachedPhotoCurrent = true;
                 }
-                
+
                 // notify the listener if it is there.
                 if (args.listener != null) {
-                    if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() + 
+                    if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
                             " image: " + args.uri + " completed");
                     args.listener.onImageLoadComplete(msg.what, args.cookie, args.view,
                             imagePresent);
                 }
                 break;
-            default:    
+            default:
         }
     }
 }
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index cf5664c..f16a7e4 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -149,14 +149,12 @@
      * @param checked The checked state.
      */
     public void setChecked(boolean checked) {
-
-        mChecked = checked;
-
-        persistBoolean(checked);
-
-        notifyDependencyChange(shouldDisableDependents());
-        
-        notifyChanged();
+        if (mChecked != checked) {
+            mChecked = checked;
+            persistBoolean(checked);
+            notifyDependencyChange(shouldDisableDependents());
+            notifyChanged();
+        }
     }
 
     /**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 09d0327..18b666c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -19,12 +19,20 @@
 import android.accounts.Account;
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.database.Cursor;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.text.TextUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 
 /**
  * The contract between the contacts provider and applications. Contains definitions
@@ -296,17 +304,65 @@
              * The directory twig for this sub-table
              */
             public static final String CONTENT_DIRECTORY = "suggestions";
+        }
 
-            /**
-             * An optional query parameter that can be supplied to limit the number of returned
-             * suggestions.
-             * <p>
-             * Type: INTEGER
-             *
-             * @deprecated Please use the "limit" parameter
-             */
-            @Deprecated
-            public static final String MAX_SUGGESTIONS = "max_suggestions";
+        /**
+         * Returns a URI that can be used to retrieve the contact's default photo.
+         *
+         * @param contactUri the contact whose photo should be used
+         */
+        public static Uri getPhotoUri(ContentResolver cr, Uri contactUri) {
+
+            // TODO remove try/catch block as soon as eclair-dev is merged in eclair
+            try {
+                long photoId = -1;
+                Cursor cursor = cr.query(contactUri, new String[] {Contacts.PHOTO_ID},
+                        null, null, null);
+                try {
+                    if (!cursor.moveToNext()) {
+                        return null;
+                    }
+
+                    if (cursor.isNull(0)) {
+                        return null;
+                    }
+
+                    photoId = cursor.getLong(0);
+                } finally {
+                    cursor.close();
+                }
+
+                return ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, photoId);
+            } catch (Exception e) {
+                return null;
+            }
+        }
+
+        /**
+         * Opens an InputStream for the person's default photo and returns the
+         * photo as a Bitmap stream.
+         *
+         * @param contactUri the contact whose photo should be used
+         */
+        public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) {
+            Uri photoUri = getPhotoUri(cr, contactUri);
+            if (photoUri == null) {
+                return null;
+            }
+            Cursor cursor = cr.query(photoUri,
+                    new String[]{ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);
+            try {
+                if (!cursor.moveToNext()) {
+                    return null;
+                }
+                byte[] data = cursor.getBlob(0);
+                if (data == null) {
+                    return null;
+                }
+                return new ByteArrayInputStream(data);
+            } finally {
+                cursor.close();
+            }
         }
     }
 
@@ -541,13 +597,33 @@
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
     }
 
+    private interface PhoneLookupColumns {
+        /**
+         * The phone number as the user entered it.
+         * <P>Type: TEXT</P>
+         */
+        public static final String NUMBER = "number";
+
+        /**
+         * The type of phone number, for example Home or Work.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+
+        /**
+         * The user defined label for the phone number.
+         * <P>Type: TEXT</P>
+         */
+        public static final String LABEL = "label";
+    }
+
     /**
      * A table that represents the result of looking up a phone number, for
-     * example for caller ID. The table joins that data row for the phone number
-     * with the raw contact that owns the number. To perform a lookup you must
-     * append the number you want to find to {@link #CONTENT_FILTER_URI}.
+     * example for caller ID. To perform a lookup you must append the number you
+     * want to find to {@link #CONTENT_FILTER_URI}.
      */
-    public static final class PhoneLookup implements BaseColumns, DataColumns, ContactsColumns {
+    public static final class PhoneLookup implements BaseColumns, PhoneLookupColumns,
+            ContactsColumns, ContactOptionsColumns {
         /**
          * This utility class cannot be instantiated
          */
@@ -881,6 +957,32 @@
              * <P>Type: TEXT</P>
              */
             public static final String NUMBER = DATA;
+
+            public static final CharSequence getDisplayLabel(Context context, int type,
+                    CharSequence label, CharSequence[] labelArray) {
+                CharSequence display = "";
+
+                if (type != Phone.TYPE_CUSTOM) {
+                    CharSequence[] labels = labelArray != null? labelArray
+                            : context.getResources().getTextArray(
+                                    com.android.internal.R.array.phoneTypes);
+                    try {
+                        display = labels[type - 1];
+                    } catch (ArrayIndexOutOfBoundsException e) {
+                        display = labels[Phone.TYPE_CUSTOM];
+                    }
+                } else {
+                    if (!TextUtils.isEmpty(label)) {
+                        display = label;
+                    }
+                }
+                return display;
+            }
+
+            public static final CharSequence getDisplayLabel(Context context, int type,
+                    CharSequence label) {
+                return getDisplayLabel(context, type, label, null);
+            }
         }
 
         /**
@@ -1520,9 +1622,8 @@
         public static final String SHOULD_SYNC = "should_sync";
 
         /**
-         * Flag indicating if the contacts from this source, but that don't have
-         * any specific {@link GroupMembership} entries should be visible in any
-         * user interface.
+         * Flag indicating if contacts without any {@link GroupMembership}
+         * entries should be visible in any user interface.
          * <p>
          * Type: INTEGER (boolean)
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1861095..84e07f0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3390,6 +3390,13 @@
                 "google_calendar_sync_window_days";
 
         /**
+         * How often to update the calendar sync window.
+         * The window will be advanced every n days.
+         */
+        public static final String GOOGLE_CALENDAR_SYNC_WINDOW_UPDATE_DAYS =
+                "google_calendar_sync_window_update_days";
+
+        /**
          * @deprecated
          * @hide
          */
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 9ec1013..5cecac3 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -246,7 +246,7 @@
     };
 
     /**
-     * Sets the display metrics used to provide canva's width/height in comaptibility mode.
+     * Sets the display metrics used to provide canva's width/height in compatibility mode.
      */
     void setCompatibleDisplayMetrics(DisplayMetrics metrics, Translator translator) {
         mCompatibleDisplayMetrics = metrics;
@@ -275,7 +275,8 @@
     public native   void clear();
     
     /** draw into a surface */
-    public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException {
+    public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException, IllegalArgumentException
+    {
         /* the dirty rectangle may be expanded to the surface's size, if
          * for instance it has been resized or if the bits were lost, since
          * the last call.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index f4e9900..ea08f33 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -344,6 +344,12 @@
     public final int TRANSIT_TASK_TO_FRONT = 10;
     /** A window in an existing task is being put below all other tasks. */
     public final int TRANSIT_TASK_TO_BACK = 11;
+    /** A window in a new activity is being opened on top of an existing one,
+     * and both are on top of the wallpaper. */
+    public final int TRANSIT_WALLPAPER_ACTIVITY_OPEN = 12;
+    /** The window in the top-most activity is being closed to reveal the
+     * previous activity, and both are on top of he wallpaper. */
+    public final int TRANSIT_WALLPAPER_ACTIVITY_CLOSE = 13;
     
     /** Screen turned off because of power button */
     public final int OFF_BECAUSE_OF_USER = 1;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index f92eb99..2a0e5e5 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -64,10 +64,10 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.SeekBar, defStyle, 0);
         Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
-        setThumb(thumb);
+        setThumb(thumb); // will guess mThumbOffset if thumb != null...
+        // ...but allow layout to override this
         int thumbOffset =
-                a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, 0);
-        setThumbOffset(thumbOffset);
+                a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
         a.recycle();
 
         a = context.obtainStyledAttributes(attrs,
@@ -77,13 +77,21 @@
     }
 
     /**
-     * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar
+     * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar.
+     * <p>
+     * If the thumb is a valid drawable (i.e. not null), half its width will be
+     * used as the new thumb offset (@see #setThumbOffset(int)).
      * 
      * @param thumb Drawable representing the thumb
      */
     public void setThumb(Drawable thumb) {
         if (thumb != null) {
             thumb.setCallback(this);
+
+            // Assuming the thumb drawable is symmetric, set the thumb offset
+            // such that the thumb will hang halfway off either edge of the
+            // progress bar.
+            mThumbOffset = (int)thumb.getIntrinsicWidth() / 2;
         }
         mThumb = thumb;
         invalidate();
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index e60ff25..549f984 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -267,12 +267,17 @@
             // Get the capabilities of the player for this stream
             Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
                                       MediaPlayer.BYPASS_METADATA_FILTER);
-            mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
-                    || data.getBoolean(Metadata.PAUSE_AVAILABLE);
-            mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
-                    || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
-            mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
-                    || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
+
+            if (data != null) {
+                mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
+                        || data.getBoolean(Metadata.PAUSE_AVAILABLE);
+                mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
+                        || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
+                mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
+                        || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
+            } else {
+                mCanPause = mCanSeekForward = mCanSeekForward = true;
+            }
 
             if (mOnPreparedListener != null) {
                 mOnPreparedListener.onPrepared(mMediaPlayer);
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
index 30349b3..70f2779 100644
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -116,7 +116,7 @@
 
     //Projection used for looking up contact id from phone number
     protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
-        RawContacts.CONTACT_ID,
+        PhoneLookup._ID,
     };
     protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2e73372..f61e247 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -525,6 +525,7 @@
     char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
     char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
     char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
+    char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char* stackTraceFile = NULL;
     bool checkJni = false;
     bool checkDexSum = false;
@@ -597,16 +598,10 @@
     mOptions.add(opt);
     //options[curOpt++].optionString = "-verbose:class";
 
-#ifdef CUSTOM_RUNTIME_HEAP_MAX
-#define __make_max_heap_opt(val) #val
-#define _make_max_heap_opt(val) "-Xmx" __make_max_heap_opt(val)
-    opt.optionString = _make_max_heap_opt(CUSTOM_RUNTIME_HEAP_MAX);
-#undef __make_max_heap_opt
-#undef _make_max_heap_opt
-#else
-    /* limit memory use to 16MB */
-    opt.optionString = "-Xmx16m";
-#endif
+    strcpy(heapsizeOptsBuf, "-Xmx");
+    property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
+    //LOGI("Heap size: %s", heapsizeOptsBuf);
+    opt.optionString = heapsizeOptsBuf;
     mOptions.add(opt);
 
     /*
diff --git a/core/res/res/anim/wallpaper_activity_close_enter.xml b/core/res/res/anim/wallpaper_activity_close_enter.xml
new file mode 100644
index 0000000..fc6e332
--- /dev/null
+++ b/core/res/res/anim/wallpaper_activity_close_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_exit.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@anim/decelerate_interpolator"
+        android:zAdjustment="top">
+    <scale android:fromXScale="2.0" android:toXScale="1.0"
+           android:fromYScale="2.0" android:toYScale="1.0"
+           android:pivotX="50%" android:pivotY="50%"
+           android:duration="@android:integer/config_mediumAnimTime" />
+	<translate android:fromXDelta="-150%" android:toXDelta="0"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_activity_close_exit.xml b/core/res/res/anim/wallpaper_activity_close_exit.xml
new file mode 100644
index 0000000..edd00fd
--- /dev/null
+++ b/core/res/res/anim/wallpaper_activity_close_exit.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_exit.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@anim/accelerate_interpolator">
+    <scale android:fromXScale="1.0" android:toXScale=".5"
+           android:fromYScale="1.0" android:toYScale=".5"
+           android:pivotX="50%" android:pivotY="50%"
+           android:duration="@android:integer/config_mediumAnimTime" />
+	<translate android:fromXDelta="0%" android:toXDelta="100%"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_activity_open_enter.xml b/core/res/res/anim/wallpaper_activity_open_enter.xml
new file mode 100644
index 0000000..5b44d97
--- /dev/null
+++ b/core/res/res/anim/wallpaper_activity_open_enter.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_exit.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@anim/decelerate_interpolator">
+    <scale android:fromXScale=".5" android:toXScale="1.0"
+           android:fromYScale=".5" android:toYScale="1.0"
+           android:pivotX="50%" android:pivotY="50%"
+           android:duration="@android:integer/config_mediumAnimTime" />
+    <translate android:fromXDelta="100%" android:toXDelta="0"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_activity_open_exit.xml b/core/res/res/anim/wallpaper_activity_open_exit.xml
new file mode 100644
index 0000000..fa39bee
--- /dev/null
+++ b/core/res/res/anim/wallpaper_activity_open_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_exit.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@anim/accelerate_interpolator"
+        android:zAdjustment="top">
+    <scale android:fromXScale="1.0" android:toXScale="2.0"
+           android:fromYScale="1.0" android:toYScale="2.0"
+           android:pivotX="50%" android:pivotY="50%"
+           android:duration="@android:integer/config_mediumAnimTime" />
+    <translate android:fromXDelta="0" android:toXDelta="-150%"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index eee87e6..729b1db 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -937,18 +937,68 @@
         <attr name="windowShowAnimation" format="reference" />
         <!-- The animation used when a window is going from VISIBLE to INVISIBLE. -->
         <attr name="windowHideAnimation" format="reference" />
+        
+        <!--  When opening a new activity, this is the animation that is
+              run on the next activity (which is entering the screen). -->
         <attr name="activityOpenEnterAnimation" format="reference" />
+        <!--  When opening a new activity, this is the animation that is
+              run on the previous activity (which is exiting the screen). -->
         <attr name="activityOpenExitAnimation" format="reference" />
+        <!--  When closing the current activity, this is the animation that is
+              run on the next activity (which is entering the screen). -->
         <attr name="activityCloseEnterAnimation" format="reference" />
+        <!--  When closing the current activity, this is the animation that is
+              run on the current activity (which is exiting the screen). -->
         <attr name="activityCloseExitAnimation" format="reference" />
+        <!--  When opening an activity in a new task, this is the animation that is
+              run on the activity of the new task (which is entering the screen). -->
         <attr name="taskOpenEnterAnimation" format="reference" />
+        <!--  When opening an activity in a new task, this is the animation that is
+              run on the activity of the old task (which is exiting the screen). -->
         <attr name="taskOpenExitAnimation" format="reference" />
+        <!--  When closing the last activity of a task, this is the animation that is
+              run on the activity of the next task (which is entering the screen). -->
         <attr name="taskCloseEnterAnimation" format="reference" />
+        <!--  When opening an activity in a new task, this is the animation that is
+              run on the activity of the old task (which is exiting the screen). -->
         <attr name="taskCloseExitAnimation" format="reference" />
+        <!--  When bringing an existing task to the foreground, this is the
+              animation that is run on the top activity of the task being brought
+              to the foreground (which is entering the screen). -->
         <attr name="taskToFrontEnterAnimation" format="reference" />
+        <!--  When bringing an existing task to the foreground, this is the
+              animation that is run on the current foreground activity
+              (which is exiting the screen). -->
         <attr name="taskToFrontExitAnimation" format="reference" />
+        <!--  When sending the current task to the background, this is the
+              animation that is run on the top activity of the task behind
+              it (which is entering the screen). -->
         <attr name="taskToBackEnterAnimation" format="reference" />
+        <!--  When sending the current task to the background, this is the
+              animation that is run on the top activity of the current task
+              (which is exiting the screen). -->
         <attr name="taskToBackExitAnimation" format="reference" />
+        
+        <!--  When opening a new activity that is on top of the wallpaper
+              when the current activity is also on top of the wallpaper,
+              this is the animation that is run on the new activity
+              (which is entering the screen). -->
+        <attr name="wallpaperActivityOpenEnterAnimation" format="reference" />
+        <!--  When opening a new activity that is on top of the wallpaper
+              when the current activity is also on top of the wallpaper,
+              this is the animation that is run on the current activity
+              (which is exiting the screen). -->
+        <attr name="wallpaperActivityOpenExitAnimation" format="reference" />
+        <!--  When closing a foreround activity that is on top of the wallpaper
+              when the previous activity is also on top of the wallpaper,
+              this is the animation that is run on the previous activity
+              (which is entering the screen). -->
+        <attr name="wallpaperActivityCloseEnterAnimation" format="reference" />
+        <!--  When closing a foreround activity that is on top of the wallpaper
+              when the previous activity is also on top of the wallpaper,
+              this is the animation that is run on the current activity
+              (which is exiting the screen). -->
+        <attr name="wallpaperActivityCloseExitAnimation" format="reference" />
     </declare-styleable>
 
     <!-- ============================= -->
@@ -3360,6 +3410,7 @@
         <attr name="contentAuthority" format="string"/>
         <attr name="accountType"/>
         <attr name="userVisible" format="boolean"/>
+        <attr name="supportsUploading" format="boolean"/>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 60b492a..4bc376b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1150,9 +1150,14 @@
   <public type="attr" name="contentAuthority" />
   <public type="attr" name="userVisible" />
   <public type="attr" name="windowShowWallpaper" />
+  <public type="attr" name="wallpaperActivityOpenEnterAnimation" />
+  <public type="attr" name="wallpaperActivityOpenExitAnimation" />
+  <public type="attr" name="wallpaperActivityCloseEnterAnimation" />
+  <public type="attr" name="wallpaperActivityCloseExitAnimation" />
 
   <public type="style" name="Theme.Wallpaper" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" />
 
+  <public type="attr" name="supportsUploading" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 22f9136..c14ecc0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1350,7 +1350,7 @@
 
     <!-- When the battery is low, this is the label of the button to go to the
          power usage activity to find out what drained the battery. -->
-    <string name="battery_low_why">Why?</string>
+    <string name="battery_low_why">Battery use</string>
 
     <!-- Title of the alert when something went wrong in the factory test. -->
     <string name="factorytest_failed">Factory test failed</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4aa4210..10d2093 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -66,6 +66,10 @@
         <item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
         <item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
         <item name="taskToBackExitAnimation">@anim/task_close_exit</item>
+        <item name="wallpaperActivityOpenEnterAnimation">@anim/wallpaper_activity_open_enter</item>
+        <item name="wallpaperActivityOpenExitAnimation">@anim/wallpaper_activity_open_exit</item>
+        <item name="wallpaperActivityCloseEnterAnimation">@anim/wallpaper_activity_close_enter</item>
+        <item name="wallpaperActivityCloseExitAnimation">@anim/wallpaper_activity_close_exit</item>
     </style>
 
     <!-- Standard animations for a non-full-screen window or activity. -->
@@ -130,8 +134,7 @@
         <item name="windowExitAnimation">@anim/slide_out_down</item>
     </style>
 
-    <!-- Window animations that are applied to input method overlay windows.
-         {@hide Pending API council approval} -->
+    <!-- Window animations that are applied to input method overlay windows. -->
     <style name="Animation.InputMethod">
         <item name="windowEnterAnimation">@anim/input_method_enter</item>
         <item name="windowExitAnimation">@anim/input_method_exit</item>
@@ -151,8 +154,7 @@
         <item name="windowExitAnimation">@anim/search_bar_exit</item>
     </style>
 
-    <!-- Window animations that are applied to the zoom buttons overlay window.
-         {@hide Pending API council approval} -->
+    <!-- Window animations that are applied to the zoom buttons overlay window. -->
     <style name="Animation.ZoomButtons">
         <item name="windowEnterAnimation">@anim/fade_in</item>
         <item name="windowExitAnimation">@anim/fade_out</item>
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 30b952d..b6b7adf 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -18,10 +18,6 @@
 
 import java.lang.reflect.Field;
 
-import android.renderscript.Element;
-import android.util.Config;
-import android.util.Log;
-
 /**
  * @hide
  *
@@ -113,9 +109,7 @@
         public void add(Dimension d, int value) {
             if(mEntries.length >= mEntryCount) {
                 Entry[] en = new Entry[mEntryCount + 8];
-                for(int ct=0; ct < mEntries.length; ct++) {
-                    en[ct] = mEntries[ct];
-                }
+                System.arraycopy(mEntries, 0, en, 0, mEntries.length);
                 mEntries = en;
             }
             mEntries[mEntryCount] = new Entry();
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
index 83c7283..435540e 100644
--- a/include/binder/MemoryHeapBase.h
+++ b/include/binder/MemoryHeapBase.h
@@ -42,7 +42,7 @@
      * maps the memory referenced by fd. but DOESN'T take ownership
      * of the filedescriptor (it makes a copy with dup()
      */
-    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0);
+    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);
     
     /*
      * maps memory from the given device
@@ -82,7 +82,7 @@
             int flags = 0, const char* device = NULL);    
 
 private:
-    status_t mapfd(int fd, size_t size);
+    status_t mapfd(int fd, size_t size, uint32_t offset = 0);
 
     int         mFD;
     size_t      mSize;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 2d5b8d8..be60565 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -28,23 +28,24 @@
 namespace android {
 
 enum {
-    kKeyMIMEType         = 'mime',
-    kKeyWidth            = 'widt',
-    kKeyHeight           = 'heig',
-    kKeyChannelCount     = '#chn',
-    kKeySampleRate       = 'srte',
-    kKeyBitRate          = 'brte',
-    kKeyESDS             = 'esds',
-    kKeyAVCC             = 'avcc',
-    kKeyTimeUnits        = '#tim',
-    kKeyTimeScale        = 'scal',
-    kKeyNeedsNALFraming  = 'NALf',
-    kKeyIsSyncFrame      = 'sync',
-    kKeyDuration         = 'dura',
-    kKeyColorFormat      = 'colf',
-    kKeyPlatformPrivate  = 'priv',
-    kKeyDecoderComponent = 'decC',
-    kKeyBufferID         = 'bfID',
+    kKeyMIMEType          = 'mime',
+    kKeyWidth             = 'widt',
+    kKeyHeight            = 'heig',
+    kKeyChannelCount      = '#chn',
+    kKeySampleRate        = 'srte',
+    kKeyBitRate           = 'brte',
+    kKeyESDS              = 'esds',
+    kKeyAVCC              = 'avcc',
+    kKeyTimeUnits         = '#tim',
+    kKeyTimeScale         = 'scal',
+    kKeyWantsNALFragments = 'NALf',
+    kKeyIsSyncFrame       = 'sync',
+    kKeyDuration          = 'dura',
+    kKeyColorFormat       = 'colf',
+    kKeyPlatformPrivate   = 'priv',
+    kKeyDecoderComponent  = 'decC',
+    kKeyBufferID          = 'bfID',
+    kKeyCompressedSize    = 'cmpS',
 };
 
 enum {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index d4ae349..0b94118 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -79,7 +79,7 @@
 
     enum Quirks {
         kNeedsFlushBeforeDisable             = 1,
-        kWantsRawNALFrames                   = 2,
+        kWantsNALFragments                   = 2,
         kRequiresLoadedToIdleAfterAllocation = 4,
         kRequiresAllocateBufferOnInputPorts  = 8,
     };
@@ -108,7 +108,7 @@
     Vector<CodecSpecificData *> mCodecSpecificData;
     size_t mCodecSpecificDataIndex;
 
-    sp<MemoryDealer> mDealer;
+    sp<MemoryDealer> mDealer[2];
 
     State mState;
     Vector<BufferInfo> mPortBuffers[2];
@@ -148,6 +148,9 @@
     void setImageOutputFormat(
             OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height);
 
+    void setJPEGInputFormat(
+            OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize);
+
     status_t allocateBuffers();
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
 
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
index 0abc5a6..99d803a 100644
--- a/include/media/stagefright/OMXDecoder.h
+++ b/include/media/stagefright/OMXDecoder.h
@@ -76,7 +76,7 @@
     };
 
     enum Quirks {
-        kWantsRawNALFrames                   = 1,
+        kWantsNALFragments                   = 1,
         kDoesntReturnBuffersOnDisable        = 2,
         kDoesntFlushOnExecutingToIdle        = 4,
         kDoesntProperlyFlushAllPortsAtOnce   = 8,
diff --git a/include/ui/ISurfaceFlingerClient.h b/include/ui/ISurfaceFlingerClient.h
index 932a70a..5d231e6d 100644
--- a/include/ui/ISurfaceFlingerClient.h
+++ b/include/ui/ISurfaceFlingerClient.h
@@ -52,6 +52,9 @@
     struct surface_data_t {
         int32_t             token;
         int32_t             identity;
+        uint32_t            width;
+        uint32_t            height;
+        uint32_t            format;
         status_t readFromParcel(const Parcel& parcel);
         status_t writeToParcel(Parcel* parcel) const;
     };
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index ac38f51..5df078f 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -78,13 +78,13 @@
     }
 }
 
-MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags)
+MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
     : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
       mDevice(0), mNeedUnmap(false)
 {
     const size_t pagesize = getpagesize();
     size = ((size + pagesize-1) & ~(pagesize-1));
-    mapfd(dup(fd), size);
+    mapfd(dup(fd), size, offset);
 }
 
 status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
@@ -100,7 +100,7 @@
     return NO_ERROR;
 }
 
-status_t MemoryHeapBase::mapfd(int fd, size_t size)
+status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
 {
     if (size == 0) {
         // try to figure out the size automatically
@@ -121,7 +121,7 @@
 
     if ((mFlags & DONT_MAP_LOCALLY) == 0) {
         void* base = (uint8_t*)mmap(0, size,
-                PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+                PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
         if (base == MAP_FAILED) {
             LOGE("mmap(fd=%d, size=%u) failed (%s)",
                     fd, uint32_t(size), strerror(errno));
diff --git a/libs/rs/java/Fall/res/drawable-hdpi/sky.jpg b/libs/rs/java/Fall/res/drawable-hdpi/sky.jpg
old mode 100755
new mode 100644
index 2508f0c..565a63b
--- a/libs/rs/java/Fall/res/drawable-hdpi/sky.jpg
+++ b/libs/rs/java/Fall/res/drawable-hdpi/sky.jpg
Binary files differ
diff --git a/libs/rs/java/Fall/res/raw/fall.c b/libs/rs/java/Fall/res/raw/fall.c
index edfc926..f348a62 100644
--- a/libs/rs/java/Fall/res/raw/fall.c
+++ b/libs/rs/java/Fall/res/raw/fall.c
@@ -18,25 +18,10 @@
 #pragma stateFragmentStore(PFSBackground)
 
 #define RSID_STATE 0
-#define RSID_FRAME_COUNT 0
-#define RSID_WIDTH 1
-#define RSID_HEIGHT 2
-#define RSID_MESH_WIDTH 3
-#define RSID_MESH_HEIGHT 4
-#define RSID_RIPPLE_MAP_SIZE 5
-#define RSID_RIPPLE_INDEX 6
-#define RSID_DROP_X 7
-#define RSID_DROP_Y 8
-#define RSID_RUNNING 9
-#define RSID_LEAVES_COUNT 10
-    
 #define RSID_RIPPLE_MAP 1
 #define RSID_REFRACTION_MAP 2
 #define RSID_LEAVES 3
-
-#define RSID_GL_STATE 4
-#define RSID_GL_WIDTH 0
-#define RSID_GL_HEIGHT 1
+#define RSID_DROP 4
 
 #define LEAF_STRUCT_FIELDS_COUNT 11
 #define LEAF_STRUCT_X 0
@@ -67,8 +52,8 @@
 }
 
 void dropWithStrength(int x, int y, int r, int s) {
-    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
-    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
+    int width = State_meshWidth;
+    int height = State_meshHeight;
 
     if (x < r) x = r;
     if (y < r) y = r;
@@ -77,23 +62,24 @@
 
     x = width - x;
 
-    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
-    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
+    int rippleMapSize = State_rippleMapSize;
+    int index = State_rippleIndex;
     int origin = offset(0, 0, width);
 
     int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
     int sqr = r * r;
+    float invs = 1.0f / s;
 
     int h = 0;
-    for ( ; h < r; h++) {
+    for ( ; h < r; h += 1) {
         int sqv = h * h;
         int yn = origin + (y - h) * (width + 2);
         int yp = origin + (y + h) * (width + 2);
         int w = 0;
-        for ( ; w < r; w++) {
+        for ( ; w < r; w += 1) {
             int squ = w * w;
             if (squ + sqv < sqr) {
-                int v = -sqrtf((sqr - (squ + sqv)) << 16) / s;
+                int v = -sqrtf((sqr - (squ + sqv)) << 16) * invs;
                 current[yn + x + w] = v;
                 current[yp + x + w] = v;
                 current[yn + x - w] = v;
@@ -108,16 +94,16 @@
 }
 
 void updateRipples() {
-    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
-    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
-    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
-    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
+    int rippleMapSize = State_rippleMapSize;
+    int width = State_meshWidth;
+    int height = State_meshHeight;
+    int index = State_rippleIndex;
     int origin = offset(0, 0, width);
 
     int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
     int* next = loadArrayI32(RSID_RIPPLE_MAP, (1 - index) * rippleMapSize + origin);
 
-    storeI32(RSID_STATE, RSID_RIPPLE_INDEX, 1 - index);
+    storeI32(RSID_STATE, OFFSETOF_WorldState_rippleIndex, 1 - index);
 
     int a = 1;
     int b = width + 2;
@@ -125,16 +111,16 @@
     while (h) {
         int w = width;
         while (w) {
-            int droplet = ((current[-b] + current[b] + current[-a] + current[a]) >> 1) - next[0];
+            int droplet = ((current[-b] + current[b] + current[-a] + current[a]) >> 1) - *next;
             droplet -= (droplet >> DAMP);
-            next[0] = droplet;
-            current++;
-            next++;
-            w--;
+            *next = droplet;
+            current += 1;
+            next += 1;
+            w -= 1;
         }
         current += 2;
         next += 2;
-        h--;
+        h -= 1;
     }
 }
 
@@ -152,22 +138,26 @@
 }
 
 void generateRipples() {
-    int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
-    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
-    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
-    int index = loadI32(RSID_STATE, RSID_RIPPLE_INDEX);
+    int rippleMapSize = loadI32(RSID_STATE, OFFSETOF_WorldState_rippleMapSize);
+    int width = State_meshWidth;
+    int height = State_meshHeight;
+    int index = State_rippleIndex;
     int origin = offset(0, 0, width);
 
     int b = width + 2;
 
     int* current = loadArrayI32(RSID_RIPPLE_MAP, index * rippleMapSize + origin);
     int *map = loadArrayI32(RSID_REFRACTION_MAP, 0);
-    float *vertices = loadTriangleMeshVerticesF(NAMED_mesh);
+    float *vertices = loadTriangleMeshVerticesF(NAMED_WaterMesh);
+
+    float fw = (float) width;
+    float fh = (float) height;
+    float fy = (1.0f / 512.0f) * (1.0f / RIPPLE_HEIGHT);
 
     int h = height - 1;
     while (h >= 0) {
         int w = width - 1;
-        int wave = current[0];
+        int wave = *current;
         int offset = h * width;
         while (w >= 0) {
             int nextWave = current[1];
@@ -184,40 +174,47 @@
             v &= ~(v >> 31);
             if (v >= height) v = height - 1;
 
-            vertices[(offset + w) * 8 + 3] = u / (float) width;
-            vertices[(offset + w) * 8 + 4] = v / (float) height;
+            int index = (offset + w) << 3;
+            vertices[index + 3] = u / fw;
+            vertices[index + 4] = v / fh;
 
             // Update Z coordinate of the vertex
-            vertices[(offset + w) * 8 + 7] = (dy / 512.0f) / RIPPLE_HEIGHT;
+            vertices[index + 7] = dy * fy;
             
-            w--;
-            current++;
+            w -= 1;
+            current += 1;
             wave = nextWave;
         }
-        h--;
+        h -= 1;
         current += 2;
     }
 
     // Compute the normals for lighting
     int y = 0;
-    for ( ; y < height; y++) {
+    int w8 = width << 3;
+    for ( ; y < height; y += 1) {
         int x = 0;
         int yOffset = y * width;
-        for ( ; x < width; x++) {
+        for ( ; x < width; x += 1) {
+            int o = (yOffset + x) << 3;
+            int o1 = o + 8;
+            int ow = o + w8;
+            int ow1 = ow + 8;
+
             // V1
-            float v1x = vertices[(yOffset + x) * 8 + 5];
-            float v1y = vertices[(yOffset + x) * 8 + 6];
-            float v1z = vertices[(yOffset + x) * 8 + 7];
+            float v1x = vertices[o + 5];
+            float v1y = vertices[o + 6];
+            float v1z = vertices[o + 7];
 
             // V2
-            float v2x = vertices[(yOffset + x + 1) * 8 + 5];
-            float v2y = vertices[(yOffset + x + 1) * 8 + 6];
-            float v2z = vertices[(yOffset + x + 1) * 8 + 7];
+            float v2x = vertices[o1 + 5];
+            float v2y = vertices[o1 + 6];
+            float v2z = vertices[o1 + 7];
             
             // V3
-            float v3x = vertices[(yOffset + width + x) * 8 + 5];
-            float v3y = vertices[(yOffset + width + x) * 8 + 6];
-            float v3z = vertices[(yOffset + width + x) * 8 + 7];
+            float v3x = vertices[ow + 5];
+            float v3y = vertices[ow + 6];
+            float v3z = vertices[ow + 7];
 
             // N1
             float n1x = v2x - v1x;
@@ -235,15 +232,15 @@
             float n3z = n1x * n2y - n1y * n2x;
 
             // Normalize
-            float len = magf3(n3x, n3y, n3z);
-            n3x /= len;
-            n3y /= len;
-            n3z /= len;
+            float len = 1.0f / magf3(n3x, n3y, n3z);
+            n3x *= len;
+            n3y *= len;
+            n3z *= len;
             
             // V2
-            v2x = vertices[(yOffset + width + x + 1) * 8 + 5];
-            v2y = vertices[(yOffset + width + x + 1) * 8 + 6];
-            v2z = vertices[(yOffset + width + x + 1) * 8 + 7];
+            v2x = vertices[ow1 + 5];
+            v2y = vertices[ow1 + 6];
+            v2z = vertices[ow1 + 7];
 
             // N1
             n1x = v2x - v1x;
@@ -255,23 +252,23 @@
             n2y = v3y - v1y;
             n2z = v3z - v1z;
 
-            // Avegare of previous normal and N1 x N2
-            n3x = n3x / 2.0f + (n1y * n2z - n1z * n2y) / 2.0f;
-            n3y = n3y / 2.0f + (n1z * n2x - n1x * n2z) / 2.0f;
-            n3z = n3z / 2.0f + (n1x * n2y - n1y * n2x) / 2.0f;
+            // Average of previous normal and N1 x N2
+            n3x = n3x * 0.5f + (n1y * n2z - n1z * n2y) * 0.5f;
+            n3y = n3y * 0.5f + (n1z * n2x - n1x * n2z) * 0.5f;
+            n3z = n3z * 0.5f + (n1x * n2y - n1y * n2x) * 0.5f;
 
             // Normalize
-            len = magf3(n3x, n3y, n3z);
-            n3x /= len;
-            n3y /= len;
-            n3z /= len;
+            len = 1.0f / magf3(n3x, n3y, n3z);
+            n3x *= len;
+            n3y *= len;
+            n3z *= len;
 
-            vertices[(yOffset + x) * 8 + 0] = n3x;
-            vertices[(yOffset + x) * 8 + 1] = n3y;
-            vertices[(yOffset + x) * 8 + 2] = -n3z;
+            vertices[o + 0] = n3x;
+            vertices[o + 1] = n3y;
+            vertices[o + 2] = -n3z;
             
             // reset Z
-            //vertices[(yOffset + x) * 8 + 7] = 0.0f;
+            //vertices[(yOffset + x) << 3 + 7] = 0.0f;
         }
     }
 }
@@ -279,10 +276,10 @@
 float averageZ(float x1, float x2, float y1, float y2, float* vertices,
         int meshWidth, int meshHeight, float glWidth, float glHeight) {
 
-    x1 = ((x1 + glWidth / 2.0f) / glWidth) * meshWidth;
-    x2 = ((x2 + glWidth / 2.0f) / glWidth) * meshWidth;
-    y1 = ((y1 + glHeight / 2.0f) / glHeight) * meshHeight;
-    y2 = ((y2 + glHeight / 2.0f) / glHeight) * meshHeight;
+    x1 = ((x1 + glWidth * 0.5f) / glWidth) * meshWidth;
+    x2 = ((x2 + glWidth * 0.5f) / glWidth) * meshWidth;
+    y1 = ((y1 + glHeight * 0.5f) / glHeight) * meshHeight;
+    y2 = ((y2 + glHeight * 0.5f) / glHeight) * meshHeight;
 
     int quadX1 = clamp(x1, 0, meshWidth);
     int quadX2 = clamp(x2, 0, meshWidth);
@@ -293,19 +290,19 @@
     int vertexCount = 0;
 
     int y = quadY1;
-    for ( ; y < quadY2; y++) {
+    for ( ; y < quadY2; y += 1) {
         int x = quadX1;
         int yOffset = y * meshWidth;
-        for ( ; x < quadX2; x++) {
-            z += vertices[(yOffset + x) * 8 + 7];
-            vertexCount++;
+        for ( ; x < quadX2; x += 1) {
+            z += vertices[(yOffset + x) << 3 + 7];
+            vertexCount += 1;
         }
     }
 
     return 55.0f * z / vertexCount;
 }
 
-void drawLeaf(int index, int frameCount, float* vertices, int meshWidth, int meshHeight,
+void drawLeaf(int index, float* vertices, int meshWidth, int meshHeight,
         float glWidth, float glHeight) {
 
     float *leafStruct = loadArrayF(RSID_LEAVES, index);
@@ -334,10 +331,10 @@
     if (a > 0.0f) {
         tz = -a;
     } else {
-        z1 = averageZ(x1, x, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight);
-        z2 = averageZ(x, x2, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight);
-        z3 = averageZ(x, x2, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight);
-        z4 = averageZ(x1, x, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight);
+//        z1 = averageZ(x1, x, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight);
+//        z2 = averageZ(x, x2, y1, y, vertices, meshWidth, meshHeight, glWidth, glHeight);
+//        z3 = averageZ(x, x2, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight);
+//        z4 = averageZ(x1, x, y, y2, vertices, meshWidth, meshHeight, glWidth, glHeight);
     }
 
     x1 -= x;
@@ -361,16 +358,16 @@
     if (a <= 0.0f) {
         float rippled = leafStruct[LEAF_STRUCT_RIPPLED];
         if (rippled < 0.0f) {
-            drop(((x + glWidth / 2.0f) / glWidth) * meshWidth,
-                 meshHeight - ((y + glHeight / 2.0f) / glHeight) * meshHeight,
+            drop(((x + glWidth * 0.5f) / glWidth) * meshWidth,
+                 meshHeight - ((y + glHeight * 0.5f) / glHeight) * meshHeight,
                  DROP_RADIUS);
             spin /= 4.0f;
             leafStruct[LEAF_STRUCT_SPIN] = spin;
             leafStruct[LEAF_STRUCT_RIPPLED] = 1.0f;
         } else {
-            dropWithStrength(((x + glWidth / 2.0f) / glWidth) * meshWidth,
-                meshHeight - ((y + glHeight / 2.0f) / glHeight) * meshHeight,
-                2, 5);
+//            dropWithStrength(((x + glWidth / 2.0f) / glWidth) * meshWidth,
+//                meshHeight - ((y + glHeight / 2.0f) / glHeight) * meshHeight,
+//                2, 5);
         }
         leafStruct[LEAF_STRUCT_X] = x + leafStruct[LEAF_STRUCT_DELTAX];
         leafStruct[LEAF_STRUCT_Y] = y + leafStruct[LEAF_STRUCT_DELTAY];
@@ -398,23 +395,24 @@
     }
 }
 
-void drawLeaves(int frameCount) {
+void drawLeaves() {
     bindProgramFragment(NAMED_PFBackground);
     bindProgramFragmentStore(NAMED_PFSLeaf);
+    bindProgramVertex(NAMED_PVSky);
     bindTexture(NAMED_PFBackground, 0, NAMED_TLeaves);
 
-    int leavesCount = loadI32(RSID_STATE, RSID_LEAVES_COUNT);
+    int leavesCount = State_leavesCount;
     int count = leavesCount * LEAF_STRUCT_FIELDS_COUNT;
-    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
-    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);    
-    float glWidth = loadF(RSID_GL_STATE, RSID_GL_WIDTH);
-    float glHeight = loadF(RSID_GL_STATE, RSID_GL_HEIGHT);
+    int width = State_meshWidth;
+    int height = State_meshHeight;    
+    float glWidth = State_glWidth;
+    float glHeight = State_glHeight;
 
-    float *vertices = loadTriangleMeshVerticesF(NAMED_mesh);    
+    float *vertices = loadTriangleMeshVerticesF(NAMED_WaterMesh);    
 
     int i = 0;
     for ( ; i < count; i += LEAF_STRUCT_FIELDS_COUNT) {
-        drawLeaf(i, frameCount, vertices, width, height, glWidth, glHeight);
+        drawLeaf(i, vertices, width, height, glWidth, glHeight);
     }
     
     float matrix[16];
@@ -422,47 +420,99 @@
     vpLoadModelMatrix(matrix);
 }
 
-int main(int index) {
-    int frameCount = loadI32(RSID_STATE, RSID_FRAME_COUNT);
-
-    int dropX = loadI32(RSID_STATE, RSID_DROP_X);
-    if (dropX != -1) {
-        int dropY = loadI32(RSID_STATE, RSID_DROP_Y);
-        drop(dropX, dropY, DROP_RADIUS);
-        storeI32(RSID_STATE, RSID_DROP_X, -1);
-        storeI32(RSID_STATE, RSID_DROP_Y, -1);
-    }
-
-    int isRunning = loadI32(RSID_STATE, RSID_RUNNING);
-    if (isRunning) {
-        updateRipples();
-        generateRipples();
-        updateTriangleMesh(NAMED_mesh);
-    }
-
+void drawRiverbed() {
     bindTexture(NAMED_PFBackground, 0, NAMED_TRiverbed);
-    drawTriangleMesh(NAMED_mesh);
 
-    color(1.0f, 1.0f, 1.0f, 0.7f);
+    drawTriangleMesh(NAMED_WaterMesh);
+}
+
+void drawSky() {
+    color(1.0f, 1.0f, 1.0f, 0.8f);
+
     bindProgramFragment(NAMED_PFSky);
     bindProgramFragmentStore(NAMED_PFSLeaf);
     bindTexture(NAMED_PFSky, 0, NAMED_TSky);
-    drawTriangleMesh(NAMED_mesh);
 
+    float x = State_skyOffsetX + State_skySpeedX;
+    float y = State_skyOffsetY + State_skySpeedY;
+
+    if (x > 1.0f) x = 0.0f;
+    if (x < -1.0f) x = 0.0f;
+    if (y > 1.0f) y = 0.0f;
+
+    storeF(RSID_STATE, OFFSETOF_WorldState_skyOffsetX, x);
+    storeF(RSID_STATE, OFFSETOF_WorldState_skyOffsetY, y);
+
+    float matrix[16];
+    matrixLoadTranslate(matrix, x, y, 0.0f);
+    vpLoadTextureMatrix(matrix);
+
+    drawTriangleMesh(NAMED_WaterMesh);
+
+    matrixLoadIdentity(matrix);
+    vpLoadTextureMatrix(matrix);
+}
+
+void drawLighting() {
     ambient(0.0f, 0.0f, 0.0f, 1.0f);
     diffuse(0.0f, 0.0f, 0.0f, 1.0f);
     specular(0.44f, 0.44f, 0.44f, 1.0f);
     shininess(40.0f);
+
     bindProgramFragmentStore(NAMED_PFSBackground);
     bindProgramFragment(NAMED_PFLighting);
     bindProgramVertex(NAMED_PVLight);
-    drawTriangleMesh(NAMED_mesh);
+
+    drawTriangleMesh(NAMED_WaterMesh);
+}
+
+void drawNormals() {
+    int width = State_meshWidth;
+    int height = State_meshHeight;
+
+    float *vertices = loadTriangleMeshVerticesF(NAMED_WaterMesh);
 
     bindProgramVertex(NAMED_PVSky);
-    drawLeaves(frameCount);
+    bindProgramFragment(NAMED_PFLighting);
 
-    frameCount++;
-    storeI32(RSID_STATE, RSID_FRAME_COUNT, frameCount);
+    color(1.0f, 0.0f, 0.0f, 1.0f);
+
+    float scale = 1.0f / 10.0f;
+    int y = 0;
+    for ( ; y < height; y += 1) {
+        int yOffset = y * width;
+        int x = 0;
+        for ( ; x < width; x += 1) {
+            int offset = (yOffset + x) << 3;
+            float vx = vertices[offset + 5];
+            float vy = vertices[offset + 6];
+            float vz = vertices[offset + 7];
+            float nx = vertices[offset + 0];
+            float ny = vertices[offset + 1];
+            float nz = vertices[offset + 2];
+            drawLine(vx, vy, vz, vx + nx * scale, vy + ny * scale, vz + nz * scale);
+        }
+    }
+}
+
+int main(int index) {
+    int dropX = Drop_dropX;
+    if (dropX != -1) {
+        int dropY = Drop_dropY;
+        drop(dropX, dropY, DROP_RADIUS);
+        storeI32(RSID_DROP, OFFSETOF_DropState_dropX, -1);
+        storeI32(RSID_DROP, OFFSETOF_DropState_dropY, -1);
+    }
+
+    updateRipples();
+    generateRipples();
+    updateTriangleMesh(NAMED_WaterMesh);
+
+    drawRiverbed();
+    drawSky();
+    drawLighting();
+    drawLeaves();
+    //drawNormals();
 
     return 1;
 }
diff --git a/libs/rs/java/Fall/src/com/android/fall/rs/FallRS.java b/libs/rs/java/Fall/src/com/android/fall/rs/FallRS.java
index b4f96f3..3b13bed 100644
--- a/libs/rs/java/Fall/src/com/android/fall/rs/FallRS.java
+++ b/libs/rs/java/Fall/src/com/android/fall/rs/FallRS.java
@@ -26,6 +26,7 @@
 import android.renderscript.Sampler;
 import android.renderscript.Element;
 import android.renderscript.Light;
+import android.renderscript.Type;
 import static android.renderscript.Sampler.Value.LINEAR;
 import static android.renderscript.Sampler.Value.WRAP;
 import static android.renderscript.ProgramStore.DepthFunc.*;
@@ -43,18 +44,7 @@
     private static final int MESH_RESOLUTION = 48;
 
     private static final int RSID_STATE = 0;
-    private static final int RSID_STATE_FRAMECOUNT = 0;
-    private static final int RSID_STATE_WIDTH = 1;
-    private static final int RSID_STATE_HEIGHT = 2;
-    private static final int RSID_STATE_MESH_WIDTH = 3;
-    private static final int RSID_STATE_MESH_HEIGHT = 4;
-    private static final int RSID_STATE_RIPPLE_MAP_SIZE = 5;
-    private static final int RSID_STATE_RIPPLE_INDEX = 6;
-    private static final int RSID_STATE_DROP_X = 7;
-    private static final int RSID_STATE_DROP_Y = 8;
-    private static final int RSID_STATE_RUNNING = 9;
-    private static final int RSID_STATE_LEAVES_COUNT = 10;
-
+    
     private static final int TEXTURES_COUNT = 3;
     private static final int LEAVES_TEXTURES_COUNT = 4;
     private static final int RSID_TEXTURE_RIVERBED = 0;
@@ -80,12 +70,8 @@
     private static final int LEAF_STRUCT_DELTAX = 9;
     private static final int LEAF_STRUCT_DELTAY = 10;
 
-    private static final int RSID_GL_STATE = 4;    
-    private static final int RSID_STATE_GL_WIDTH = 0;
-    private static final int RSID_STATE_GL_HEIGHT = 1;
-    
-    private boolean mIsRunning = true;    
-    
+    private static final int RSID_DROP = 4;    
+
     private Resources mResources;
     private RenderScript mRS;
 
@@ -94,34 +80,35 @@
     private final int mWidth;
     private final int mHeight;
 
-    private ScriptC mScript;
-    private Sampler mSampler;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramFragment mPfBackground;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramFragment mPfLighting;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramFragment mPfSky;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramStore mPfsBackground;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramStore mPfsLeaf;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramVertex mPvLight;
+    @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramVertex mPvSky;
-    private ProgramVertex.MatrixAllocation mPvOrthoAlloc;
-    private Light mLight;
-
-    private Allocation[] mTextures;
 
     private Allocation mState;
-    private RenderScript.TriangleMesh mMesh;
+    private Allocation mDropState;
+    private DropState mDrop;
+    private Type mStateType;
+    private Type mDropType;
     private int mMeshWidth;
-    private int mMeshHeight;
 
+    private int mMeshHeight;
     private Allocation mRippleMap;
     private Allocation mRefractionMap;
+
     private Allocation mLeaves;
-
-    private Allocation mGlState;
     private float mGlHeight;
 
-    private final int[] mIntData2 = new int[2];
-
     public FallRS(int width, int height) {
         mWidth = width;
         mHeight = height;
@@ -135,38 +122,6 @@
         initRS();
     }
 
-    public void destroy() {
-        mScript.destroy();
-        mSampler.destroy();
-        mPfBackground.destroy();
-        mPfsBackground.destroy();
-        mPvLight.destroy();
-        mPvOrthoAlloc.mAlloc.destroy();
-        for (Allocation a : mTextures) {
-            a.destroy();
-        }
-        mState.destroy();
-        mMesh.destroy();
-        mLight.destroy();
-        mRippleMap.destroy();
-        mRefractionMap.destroy();
-        mPvSky.destroy();
-        mPfLighting.destroy();
-        mLeaves.destroy();
-        mPfsLeaf.destroy();
-        mPfSky.destroy();
-        mGlState.destroy();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            destroy();
-        } finally {
-            super.finalize();
-        }
-    }
-
     private void initRS() {
         createProgramVertex();
         createProgramFragmentStore();
@@ -176,19 +131,22 @@
         loadTextures();
 
         ScriptC.Builder sb = new ScriptC.Builder(mRS);
+        sb.setType(mStateType, "State", RSID_STATE);
+        sb.setType(mDropType, "Drop", RSID_DROP);
         sb.setScript(mResources, R.raw.fall);
         sb.setRoot(true);
-        mScript = sb.create();
-        mScript.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-        mScript.setTimeZone(TimeZone.getDefault().getID());
 
-        mScript.bindAllocation(mState, RSID_STATE);
-        mScript.bindAllocation(mRippleMap, RSID_RIPPLE_MAP);
-        mScript.bindAllocation(mRefractionMap, RSID_REFRACTION_MAP);
-        mScript.bindAllocation(mLeaves, RSID_LEAVES);
-        mScript.bindAllocation(mGlState, RSID_GL_STATE);
+        ScriptC script = sb.create();
+        script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        script.setTimeZone(TimeZone.getDefault().getID());
 
-        mRS.contextBindRootScript(mScript);
+        script.bindAllocation(mState, RSID_STATE);
+        script.bindAllocation(mRippleMap, RSID_RIPPLE_MAP);
+        script.bindAllocation(mRefractionMap, RSID_REFRACTION_MAP);
+        script.bindAllocation(mLeaves, RSID_LEAVES);
+        script.bindAllocation(mDropState, RSID_DROP);
+
+        mRS.contextBindRootScript(script);
     }
 
     private void createMesh() {
@@ -219,27 +177,42 @@
         hResolution += 2;        
         
         for (int y = 0; y <= hResolution; y++) {
+            final boolean shift = (y & 0x1) == 0;
             final float yOffset = y * quadHeight - glHeight / 2.0f - quadHeight;
             final float t = 1.0f - y / (float) hResolution;
             for (int x = 0; x <= wResolution; x++) {
-                rs.triangleMeshAddVertex_XYZ_ST_NORM(
-                        -1.0f + x * quadWidth - quadWidth, yOffset, 0.0f,
-                        x / (float) wResolution, t,
-                        0.0f, 0.0f, -1.0f);
+                if (shift) {
+                    rs.triangleMeshAddVertex_XYZ_ST_NORM(
+                            -1.0f + x * quadWidth - quadWidth, yOffset, 0.0f,
+                            x / (float) wResolution, t,
+                            0.0f, 0.0f, -1.0f);
+                } else {
+                    rs.triangleMeshAddVertex_XYZ_ST_NORM(
+                            -1.0f + x * quadWidth - quadWidth * 0.5f, yOffset, 0.0f,
+                            x / (float) wResolution, t,
+                            0.0f, 0.0f, -1.0f);
+                }
             }
         }
 
         for (int y = 0; y < hResolution; y++) {
+            final boolean shift = (y & 0x1) == 0;
+            final int yOffset = y * (wResolution + 1);
             for (int x = 0; x < wResolution; x++) {
-                final int index = y * (wResolution + 1) + x;
+                final int index = yOffset + x;
                 final int iWR1 = index + wResolution + 1;
-                rs.triangleMeshAddTriangle(index, index + 1, iWR1);
-                rs.triangleMeshAddTriangle(index + 1, iWR1, iWR1 + 1);
+                if (shift) {
+                    rs.triangleMeshAddTriangle(index, index + 1, iWR1);
+                    rs.triangleMeshAddTriangle(index + 1, iWR1 + 1, iWR1);
+                } else {
+                    rs.triangleMeshAddTriangle(index, iWR1 + 1, iWR1);
+                    rs.triangleMeshAddTriangle(index, index + 1, iWR1 + 1);
+                }
             }
         }
 
-        mMesh = rs.triangleMeshCreate();
-        mMesh.setName("mesh");
+        RenderScript.TriangleMesh mesh = rs.triangleMeshCreate();
+        mesh.setName("WaterMesh");
 
         mMeshWidth = wResolution + 1;
         mMeshHeight = hResolution + 1;
@@ -249,7 +222,6 @@
         final int rippleMapSize = (mMeshWidth + 2) * (mMeshHeight + 2);
 
         createState(rippleMapSize);
-        createGlState();
         createRippleMap(rippleMapSize);
         createRefractionMap();
         createLeaves();
@@ -281,29 +253,53 @@
         mRippleMap.data(rippleMap);
     }
 
-    private void createGlState() {
-        final float[] meshState = new float[2];
-        mGlState = Allocation.createSized(mRS, USER_FLOAT, meshState.length);
-        meshState[RSID_STATE_GL_WIDTH] = 2.0f;
-        meshState[RSID_STATE_GL_HEIGHT] = mGlHeight;
-        mGlState.data(meshState);
+    static class WorldState {
+        public int frameCount;
+        public int width;
+        public int height;
+        public int meshWidth;
+        public int meshHeight;
+        public int rippleMapSize;
+        public int rippleIndex;
+        public int leavesCount;
+        public float glWidth;
+        public float glHeight;
+        public float skyOffsetX;
+        public float skyOffsetY;
+        public float skySpeedX;
+        public float skySpeedY;
+    }
+    
+    static class DropState {
+        public int dropX;
+        public int dropY;
     }
 
     private void createState(int rippleMapSize) {
-        final int[] data = new int[11];
-        mState = Allocation.createSized(mRS, USER_I32, data.length);
-        data[RSID_STATE_FRAMECOUNT] = 0;
-        data[RSID_STATE_WIDTH] = mWidth;
-        data[RSID_STATE_HEIGHT] = mHeight;
-        data[RSID_STATE_MESH_WIDTH] = mMeshWidth;
-        data[RSID_STATE_MESH_HEIGHT] = mMeshHeight;
-        data[RSID_STATE_RIPPLE_MAP_SIZE] = rippleMapSize;
-        data[RSID_STATE_RIPPLE_INDEX] = 0;
-        data[RSID_STATE_DROP_X] = -1;
-        data[RSID_STATE_DROP_Y] = -1;
-        data[RSID_STATE_RUNNING] = 1;
-        data[RSID_STATE_LEAVES_COUNT] = LEAVES_COUNT;
-        mState.data(data);
+        WorldState worldState = new WorldState();
+        worldState.width = mWidth;
+        worldState.height = mHeight;
+        worldState.meshWidth = mMeshWidth;
+        worldState.meshHeight = mMeshHeight;
+        worldState.rippleMapSize = rippleMapSize;
+        worldState.rippleIndex = 0;
+        worldState.leavesCount = LEAVES_COUNT;
+        worldState.glWidth = 2.0f;
+        worldState.glHeight = mGlHeight;
+        worldState.skySpeedX = random(-0.001f, 0.001f);
+        worldState.skySpeedY = random(0.00008f, 0.0002f);
+
+        mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState");
+        mState = Allocation.createTyped(mRS, mStateType);
+        mState.data(worldState);
+        
+        mDrop = new DropState();
+        mDrop.dropX = -1;
+        mDrop.dropY = -1;
+        
+        mDropType = Type.createFromClass(mRS, DropState.class, 1, "DropState");
+        mDropState = Allocation.createTyped(mRS, mDropType);
+        mDropState.data(mDrop);
     }
 
     private void createLeaf(float[] leaves, int index) {
@@ -323,9 +319,7 @@
     }
 
     private void loadTextures() {
-        mTextures = new Allocation[TEXTURES_COUNT];
-
-        final Allocation[] textures = mTextures;
+        final Allocation[] textures = new Allocation[TEXTURES_COUNT];
         textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.riverbed, "TRiverbed");
         textures[RSID_TEXTURE_LEAVES] = loadTextureARGB(R.drawable.leaves, "TLeaves");
         textures[RSID_TEXTURE_SKY] = loadTextureARGB(R.drawable.sky, "TSky");
@@ -357,27 +351,27 @@
         sampleBuilder.setMag(LINEAR);
         sampleBuilder.setWrapS(WRAP);
         sampleBuilder.setWrapT(WRAP);
-        mSampler = sampleBuilder.create();
+        Sampler sampler = sampleBuilder.create();
 
         ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null);
         builder.setTexEnable(true, 0);
         builder.setTexEnvMode(REPLACE, 0);
         mPfBackground = builder.create();
         mPfBackground.setName("PFBackground");
-        mPfBackground.bindSampler(mSampler, 0);
+        mPfBackground.bindSampler(sampler, 0);
 
         builder = new ProgramFragment.Builder(mRS, null, null);
         builder.setTexEnable(false, 0);
         mPfLighting = builder.create();
         mPfLighting.setName("PFLighting");
-        mPfLighting.bindSampler(mSampler, 0);
+        mPfLighting.bindSampler(sampler, 0);
         
         builder = new ProgramFragment.Builder(mRS, null, null);
         builder.setTexEnable(true, 0);
         builder.setTexEnvMode(MODULATE, 0);
         mPfSky = builder.create();
         mPfSky.setName("PFSky");
-        mPfSky.bindSampler(mSampler, 0);
+        mPfSky.bindSampler(sampler, 0);
     }
 
     private void createProgramFragmentStore() {
@@ -399,33 +393,28 @@
     }
 
     private void createProgramVertex() {
-        mPvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
-        mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
+        ProgramVertex.MatrixAllocation pvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
+        pvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
 
-        mLight = new Light.Builder(mRS).create();
-        mLight.setPosition(0.0f, 2.0f, -8.0f);
+        Light light = new Light.Builder(mRS).create();
+        light.setPosition(0.0f, 2.0f, -8.0f);
 
         ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null);
-        builder.setTextureMatrixEnable(true);
-        builder.addLight(mLight);
+        builder.addLight(light);
         mPvLight = builder.create();
-        mPvLight.bindAllocation(mPvOrthoAlloc);
+        mPvLight.bindAllocation(pvOrthoAlloc);
         mPvLight.setName("PVLight");
         
         builder = new ProgramVertex.Builder(mRS, null, null);
+        builder.setTextureMatrixEnable(true);
         mPvSky = builder.create();
-        mPvSky.bindAllocation(mPvOrthoAlloc);
+        mPvSky.bindAllocation(pvOrthoAlloc);
         mPvSky.setName("PVSky");
     }
 
     void addDrop(float x, float y) {
-        mIntData2[0] = (int) ((x / mWidth) * mMeshWidth);
-        mIntData2[1] = (int) ((y / mHeight) * mMeshHeight);
-        mState.subData1D(RSID_STATE_DROP_X, 2, mIntData2);
-    }
-    
-    void togglePause() {
-        mIsRunning = !mIsRunning;
-        mState.subData1D(RSID_STATE_RUNNING, 1, new int[] { mIsRunning ? 1 : 0 });
+        mDrop.dropX = (int) ((x / mWidth) * mMeshWidth);
+        mDrop.dropY = (int) ((y / mHeight) * mMeshHeight);
+        mDropState.data(mDrop);
     }
 }
diff --git a/libs/rs/java/Fall/src/com/android/fall/rs/FallView.java b/libs/rs/java/Fall/src/com/android/fall/rs/FallView.java
index d7573be..fa2caa7 100644
--- a/libs/rs/java/Fall/src/com/android/fall/rs/FallView.java
+++ b/libs/rs/java/Fall/src/com/android/fall/rs/FallView.java
@@ -42,20 +42,6 @@
     }
 
     @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        if (mRender != null) mRender.destroy();
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
-                keyCode == KeyEvent.KEYCODE_MENU) {
-            mRender.togglePause();
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
diff --git a/libs/rs/java/Galaxy/res/raw/galaxy.c b/libs/rs/java/Galaxy/res/raw/galaxy.c
index 59c31a1..9ff449f 100644
--- a/libs/rs/java/Galaxy/res/raw/galaxy.c
+++ b/libs/rs/java/Galaxy/res/raw/galaxy.c
@@ -19,11 +19,13 @@
 
 #define RSID_PARTICLES 1
 
-#define PARTICLE_STRUCT_FIELDS_COUNT 4
+#define PARTICLE_STRUCT_FIELDS_COUNT 6
 #define PARTICLE_STRUCT_ANGLE 0
 #define PARTICLE_STRUCT_DISTANCE 1
 #define PARTICLE_STRUCT_SPEED 2
 #define PARTICLE_STRUCT_RADIUS 3
+#define PARTICLE_STRUCT_S 4
+#define PARTICLE_STRUCT_T 5
 
 #define RSID_PARTICLES_BUFFER 2
 #define PARTICLE_BUFFER_COMPONENTS_COUNT 5
@@ -31,19 +33,14 @@
 #define PARTICLES_TEXTURES_COUNT 2
 
 #define ELLIPSE_RATIO 0.892f
-#define ELLIPSE_TWIST 0.02333333333f
 
 void drawSpace(int width, int height) {
     bindTexture(NAMED_PFBackground, 0, NAMED_TSpace);
     drawQuadTexCoords(
-            0.0f, 0.0f, 0.0f,
-            0.0f, 1.0f,
-            width, 0.0f, 0.0f,
-            2.0f, 1.0f,
-            width, height, 0.0f,
-            2.0f, 0.0f,
-            0.0f, height, 0.0f,
-            0.0f, 0.0f);
+            0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+            width, 0.0f, 0.0f, 2.0f, 1.0f,
+            width, height, 0.0f, 2.0f, 0.0f,
+            0.0f, height, 0.0f, 0.0f, 0.0f);
 }
 
 void drawLights(int width, int height) {
@@ -61,39 +58,34 @@
              x + 512.0f * 1.1f, y + 512.0f, 0.0f);
 }
 
-void drawParticle(float *particle, int index, float *particleBuffer, int bufferIndex,
-        float w, float h) {
-
-    float distance = particle[index + PARTICLE_STRUCT_DISTANCE];
-    float angle = particle[index + PARTICLE_STRUCT_ANGLE];
-    float speed = particle[index + PARTICLE_STRUCT_SPEED];
-    float r = particle[index + PARTICLE_STRUCT_RADIUS];
+void drawParticle(float *particle, float *particleBuffer, float w, float h) {
+    float distance = particle[PARTICLE_STRUCT_DISTANCE];
+    float angle = particle[PARTICLE_STRUCT_ANGLE];
+    float speed = particle[PARTICLE_STRUCT_SPEED];
+    float r = particle[PARTICLE_STRUCT_RADIUS];
 
     float a = angle + speed;
     float x = distance * sinf_fast(a);
     float y = distance * cosf_fast(a) * ELLIPSE_RATIO;
-    float z = distance * ELLIPSE_TWIST;
-    float s = cosf_fast(z);
-    float t = sinf_fast(z);
+    float s = particle[PARTICLE_STRUCT_S];
+    float t = particle[PARTICLE_STRUCT_T];
 
     float sX = t * x + s * y + w;
     float sY = s * x - t * y + h;
 
     // lower left vertex of the particle's triangle
-    particleBuffer[bufferIndex + 1] = sX - r;     // X
-    particleBuffer[bufferIndex + 2] = sY + r;     // Y
+    particleBuffer[1] = sX - r;     // X
+    particleBuffer[2] = sY + r;     // Y
 
     // lower right vertex of the particle's triangle
-    bufferIndex += PARTICLE_BUFFER_COMPONENTS_COUNT;
-    particleBuffer[bufferIndex + 1] = sX + r;     // X
-    particleBuffer[bufferIndex + 2] = sY + r;     // Y
+    particleBuffer[6] = sX + r;     // X
+    particleBuffer[7] = sY + r;     // Y
 
     // upper middle vertex of the particle's triangle
-    bufferIndex += PARTICLE_BUFFER_COMPONENTS_COUNT;
-    particleBuffer[bufferIndex + 1] = sX;         // X
-    particleBuffer[bufferIndex + 2] = sY - r;     // Y
+    particleBuffer[11] = sX;         // X
+    particleBuffer[12] = sY - r;     // Y
 
-    particle[index + PARTICLE_STRUCT_ANGLE] = a;
+    particle[PARTICLE_STRUCT_ANGLE] = a;
 }
 
 void drawParticles(int width, int height) {
@@ -103,7 +95,6 @@
 
     int radius = State_galaxyRadius;
     int particlesCount = State_particlesCount;
-    int count = particlesCount * PARTICLE_STRUCT_FIELDS_COUNT;
 
     float *particle = loadArrayF(RSID_PARTICLES, 0);
     float *particleBuffer = loadArrayF(RSID_PARTICLES_BUFFER, 0);
@@ -112,11 +103,11 @@
     float h = height * 0.5f;
 
     int i = 0;
-    int bufferIndex = 0;
-    for ( ; i < count; i += PARTICLE_STRUCT_FIELDS_COUNT) {
-        drawParticle(particle, i, particleBuffer, bufferIndex, w, h);
-        // each particle is a triangle (3 vertices) of 6 properties (ABGR, X, Y, Z, S, T)
-        bufferIndex += 3 * PARTICLE_BUFFER_COMPONENTS_COUNT;
+    for ( ; i < particlesCount; i++) {
+        drawParticle(particle, particleBuffer, w, h);
+        particle += PARTICLE_STRUCT_FIELDS_COUNT;
+        // each particle is a triangle (3 vertices) of 5 properties (ABGR, X, Y, S, T)
+        particleBuffer += 3 * PARTICLE_BUFFER_COMPONENTS_COUNT;
     }
 
     uploadToBufferObject(NAMED_ParticlesBuffer);
diff --git a/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyRS.java b/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyRS.java
index 717100d..c6f5816 100644
--- a/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyRS.java
+++ b/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyRS.java
@@ -42,9 +42,11 @@
 
 import java.util.TimeZone;
 
+@SuppressWarnings({"FieldCanBeLocal"})
 class GalaxyRS {
     private static final int GALAXY_RADIUS = 300;
     private static final int PARTICLES_COUNT = 12000;
+    private static final float ELLIPSE_TWIST = 0.023333333f;
 
     private static final int RSID_STATE = 0;
 
@@ -54,11 +56,13 @@
     private static final int RSID_TEXTURE_FLARES = 2;
 
     private static final int RSID_PARTICLES = 1;
-    private static final int PARTICLE_STRUCT_FIELDS_COUNT = 4;
+    private static final int PARTICLE_STRUCT_FIELDS_COUNT = 6;
     private static final int PARTICLE_STRUCT_ANGLE = 0;
     private static final int PARTICLE_STRUCT_DISTANCE = 1;
     private static final int PARTICLE_STRUCT_SPEED = 2;
     private static final int PARTICLE_STRUCT_RADIUS = 3;
+    private static final int PARTICLE_STRUCT_S = 4;
+    private static final int PARTICLE_STRUCT_T = 5;
 
     private static final int RSID_PARTICLES_BUFFER = 2;
 
@@ -103,35 +107,6 @@
         initRS();
     }
 
-    public void destroy() {
-        mScript.destroy();
-        mSampler.destroy();
-        mLightSampler.destroy();
-        mPfBackground.destroy();
-        mPfsBackground.destroy();
-        mPvBackground.destroy();
-        mPvOrthoAlloc.mAlloc.destroy();
-        for (Allocation a : mTextures) {
-            a.destroy();
-        }
-        mState.destroy();
-        mPfLighting.destroy();
-        mParticles.destroy();
-        mPfsLights.destroy();
-        mParticlesMesh.destroy();
-        mParticlesBuffer.destroy();
-        mStateType.destroy();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            destroy();
-        } finally {
-            super.finalize();
-        }
-    }
-
     private void initRS() {
         createProgramVertex();
         createProgramFragmentStore();
@@ -218,13 +193,16 @@
         float d = abs(randomGauss()) * GALAXY_RADIUS / 2.0f + random(-4.0f, 4.0f);
         float z = randomGauss() * 0.5f * 0.8f * ((GALAXY_RADIUS - d) / (float) GALAXY_RADIUS);
         z += 1.0f;
+        float p = d * ELLIPSE_TWIST;
 
         particles[index + PARTICLE_STRUCT_ANGLE] = random(0.0f, (float) (Math.PI * 2.0));
         particles[index + PARTICLE_STRUCT_DISTANCE] = d;
         particles[index + PARTICLE_STRUCT_SPEED] = random(0.0015f, 0.0025f) *
                 (0.5f + (0.5f * (float) GALAXY_RADIUS / d)) * 0.7f;
         particles[index + PARTICLE_STRUCT_RADIUS] = z * random(1.2f, 2.1f);
-
+        particles[index + PARTICLE_STRUCT_S] = (float) Math.cos(p);
+        particles[index + PARTICLE_STRUCT_T] = (float) Math.sin(p);
+        
         int red, green, blue;
         if (d < GALAXY_RADIUS / 3.0f) {
             red = (int) (220 + (d / (float) GALAXY_RADIUS) * 35);
diff --git a/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyView.java b/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyView.java
index 341293b..4f6d3f0 100644
--- a/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyView.java
+++ b/libs/rs/java/Galaxy/src/com/android/galaxy/rs/GalaxyView.java
@@ -22,8 +22,6 @@
 import android.renderscript.RSSurfaceView;
 
 class GalaxyView extends RSSurfaceView {
-    private GalaxyRS mRender;
-
     public GalaxyView(Context context) {
         super(context);
         setFocusable(true);
@@ -34,12 +32,7 @@
         super.surfaceChanged(holder, format, w, h);
 
         RenderScript RS = createRenderScript();
-        mRender = new GalaxyRS(w, h);
-        mRender.init(RS, getResources());
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        if (mRender != null) mRender.destroy();
+        GalaxyRS render = new GalaxyRS(w, h);
+        render.init(RS, getResources());
     }
 }
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 7bfa81ed..413caab 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -25,7 +25,6 @@
 using namespace android;
 using namespace android::renderscript;
 
-Context * Context::gCon = NULL;
 pthread_key_t Context::gThreadTLSKey = 0;
 
 void Context::initEGL()
@@ -84,6 +83,9 @@
 
 bool Context::runRootScript()
 {
+#if RS_LOG_TIMES
+    timerSet(RS_TIMER_CLEAR_SWAP);
+#endif
     rsAssert(mRootScript->mEnviroment.mIsRoot);
 
     //glColor4f(1,1,1,1);
@@ -102,24 +104,55 @@
     glClear(GL_DEPTH_BUFFER_BIT);
 
 #if RS_LOG_TIMES
-    struct timespec startTime;
-    clock_gettime(CLOCK_MONOTONIC, &startTime);
+    timerSet(RS_TIMER_SCRIPT);
 #endif
     bool ret = runScript(mRootScript.get(), 0);
-
-#if RS_LOG_TIMES
-    struct timespec endTime;
-    clock_gettime(CLOCK_MONOTONIC, &endTime);
-
-    uint64_t t1 = endTime.tv_nsec + ((uint64_t)endTime.tv_sec * 1000 * 1000 * 1000);
-    uint64_t t2 = startTime.tv_nsec + ((uint64_t)startTime.tv_sec * 1000 * 1000 * 1000);
-    int t3 = (int)((t1 - t2) / 1000 / 1000);
-    LOGE("times  %i", t3);
-#endif
-
     return ret;
 }
 
+uint64_t Context::getTime() const
+{
+    struct timespec t;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+void Context::timerReset()
+{
+    for (int ct=0; ct < _RS_TIMER_TOTAL; ct++) {
+        mTimers[ct] = 0;
+    }
+}
+
+void Context::timerInit()
+{
+    mTimeLast = getTime();
+    mTimerActive = RS_TIMER_INTERNAL;
+    timerReset();
+}
+
+void Context::timerSet(Timers tm)
+{
+    uint64_t last = mTimeLast;
+    mTimeLast = getTime();
+    mTimers[mTimerActive] += mTimeLast - last;
+    mTimerActive = tm;
+}
+
+void Context::timerPrint()
+{
+    double total = 0;
+    for (int ct = 0; ct < _RS_TIMER_TOTAL; ct++) {
+        total += mTimers[ct];
+    }
+
+    LOGV("RS Time Data: Idle %2.1f (%lli),  Internal %2.1f (%lli),  Script %2.1f (%lli),  Clear & Swap %2.1f (%lli)",
+         100.0 * mTimers[RS_TIMER_IDLE] / total, mTimers[RS_TIMER_IDLE] / 1000000,
+         100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000,
+         100.0 * mTimers[RS_TIMER_SCRIPT] / total, mTimers[RS_TIMER_SCRIPT] / 1000000,
+         100.0 * mTimers[RS_TIMER_CLEAR_SWAP] / total, mTimers[RS_TIMER_CLEAR_SWAP] / 1000000);
+}
+
 void Context::setupCheck()
 {
     if (mFragmentStore.get()) {
@@ -168,9 +201,19 @@
 
          if (mDraw) {
              mDraw = rsc->runRootScript();
+#if RS_LOG_TIMES
+             rsc->timerSet(RS_TIMER_CLEAR_SWAP);
+#endif
              eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
+#if RS_LOG_TIMES
+             rsc->timerSet(RS_TIMER_INTERNAL);
+             rsc->timerPrint();
+             rsc->timerReset();
+#endif
          }
-         rsc->objDestroyOOBRun();
+         if (rsc->mObjDestroy.mNeedToEmpty) {
+             rsc->objDestroyOOBRun();
+         }
      }
 
      glClearColor(0,0,0,0);
@@ -188,9 +231,6 @@
     mRunning = false;
     mExit = false;
 
-    // see comment in header
-    gCon = this;
-
     int status;
     pthread_attr_t threadAttr;
 
@@ -213,6 +253,7 @@
     mWndSurface = sur;
 
     objDestroyOOBInit();
+    timerInit();
 
     LOGV("RS Launching thread");
     status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 52901b2..ca67e40 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -72,8 +72,6 @@
 
     ScriptCState mScriptC;
 
-    static Context * getContext() {return gCon;}
-
     void swapBuffers();
     void setRootScript(Script *);
     void setVertex(ProgramVertex *);
@@ -120,6 +118,20 @@
     ThreadIO mIO;
     void objDestroyAdd(ObjectBase *);
 
+    // Timers
+    enum Timers {
+        RS_TIMER_IDLE,
+        RS_TIMER_INTERNAL,
+        RS_TIMER_SCRIPT,
+        RS_TIMER_CLEAR_SWAP,
+        _RS_TIMER_TOTAL
+    };
+    uint64_t getTime() const;
+    void timerInit();
+    void timerReset();
+    void timerSet(Timers);
+    void timerPrint();
+
 protected:
     Device *mDev;
 
@@ -164,13 +176,15 @@
 
     static void * threadProc(void *);
 
-    // todo: put in TLS
-    static Context *gCon;
     Surface *mWndSurface;
 
     Vector<ObjectBase *> mNames;
     KeyedVector<String8,int> mInt32Defines;
     KeyedVector<String8,float> mFloatDefines;
+
+    uint64_t mTimers[_RS_TIMER_TOTAL];
+    Timers mTimerActive;
+    uint64_t mTimeLast;
 };
 
 
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index 20b0a94..4a1dbbb 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -32,12 +32,18 @@
 
 bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand)
 {
-    uint32_t cmdID = 0;
-    uint32_t cmdSize = 0;
     bool ret = false;
     while(!mToCore.isEmpty() || waitForCommand) {
+        uint32_t cmdID = 0;
+        uint32_t cmdSize = 0;
         ret = true;
+#if RS_LOG_TIMES
+        con->timerSet(Context::RS_TIMER_IDLE);
+#endif
         const void * data = mToCore.get(&cmdID, &cmdSize);
+#if RS_LOG_TIMES
+        con->timerSet(Context::RS_TIMER_INTERNAL);
+#endif
         waitForCommand = false;
         //LOGV("playCoreCommands 3 %i %i", cmdID, cmdSize);
 
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 419574c..fd54e35 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -730,14 +730,6 @@
     return owner;
 }
 
-
-void LayerBaseClient::Surface::getSurfaceData(
-        ISurfaceFlingerClient::surface_data_t* params) const 
-{
-    params->token = mToken;
-    params->identity = mIdentity;
-}
-
 status_t LayerBaseClient::Surface::onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index 65bf55b..7791941 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -321,10 +321,9 @@
     class Surface : public BnSurface 
     {
     public:
+        int32_t getToken() const { return mToken; }
+        int32_t getIdentity() const { return mIdentity; }
         
-        virtual void getSurfaceData(
-                ISurfaceFlingerClient::surface_data_t* params) const;
-
     protected:
         Surface(const sp<SurfaceFlinger>& flinger, 
                 SurfaceID id, int identity, 
diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp
index 5e74451..dd61e1a 100644
--- a/libs/surfaceflinger/LayerBitmap.cpp
+++ b/libs/surfaceflinger/LayerBitmap.cpp
@@ -97,11 +97,9 @@
     err = allocator.alloc(w, h, format, usage, &handle, &stride);
     
     if (err == NO_ERROR) {
-        if (err == NO_ERROR) {
-            width  = w;
-            height = h;
-            mVStride = 0;
-        }
+        width  = w;
+        height = h;
+        mVStride = 0;
     }
 
     return err;
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 102899c..3824024 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -1239,9 +1239,11 @@
     switch (flags & eFXSurfaceMask) {
         case eFXSurfaceNormal:
             if (UNLIKELY(flags & ePushBuffers)) {
-                layer = createPushBuffersSurfaceLocked(client, d, id, w, h, flags);
+                layer = createPushBuffersSurfaceLocked(client, d, id,
+                        w, h, flags);
             } else {
-                layer = createNormalSurfaceLocked(client, d, id, w, h, format, flags);
+                layer = createNormalSurfaceLocked(client, d, id,
+                        w, h, flags, format);
             }
             break;
         case eFXSurfaceBlur:
@@ -1255,8 +1257,13 @@
     if (layer != 0) {
         setTransactionFlags(eTransactionNeeded);
         surfaceHandle = layer->getSurface();
-        if (surfaceHandle != 0)
-            surfaceHandle->getSurfaceData(params);
+        if (surfaceHandle != 0) { 
+            params->token = surfaceHandle->getToken();
+            params->identity = surfaceHandle->getIdentity();
+            params->width = w;
+            params->height = h;
+            params->format = format;
+        }
     }
 
     return surfaceHandle;
@@ -1264,7 +1271,8 @@
 
 sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
         const sp<Client>& client, DisplayID display,
-        int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)
+        int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+        PixelFormat& format)
 {
     // initialize the surfaces
     switch (format) { // TODO: take h/w into account
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index 2569a0f..56ea97a 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -195,8 +195,8 @@
 
     sp<LayerBaseClient> createNormalSurfaceLocked(
             const sp<Client>& client, DisplayID display,
-            int32_t id, uint32_t w, uint32_t h, 
-            PixelFormat format, uint32_t flags);
+            int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+            PixelFormat& format);
 
     sp<LayerBaseClient> createBlurSurfaceLocked(
             const sp<Client>& client, DisplayID display,
diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp
index 51e8422..4a6a1d7 100644
--- a/libs/ui/ISurfaceFlingerClient.cpp
+++ b/libs/ui/ISurfaceFlingerClient.cpp
@@ -189,8 +189,11 @@
 
 status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel)
 {
-    token = parcel.readInt32();
-    identity  = parcel.readInt32();
+    token    = parcel.readInt32();
+    identity = parcel.readInt32();
+    width    = parcel.readInt32();
+    height   = parcel.readInt32();
+    format   = parcel.readInt32();
     return NO_ERROR;
 }
 
@@ -198,6 +201,9 @@
 {
     parcel->writeInt32(token);
     parcel->writeInt32(identity);
+    parcel->writeInt32(width);
+    parcel->writeInt32(height);
+    parcel->writeInt32(format);
     return NO_ERROR;
 }
 
diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp
index f6792c4..36a10cf 100644
--- a/libs/ui/Surface.cpp
+++ b/libs/ui/Surface.cpp
@@ -64,11 +64,16 @@
 {
     // we own the handle in this case
     width  = data.readInt32();
-    height = data.readInt32();
-    stride = data.readInt32();
-    format = data.readInt32();
-    usage  = data.readInt32();
-    handle = data.readNativeHandle();
+    if (width < 0) {
+        width = height = stride = format = usage = 0;
+        handle = 0;
+    } else {
+        height = data.readInt32();
+        stride = data.readInt32();
+        format = data.readInt32();
+        usage  = data.readInt32();
+        handle = data.readNativeHandle();
+    }
 }
 
 SurfaceBuffer::~SurfaceBuffer()
@@ -108,16 +113,25 @@
 status_t SurfaceBuffer::writeToParcel(Parcel* reply, 
         android_native_buffer_t const* buffer)
 {
-    if (buffer == NULL) {
+    if (buffer == NULL)
         return BAD_VALUE;
+
+    if (buffer->width < 0 || buffer->height < 0)
+        return BAD_VALUE;
+
+    status_t err = NO_ERROR;
+    if (buffer->handle == NULL) {
+        // this buffer doesn't have a handle
+        reply->writeInt32(NO_MEMORY);
+    } else {
+        reply->writeInt32(buffer->width);
+        reply->writeInt32(buffer->height);
+        reply->writeInt32(buffer->stride);
+        reply->writeInt32(buffer->format);
+        reply->writeInt32(buffer->usage);
+        err = reply->writeNativeHandle(buffer->handle);
     }
-    reply->writeInt32(buffer->width);
-    reply->writeInt32(buffer->height);
-    reply->writeInt32(buffer->stride);
-    reply->writeInt32(buffer->format);
-    reply->writeInt32(buffer->usage);
-    reply->writeNativeHandle(buffer->handle);
-    return NO_ERROR;
+    return err;
 }
 
 // ----------------------------------------------------------------------
@@ -183,7 +197,8 @@
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)
     : mClient(client), mSurface(surface),
       mToken(data.token), mIdentity(data.identity),
-      mWidth(w), mHeight(h), mFormat(format), mFlags(flags)
+      mWidth(data.width), mHeight(data.height), mFormat(data.format),
+      mFlags(flags)
 {
 }
         
@@ -434,7 +449,7 @@
     // this is a client-side operation, the surface is destroyed, unmap
     // its buffers in this process.
     for (int i=0 ; i<2 ; i++) {
-        if (mBuffers[i] != 0) {
+        if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) {
             getBufferMapper().unregisterBuffer(mBuffers[i]->handle);
         }
     }
@@ -590,17 +605,24 @@
     if ((back->flags & surface_info_t::eNeedNewBuffer) || mUsageChanged) {
         mUsageChanged = false;
         err = getBufferLocked(backIdx, mUsage);
+        if (err == NO_ERROR) {
+            // reset the width/height with the what we get from the buffer
+            const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]);
+            mWidth  = uint32_t(backBuffer->width);
+            mHeight = uint32_t(backBuffer->height);
+        }
     }
 
     if (err == NO_ERROR) {
         const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]);
-        // reset the width/height with the what we get from the buffer
-        mWidth  = uint32_t(backBuffer->width);
-        mHeight = uint32_t(backBuffer->height);
-        mDirtyRegion.set(backBuffer->width, backBuffer->height);
-        *buffer = backBuffer.get();
+        if (backBuffer != 0) {
+            mDirtyRegion.set(backBuffer->width, backBuffer->height);
+            *buffer = backBuffer.get();
+        } else {
+            err = NO_MEMORY;
+        }
     }
-  
+
     return err;
 }
 
@@ -716,7 +738,8 @@
             } else {
                 newDirtyRegion.andSelf(bounds);
                 const sp<SurfaceBuffer>& frontBuffer(mBuffers[1-mBackbufferIndex]);
-                if (backBuffer->width  == frontBuffer->width && 
+                if (frontBuffer !=0 &&
+                    backBuffer->width  == frontBuffer->width && 
                     backBuffer->height == frontBuffer->height &&
                     !(lcblk->flags & eNoCopyBack)) 
                 {
@@ -788,18 +811,24 @@
     if (s == 0) return NO_INIT;
 
     status_t err = NO_MEMORY;
+
+    // free the current buffer
+    sp<SurfaceBuffer>& currentBuffer(mBuffers[index]);
+    if (currentBuffer != 0) {
+        getBufferMapper().unregisterBuffer(currentBuffer->handle);
+        currentBuffer.clear();
+    }
+
     sp<SurfaceBuffer> buffer = s->getBuffer(usage);
     LOGE_IF(buffer==0, "ISurface::getBuffer() returned NULL");
-    if (buffer != 0) {
-        sp<SurfaceBuffer>& currentBuffer(mBuffers[index]);
-        if (currentBuffer != 0) {
-            getBufferMapper().unregisterBuffer(currentBuffer->handle);
-            currentBuffer.clear();
-        }
-        err = getBufferMapper().registerBuffer(buffer->handle);
-        LOGW_IF(err, "registerBuffer(...) failed %d (%s)", err, strerror(-err));
-        if (err == NO_ERROR) {
-            currentBuffer = buffer;
+    if (buffer != 0) { // this should never happen by construction
+        if (buffer->handle != NULL) { 
+            err = getBufferMapper().registerBuffer(buffer->handle);
+            LOGW_IF(err, "registerBuffer(...) failed %d (%s)",
+                    err, strerror(-err));
+            if (err == NO_ERROR) {
+                currentBuffer = buffer;
+            }
         }
     }
     return err; 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 05b68d4..0a1b142 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1391,21 +1391,26 @@
             } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
                 int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
                                                BluetoothHeadset.STATE_ERROR);
-                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothIntent.DEVICE);
-                String address = btDevice.getAddress();
                 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
-                int btClass = btDevice.getBluetoothClass();
-                if (BluetoothClass.Device.Major.getDeviceMajor(btClass) == BluetoothClass.Device.Major.AUDIO_VIDEO) {
-                    switch (BluetoothClass.Device.getDevice(btClass)) {
-                    case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
-                    case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
-                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
-                        break;
-                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-                        break;
-                    default:
-                        break;
+                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothIntent.DEVICE);
+                String address = null;
+                int btClass = BluetoothClass.ERROR;
+                if (btDevice != null) {
+                    address = btDevice.getAddress();
+                    btClass = btDevice.getBluetoothClass();
+                    if (BluetoothClass.Device.Major.getDeviceMajor(btClass) ==
+                                BluetoothClass.Device.Major.AUDIO_VIDEO) {
+                        switch (BluetoothClass.Device.getDevice(btClass)) {
+                        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+                        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+                            break;
+                        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+                            break;
+                        default:
+                            break;
+                        }
                     }
                 }
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 662d5fb..894d46c 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -19,9 +19,6 @@
 
 #include <arpa/inet.h>
 
-#undef NDEBUG
-#include <assert.h>
-
 #include <ctype.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -31,6 +28,7 @@
 #include <media/stagefright/MPEG4Extractor.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/SampleTable.h>
@@ -70,10 +68,8 @@
     MediaBufferGroup *mGroup;
 
     MediaBuffer *mBuffer;
-    size_t mBufferOffset;
-    size_t mBufferSizeRemaining;
 
-    bool mNeedsNALFraming;
+    bool mWantsNALFragments;
 
     uint8_t *mSrcBuffer;
 
@@ -138,7 +134,7 @@
             return "video/avc";
 
         default:
-            assert(!"should not be here.");
+            CHECK(!"should not be here.");
             return NULL;
     }
 }
@@ -279,7 +275,7 @@
                     return err;
                 }
             }
-            assert(*offset == stop_offset);
+            CHECK_EQ(*offset, stop_offset);
 
             if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mHaveMetadata = true;
@@ -291,7 +287,7 @@
 
         case FOURCC('t', 'k', 'h', 'd'):
         {
-            assert(chunk_data_size >= 4);
+            CHECK(chunk_data_size >= 4);
 
             uint8_t version;
             if (mDataSource->read_at(data_offset, &version, 1) < 1) {
@@ -444,7 +440,7 @@
             }
 
             uint8_t buffer[8];
-            assert(chunk_data_size >= (off_t)sizeof(buffer));
+            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
             if (mDataSource->read_at(
                         data_offset, buffer, 8) < 8) {
                 return ERROR_IO;
@@ -470,7 +466,7 @@
                     return err;
                 }
             }
-            assert(*offset == stop_offset);
+            CHECK_EQ(*offset, stop_offset);
             break;
         }
 
@@ -518,7 +514,7 @@
                     return err;
                 }
             }
-            assert(*offset == stop_offset);
+            CHECK_EQ(*offset, stop_offset);
             break;
         }
 
@@ -560,7 +556,7 @@
                     return err;
                 }
             }
-            assert(*offset == stop_offset);
+            CHECK_EQ(*offset, stop_offset);
             break;
         }
 
@@ -728,16 +724,14 @@
       mStarted(false),
       mGroup(NULL),
       mBuffer(NULL),
-      mBufferOffset(0),
-      mBufferSizeRemaining(0),
-      mNeedsNALFraming(false),
+      mWantsNALFragments(false),
       mSrcBuffer(NULL) {
     const char *mime;
     bool success = mFormat->findCString(kKeyMIMEType, &mime);
-    assert(success);
+    CHECK(success);
 
     success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
-    assert(success);
+    CHECK(success);
 
     mIsAVC = !strcasecmp(mime, "video/avc");
 }
@@ -749,21 +743,21 @@
 }
 
 status_t MPEG4Source::start(MetaData *params) {
-    assert(!mStarted);
+    CHECK(!mStarted);
 
     int32_t val;
-    if (mIsAVC && params && params->findInt32(kKeyNeedsNALFraming, &val)
+    if (params && params->findInt32(kKeyWantsNALFragments, &val)
         && val != 0) {
-        mNeedsNALFraming = true;
+        mWantsNALFragments = true;
     } else {
-        mNeedsNALFraming = false;
+        mWantsNALFragments = false;
     }
 
     mGroup = new MediaBufferGroup;
 
     size_t max_size;
     status_t err = mSampleTable->getMaxSampleSize(&max_size);
-    assert(err == OK);
+    CHECK_EQ(err, OK);
 
     // Assume that a given buffer only contains at most 10 fragments,
     // each fragment originally prefixed with a 2 byte length will
@@ -779,7 +773,7 @@
 }
 
 status_t MPEG4Source::stop() {
-    assert(mStarted);
+    CHECK(mStarted);
 
     if (mBuffer != NULL) {
         mBuffer->release();
@@ -804,7 +798,7 @@
 
 status_t MPEG4Source::read(
         MediaBuffer **out, const ReadOptions *options) {
-    assert(mStarted);
+    CHECK(mStarted);
 
     *out = NULL;
 
@@ -830,38 +824,124 @@
 
     off_t offset;
     size_t size;
-    status_t err = mSampleTable->getSampleOffsetAndSize(
-            mCurrentSampleIndex, &offset, &size);
-
-    if (err != OK) {
-        return err;
-    }
-
     uint32_t dts;
-    err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+    bool newBuffer = false;
+    if (mBuffer == NULL) {
+        newBuffer = true;
 
-    if (err != OK) {
-        return err;
+        status_t err = mSampleTable->getSampleOffsetAndSize(
+                mCurrentSampleIndex, &offset, &size);
+
+        if (err != OK) {
+            return err;
+        }
+
+        err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+
+        if (err != OK) {
+            return err;
+        }
+
+        err = mGroup->acquire_buffer(&mBuffer);
+        if (err != OK) {
+            CHECK_EQ(mBuffer, NULL);
+            return err;
+        }
     }
 
-    err = mGroup->acquire_buffer(&mBuffer);
-    if (err != OK) {
-        assert(mBuffer == NULL);
-        return err;
-    }
+    if (!mIsAVC || mWantsNALFragments) {
+        if (newBuffer) {
+            ssize_t num_bytes_read =
+                mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
 
-    if (!mIsAVC || !mNeedsNALFraming) {
+            if (num_bytes_read < (ssize_t)size) {
+                mBuffer->release();
+                mBuffer = NULL;
+
+                return ERROR_IO;
+            }
+
+            mBuffer->set_range(0, size);
+            mBuffer->meta_data()->clear();
+            mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+            mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+            ++mCurrentSampleIndex;
+        }
+
+        if (!mIsAVC) {
+            *out = mBuffer;
+            mBuffer = NULL;
+
+            return OK;
+        }
+
+        // Each NAL unit is split up into its constituent fragments and
+        // each one of them returned in its own buffer.
+
+        CHECK(mBuffer->range_length() >= 2);
+
+        const uint8_t *src =
+            (const uint8_t *)mBuffer->data() + mBuffer->range_offset();
+
+        size_t nal_size = U16_AT(src);
+
+        CHECK(mBuffer->range_length() >= 2 + nal_size);
+
+        MediaBuffer *clone = mBuffer->clone();
+        clone->set_range(mBuffer->range_offset() + 2, nal_size);
+
+        mBuffer->set_range(
+                mBuffer->range_offset() + 2 + nal_size,
+                mBuffer->range_length() - 2 - nal_size);
+
+        if (mBuffer->range_length() == 0) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        *out = clone;
+
+        return OK;
+    } else {
+        // Whole NAL units are returned but each fragment is prefixed by
+        // the start code (0x00 00 00 01).
+
         ssize_t num_bytes_read =
-            mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
+            mDataSource->read_at(offset, mSrcBuffer, size);
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
             mBuffer = NULL;
 
-            return err;
+            return ERROR_IO;
         }
 
-        mBuffer->set_range(0, size);
+        uint8_t *dstData = (uint8_t *)mBuffer->data();
+        size_t srcOffset = 0;
+        size_t dstOffset = 0;
+        while (srcOffset < size) {
+            CHECK(srcOffset + 1 < size);
+            size_t nalLength =
+                (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
+            CHECK(srcOffset + 1 + nalLength < size);
+            srcOffset += 2;
+
+            if (nalLength == 0) {
+                continue;
+            }
+
+            CHECK(dstOffset + 4 <= mBuffer->size());
+
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 1;
+            memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+            srcOffset += nalLength;
+            dstOffset += nalLength;
+        }
+
+        mBuffer->set_range(0, dstOffset);
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
         mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
@@ -872,52 +952,6 @@
 
         return OK;
     }
-
-    ssize_t num_bytes_read =
-        mDataSource->read_at(offset, mSrcBuffer, size);
-
-    if (num_bytes_read < (ssize_t)size) {
-        mBuffer->release();
-        mBuffer = NULL;
-
-        return err;
-    }
-
-    uint8_t *dstData = (uint8_t *)mBuffer->data();
-    size_t srcOffset = 0;
-    size_t dstOffset = 0;
-    while (srcOffset < size) {
-        assert(srcOffset + 1 < size);
-        size_t nalLength =
-            (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
-        assert(srcOffset + 1 + nalLength < size);
-        srcOffset += 2;
-
-        if (nalLength == 0) {
-            continue;
-        }
-
-        assert(dstOffset + 4 <= mBuffer->size());
-
-        dstData[dstOffset++] = 0;
-        dstData[dstOffset++] = 0;
-        dstData[dstOffset++] = 0;
-        dstData[dstOffset++] = 1;
-        memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
-        srcOffset += nalLength;
-        dstOffset += nalLength;
-    }
-
-    mBuffer->set_range(0, dstOffset);
-    mBuffer->meta_data()->clear();
-    mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
-    mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
-    ++mCurrentSampleIndex;
-
-    *out = mBuffer;
-    mBuffer = NULL;
-
-    return OK;
 }
 
 bool SniffMPEG4(
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 35d599c..ec9f6d3 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MmapSource.h>
 #include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
 #include <utils/Vector.h>
 
 #include <OMX_Audio.h>
@@ -116,6 +117,39 @@
     return NULL;
 }
 
+enum {
+    kAVCProfileBaseline      = 0x42,
+    kAVCProfileMain          = 0x4d,
+    kAVCProfileExtended      = 0x58,
+    kAVCProfileHigh          = 0x64,
+    kAVCProfileHigh10        = 0x6e,
+    kAVCProfileHigh422       = 0x7a,
+    kAVCProfileHigh444       = 0xf4,
+    kAVCProfileCAVLC444Intra = 0x2c
+};
+
+static const char *AVCProfileToString(uint8_t profile) {
+    switch (profile) {
+        case kAVCProfileBaseline:
+            return "Baseline";
+        case kAVCProfileMain:
+            return "Main";
+        case kAVCProfileExtended:
+            return "Extended";
+        case kAVCProfileHigh:
+            return "High";
+        case kAVCProfileHigh10:
+            return "High 10";
+        case kAVCProfileHigh422:
+            return "High 422";
+        case kAVCProfileHigh444:
+            return "High 444";
+        case kAVCProfileCAVLC444Intra:
+            return "CAVLC 444 Intra";
+        default:   return "Unknown";
+    }
+}
+
 // static
 sp<OMXCodec> OMXCodec::Create(
         const sp<IOMX> &omx,
@@ -152,7 +186,7 @@
 
     uint32_t quirks = 0;
     if (!strcmp(componentName, "OMX.PV.avcdec")) {
-        quirks |= kWantsRawNALFrames;
+        quirks |= kWantsNALFragments;
     }
     if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
         quirks |= kNeedsFlushBeforeDisable;
@@ -189,29 +223,72 @@
     } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
         printf("found avcc of size %d\n", size);
 
-        const uint8_t *ptr = (const uint8_t *)data + 6;
+        // Parse the AVCDecoderConfigurationRecord
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+
+        CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+        size_t lengthSize = 1 + (ptr[4] & 3);
+
+        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+        // violates it...
+        // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+        size_t numSeqParameterSets = ptr[5] & 31;
+
+        ptr += 6;
         size -= 6;
-        while (size >= 2) {
-            size_t length = ptr[0] << 8 | ptr[1];
+
+        for (size_t i = 0; i < numSeqParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
 
             ptr += 2;
             size -= 2;
 
-            // printf("length = %d, size = %d\n", length, size);
-
             CHECK(size >= length);
 
             codec->addCodecSpecificData(ptr, length);
 
             ptr += length;
             size -= length;
+        }
 
-            if (size <= 1) {
-                break;
-            }
+        CHECK(size >= 1);
+        size_t numPictureParameterSets = *ptr;
+        ++ptr;
+        --size;
 
-            ptr++;  // XXX skip trailing 0x01 byte???
-            --size;
+        for (size_t i = 0; i < numPictureParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            codec->addCodecSpecificData(ptr, length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        LOGI("AVC profile = %d (%s), level = %d",
+             (int)profile, AVCProfileToString(profile), (int)level / 10);
+
+        if (!strcmp(componentName, "OMX.TI.Video.Decoder")
+            && (profile != kAVCProfileBaseline || level > 39)) {
+            // This stream exceeds the decoder's capabilities.
+
+            LOGE("Profile and/or level exceed the decoder's capabilities.");
+            return NULL;
         }
     }
 
@@ -225,7 +302,7 @@
         int32_t width, height;
         bool success = meta->findInt32(kKeyWidth, &width);
         success = success && meta->findInt32(kKeyHeight, &height);
-        assert(success);
+        CHECK(success);
 
         if (createEncoder) {
             codec->setVideoInputFormat(mime, width, height);
@@ -244,9 +321,16 @@
         int32_t width, height;
         bool success = meta->findInt32(kKeyWidth, &width);
         success = success && meta->findInt32(kKeyHeight, &height);
-        assert(success);
+
+        int32_t compressedSize;
+        success = success && meta->findInt32(
+                kKeyCompressedSize, &compressedSize);
+
+        CHECK(success);
+        CHECK(compressedSize > 0);
 
         codec->setImageOutputFormat(format, width, height);
+        codec->setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
     }
 
     codec->initOutputFormat(meta);
@@ -278,7 +362,7 @@
         }
 
         // The following assertion is violated by TI's video decoder.
-        // assert(format.nIndex == index);
+        // CHECK_EQ(format.nIndex, index);
 
 #if 1
         LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
@@ -529,7 +613,7 @@
 
 OMXCodec::OMXCodec(
         const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
-        bool isEncoder, 
+        bool isEncoder,
         const char *mime,
         const char *componentName,
         const sp<MediaSource> &source)
@@ -541,7 +625,6 @@
       mComponentName(strdup(componentName)),
       mSource(source),
       mCodecSpecificDataIndex(0),
-      mDealer(new MemoryDealer(5 * 1024 * 1024)),
       mState(LOADED),
       mSignalledEOS(false),
       mNoMoreOutputData(false),
@@ -554,7 +637,7 @@
 }
 
 OMXCodec::~OMXCodec() {
-    CHECK_EQ(mState, LOADED);
+    CHECK(mState == LOADED || mState == ERROR);
 
     status_t err = mOMX->observe_node(mNode, NULL);
     CHECK_EQ(err, OK);
@@ -569,7 +652,7 @@
 
     free(mComponentName);
     mComponentName = NULL;
-    
+
     free(mMIME);
     mMIME = NULL;
 }
@@ -639,8 +722,11 @@
         return err;
     }
 
+    size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+    mDealer[portIndex] = new MemoryDealer(totalSize);
+
     for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
-        sp<IMemory> mem = mDealer->allocate(def.nBufferSize);
+        sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
         CHECK(mem.get() != NULL);
 
         IOMX::buffer_id buffer;
@@ -772,7 +858,7 @@
                 mBufferFilled.signal();
             } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
                 CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
-                
+
                 MediaBuffer *buffer = info->mMediaBuffer;
 
                 buffer->set_range(
@@ -1149,8 +1235,8 @@
 
         size_t size = specific->mSize;
 
-        if (!strcasecmp(mMIME, "video/avc")
-            && !(mQuirks & kWantsRawNALFrames)) {
+        if (!strcasecmp("video/avc", mMIME)
+                && !(mQuirks & kWantsNALFragments)) {
             static const uint8_t kNALStartCode[4] =
                     { 0x00, 0x00, 0x00, 0x01 };
 
@@ -1383,7 +1469,7 @@
     CHECK_EQ(def.eDomain, OMX_PortDomainImage);
 
     OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
-    
+
     CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
     imageDef->eColorFormat = format;
     imageDef->nFrameWidth = width;
@@ -1414,23 +1500,33 @@
             break;
     }
 
+    def.nBufferCountActual = def.nBufferCountMin;
+
     err = mOMX->set_parameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     CHECK_EQ(err, OK);
+}
 
-    ////
-
+void OMXCodec::setJPEGInputFormat(
+        OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
     def.nPortIndex = kPortIndexInput;
 
-    err = mOMX->get_parameter(
+    status_t err = mOMX->get_parameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     CHECK_EQ(err, OK);
 
+    CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+    OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
     CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingJPEG);
     imageDef->nFrameWidth = width;
     imageDef->nFrameHeight = height;
 
-    def.nBufferSize = 128 * 1024;
+    def.nBufferSize = compressedSize;
     def.nBufferCountActual = def.nBufferCountMin;
 
     err = mOMX->set_parameter(
@@ -1460,10 +1556,10 @@
     if (mState != LOADED) {
         return UNKNOWN_ERROR;
     }
-    
+
     sp<MetaData> params = new MetaData;
-    if (!strcasecmp(mMIME, "video/avc") && !(mQuirks & kWantsRawNALFrames)) {
-        params->setInt32(kKeyNeedsNALFraming, true);
+    if (mQuirks & kWantsNALFragments) {
+        params->setInt32(kKeyWantsNALFragments, true);
     }
     status_t err = mSource->start(params.get());
 
@@ -1481,7 +1577,7 @@
 }
 
 status_t OMXCodec::stop() {
-    LOGI("stop");
+    LOGV("stop");
 
     Mutex::Autolock autoLock(mLock);
 
@@ -1630,7 +1726,7 @@
         "OMX_COLOR_Format16bitBGR565",
         "OMX_COLOR_Format18bitRGB666",
         "OMX_COLOR_Format18bitARGB1665",
-        "OMX_COLOR_Format19bitARGB1666", 
+        "OMX_COLOR_Format19bitARGB1666",
         "OMX_COLOR_Format24bitRGB888",
         "OMX_COLOR_Format24bitBGR888",
         "OMX_COLOR_Format24bitARGB1887",
@@ -1653,11 +1749,11 @@
         "OMX_COLOR_FormatRawBayer8bit",
         "OMX_COLOR_FormatRawBayer10bit",
         "OMX_COLOR_FormatRawBayer8bitcompressed",
-        "OMX_COLOR_FormatL2", 
-        "OMX_COLOR_FormatL4", 
-        "OMX_COLOR_FormatL8", 
-        "OMX_COLOR_FormatL16", 
-        "OMX_COLOR_FormatL24", 
+        "OMX_COLOR_FormatL2",
+        "OMX_COLOR_FormatL4",
+        "OMX_COLOR_FormatL8",
+        "OMX_COLOR_FormatL16",
+        "OMX_COLOR_FormatL24",
         "OMX_COLOR_FormatL32",
         "OMX_COLOR_FormatYUV420PackedSemiPlanar",
         "OMX_COLOR_FormatYUV422PackedSemiPlanar",
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
index 94cca43..cf08fa5 100644
--- a/media/libstagefright/OMXDecoder.cpp
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -139,7 +139,7 @@
 
     uint32_t quirks = 0;
     if (!strcmp(codec, "OMX.PV.avcdec")) {
-        quirks |= kWantsRawNALFrames;
+        quirks |= kWantsNALFragments;
     }
     if (!strcmp(codec, "OMX.TI.AAC.decode")
         || !strcmp(codec, "OMX.TI.MP3.decode")) {
@@ -274,8 +274,8 @@
     // mDealer->dump("Decoder Dealer");
 
     sp<MetaData> params = new MetaData;
-    if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
-        params->setInt32(kKeyNeedsNALFraming, true);
+    if (mQuirks & kWantsNALFragments) {
+        params->setInt32(kKeyWantsNALFragments, true);
     }
 
     status_t err = mSource->start(params.get());
@@ -1331,7 +1331,7 @@
 
         size_t range_length = 0;
 
-        if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
+        if (mIsAVC && !(mQuirks & kWantsNALFragments)) {
             assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
 
             memcpy(mem->pointer(), kNALStartCode, 4);
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index f569595..1b66662 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -897,6 +897,11 @@
             if (lower < 0) {
                 lower += 256;
             }
+            // If upper and lower both equal 0, it should be the end of string.
+            // Ignore left bytes from array to avoid potential issues
+            if (upper == 0 && lower == 0) {
+                return new String(c, 0, i);
+            }
 
             c[i] = (char)((upper << 8) | lower);
         }
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index 423d5a7..675272d 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -168,7 +168,7 @@
             } else {
                 response = validateResponseCode(mListener.onPut(op));
             }
-            if (response != ResponseCodes.OBEX_HTTP_OK) {
+            if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
                 op.sendReply(response);
             } else if (!op.isAborted) {
                 // wait for the final bit
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 0762ebf..9c0f7fd 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -141,7 +141,8 @@
 
                 egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat);
     virtual     ~egl_surface_t();
-    virtual     bool    isValid() const = 0;
+                bool    isValid() const;
+    virtual     bool    initCheck() const = 0;
 
     virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl) = 0;
     virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl) = 0;
@@ -175,6 +176,11 @@
     magic = 0;
     free(depth.data);
 }
+bool egl_surface_t::isValid() const {
+    LOGE_IF(magic != MAGIC, "invalid EGLSurface (%p)", this);
+    return magic == MAGIC; 
+}
+
 EGLBoolean egl_surface_t::swapBuffers() {
     return EGL_FALSE;
 }
@@ -208,9 +214,9 @@
             int32_t depthFormat,
             android_native_window_t* window);
 
-     ~egl_window_surface_v2_t();
+    ~egl_window_surface_v2_t();
 
-    virtual     bool        isValid() const { return nativeWindow->common.magic == ANDROID_NATIVE_WINDOW_MAGIC; }
+    virtual     bool        initCheck() const { return true; } // TODO: report failure if ctor fails
     virtual     EGLBoolean  swapBuffers();
     virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
     virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
@@ -704,7 +710,7 @@
 
     virtual ~egl_pixmap_surface_t() { }
 
-    virtual     bool        isValid() const { return nativePixmap.version == sizeof(egl_native_pixmap_t); }
+    virtual     bool        initCheck() const { return !depth.format || depth.data!=0; } 
     virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
     virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
     virtual     EGLint      getWidth() const    { return nativePixmap.width;  }
@@ -726,7 +732,6 @@
         depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
         if (depth.data == 0) {
             setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-            return;
         }
     }
 }
@@ -768,7 +773,7 @@
 
     virtual ~egl_pbuffer_surface_t();
 
-    virtual     bool        isValid() const { return pbuffer.data != 0; }
+    virtual     bool        initCheck() const   { return pbuffer.data != 0; }
     virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
     virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
     virtual     EGLint      getWidth() const    { return pbuffer.width;  }
@@ -1196,6 +1201,11 @@
     if (!(surfaceType & EGL_WINDOW_BIT))
         return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
 
+    if (static_cast<android_native_window_t*>(window)->common.magic !=
+            ANDROID_NATIVE_WINDOW_MAGIC) {
+        return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+    }
+        
     EGLint configID;
     if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
         return EGL_FALSE;
@@ -1241,7 +1251,7 @@
     surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
             static_cast<android_native_window_t*>(window));
 
-    if (!surface->isValid()) {
+    if (!surface->initCheck()) {
         // there was a problem in the ctor, the error
         // flag has been set.
         delete surface;
@@ -1265,6 +1275,11 @@
     if (!(surfaceType & EGL_PIXMAP_BIT))
         return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
 
+    if (static_cast<egl_native_pixmap_t*>(pixmap)->version != 
+            sizeof(egl_native_pixmap_t)) {
+        return setError(EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE);
+    }
+    
     EGLint configID;
     if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
         return EGL_FALSE;
@@ -1307,7 +1322,7 @@
         new egl_pixmap_surface_t(dpy, config, depthFormat,
                 static_cast<egl_native_pixmap_t*>(pixmap));
 
-    if (!surface->isValid()) {
+    if (!surface->initCheck()) {
         // there was a problem in the ctor, the error
         // flag has been set.
         delete surface;
@@ -1375,7 +1390,7 @@
     egl_surface_t* surface =
         new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat);
 
-    if (!surface->isValid()) {
+    if (!surface->initCheck()) {
         // there was a problem in the ctor, the error
         // flag has been set.
         delete surface;
@@ -1590,7 +1605,7 @@
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     if (eglSurface != EGL_NO_SURFACE) {
         egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
-        if (surface->magic != egl_surface_t::MAGIC)
+        if (!surface->isValid())
             return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (surface->dpy != dpy)
             return setError(EGL_BAD_DISPLAY, EGL_FALSE);
@@ -1610,6 +1625,8 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface);
+    if (!surface->isValid())
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
     if (surface->dpy != dpy)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
@@ -1702,9 +1719,19 @@
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     if (draw) {
         egl_surface_t* s = (egl_surface_t*)draw;
+        if (!s->isValid())
+            return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (s->dpy != dpy)
             return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        // TODO: check that draw and read are compatible with the context
+        // TODO: check that draw is compatible with the context
+    }
+    if (read && read!=draw) {
+        egl_surface_t* s = (egl_surface_t*)read;
+        if (!s->isValid())
+            return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (s->dpy != dpy)
+            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        // TODO: check that read is compatible with the context
     }
 
     EGLContext current_ctx = EGL_NO_CONTEXT;
@@ -1737,7 +1764,8 @@
             egl_surface_t* r = (egl_surface_t*)read;
             
             if (c->draw) {
-                reinterpret_cast<egl_surface_t*>(c->draw)->disconnect();
+                egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
+                s->disconnect();
             }
             if (c->read) {
                 // FIXME: unlock/disconnect the read surface too 
@@ -1860,6 +1888,8 @@
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
     egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+    if (!d->isValid())
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
     if (d->dpy != dpy)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
@@ -2073,6 +2103,8 @@
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
     egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+    if (!d->isValid())
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
     if (d->dpy != dpy)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
@@ -2088,6 +2120,8 @@
         return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
 
     egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+    if (!d->isValid())
+        return setError(EGL_BAD_SURFACE, (EGLClientBuffer)0);
     if (d->dpy != dpy)
         return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 72a1192..df37d35 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -513,7 +513,8 @@
                     mNetRequestersPids[usedNetworkType].add(currentPid);
                 }
 
-                if (ni.isConnectedOrConnecting() == true) {
+                if ((ni.isConnectedOrConnecting() == true) &&
+                        !network.isTeardownRequested()) {
                     if (ni.isConnected() == true) {
                         // add the pid-specific dns
                         handleDnsConfigurationChange();
@@ -686,6 +687,7 @@
                 ++numConnectedNets;
             }
         }
+        if (DBG) Log.d(TAG, "numConnectedNets returning "+numConnectedNets);
         return numConnectedNets;
     }
 
@@ -792,7 +794,8 @@
                 if (newNet.isAvailable()) {
                     NetworkInfo switchTo = newNet.getNetworkInfo();
                     switchTo.setFailover(true);
-                    if (!switchTo.isConnectedOrConnecting()) {
+                    if (!switchTo.isConnectedOrConnecting() ||
+                            newNet.isTeardownRequested()) {
                         newNet.reconnect();
                     }
                     if (DBG) {
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 7597f85..6ac72e0 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -141,8 +141,11 @@
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
         }
-        if (mCurrentVibration != null
-                && mCurrentVibration.hasLongerTimeout(milliseconds)) {
+        // We're running in the system server so we cannot crash. Check for a
+        // timeout of 0 or negative. This will ensure that a vibration has
+        // either a timeout of > 0 or a non-null pattern.
+        if (milliseconds <= 0 || (mCurrentVibration != null
+                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
             // Ignore this vibration since the current vibration will play for
             // longer than milliseconds.
             return;
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 7379b5d..170a9f8 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -535,9 +535,12 @@
             intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
         }
         intent.putExtra(Phone.DATA_APN_KEY, apn);
-        String types = apnTypes[0];
-        for (int i = 1; i < apnTypes.length; i++) {
-            types = types+","+apnTypes[i];
+        String types = new String("");
+        if (apnTypes.length > 0) {
+            types = apnTypes[0];
+            for (int i = 1; i < apnTypes.length; i++) {
+                types = types+","+apnTypes[i];
+            }
         }
         intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
         intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index a00a756..7ca8b52 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -405,6 +405,7 @@
     // If non-null, this is the currently visible window that is associated
     // with the wallpaper.
     WindowState mWallpaperTarget = null;
+    WindowState mUpcomingWallpaperTarget = null;
     int mWallpaperAnimLayerAdjustment;
     
     AppWindowToken mFocusedApp = null;
@@ -1178,60 +1179,136 @@
     boolean adjustWallpaperWindowsLocked() {
         boolean changed = false;
         
+        mUpcomingWallpaperTarget = null;
+        
         // First find top-most window that has asked to be on top of the
         // wallpaper; all wallpapers go behind it.
         final ArrayList localmWindows = mWindows;
         int N = localmWindows.size();
         WindowState w = null;
+        WindowState foundW = null;
+        int foundI = 0;
+        AppWindowToken topToken = null;
+        AppWindowToken behindToken = null;
         int i = N;
-        boolean visible = false;
         while (i > 0) {
             i--;
             w = (WindowState)localmWindows.get(i);
+            if (topToken != null) {
+                if (w.mAppToken == topToken) {
+                    continue;
+                }
+                if (w.mAppToken != null) {
+                    if (behindToken == null) {
+                        // We need to look through for what is behind the
+                        // potential new wallpaper target...  skip all tokens
+                        // that are hidden and not animating, since they can't
+                        // be involved with the transition.
+                        if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+                            continue;
+                        }
+                        behindToken = w.mAppToken;
+                    }
+                    if (w.mAppToken != behindToken) {
+                        break;
+                    }
+                }
+            }
             if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()
                     && !w.mDrawPending && !w.mCommitDrawPending) {
-                visible = true;
+                if (behindToken != null && w.mAppToken == behindToken) {
+                    // We had previously found a wallpaper window that was
+                    // animating, and now we found one behind it.  We could
+                    // be doing an animation between two windows on top of
+                    // the wallpaper!
+                    if (mWallpaperTarget == w || mWallpaperTarget == foundW) {
+                        // Retain the current wallpaper target (don't move
+                        // the wallpaper yet), but note the window that is
+                        // going to become the wallpaper target so that
+                        // others know about this special state.
+                        if (DEBUG_WALLPAPER) Log.v(TAG,
+                                "Switching wallpaper activities: cur#" + i + "="
+                                + w + " upcoming#" + foundI + "=" + foundW);
+                        mUpcomingWallpaperTarget = foundW;
+                        foundW = w;
+                        foundI = i;
+                        break;
+                    }
+                }
+                foundW = w;
+                foundI = i;
+                if (w.mAppToken != null && w.mAppToken.animation != null) {
+                    // If this app token is animating, we want to keep the
+                    // wallpaper below it if it is animating on top of another
+                    // app with a wallpaper.
+                    topToken = w.mAppToken;
+                    continue;
+                }
                 break;
             }
         }
 
-        if (!visible) w = null;
-        if (DEBUG_WALLPAPER && mWallpaperTarget != w) {
-            Log.v(TAG, "New wallpaper target: " + w);
+        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+            // If we are currently waiting for an app transition, and either
+            // the current target or the next target are involved with it,
+            // then hold off on doing anything with the wallpaper.
+            // Note that we are checking here for just whether the target
+            // is part of an app token...  which is potentially overly aggressive
+            // (the app token may not be involved in the transition), but good
+            // enough (we'll just wait until whatever transition is pending
+            // executes).
+            if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) {
+                return false;
+            }
+            if (foundW != null && foundW.mAppToken != null) {
+                return false;
+            }
+            if (mUpcomingWallpaperTarget != null && mUpcomingWallpaperTarget.mAppToken != null) {
+                return false;
+            }
         }
-        mWallpaperTarget = w;
         
+        if (mWallpaperTarget != foundW) {
+            mWallpaperTarget = foundW;
+            if (DEBUG_WALLPAPER) {
+                Log.v(TAG, "New wallpaper target: " + foundW);
+            }
+        }
+        
+        boolean visible = foundW != null;
         if (visible) {
             // The window is visible to the compositor...  but is it visible
             // to the user?  That is what the wallpaper cares about.
-            visible = !w.mObscured;
+            visible = !foundW.mObscured;
             if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper visibility: " + visible);
             
             // If the wallpaper target is animating, we may need to copy
-            // its layer adjustment.
-            mWallpaperAnimLayerAdjustment = w.mAppToken != null
-                    ? w.mAppToken.animLayerAdjustment : 0;
+            // its layer adjustment.  Only do this if we are not transfering
+            // between two wallpaper targets.
+            mWallpaperAnimLayerAdjustment =
+                    (mUpcomingWallpaperTarget == null && foundW.mAppToken != null)
+                    ? foundW.mAppToken.animLayerAdjustment : 0;
             
             // Now w is the window we are supposed to be behind...  but we
             // need to be sure to also be behind any of its attached windows,
             // AND any starting window associated with it.
-            while (i > 0) {
-                WindowState wb = (WindowState)localmWindows.get(i-1);
-                if (wb.mAttachedWindow != w &&
+            while (foundI > 0) {
+                WindowState wb = (WindowState)localmWindows.get(foundI-1);
+                if (wb.mAttachedWindow != foundW &&
                         (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
-                                wb.mToken != w.mToken)) {
+                                wb.mToken != foundW.mToken)) {
                     // This window is not related to the previous one in any
                     // interesting way, so stop here.
                     break;
                 }
-                w = wb;
-                i--;
+                foundW = wb;
+                foundI--;
             }
         }
         
         // Okay i is the position immediately above the wallpaper.  Look at
         // what is below it for later.
-        w = i > 0 ? (WindowState)localmWindows.get(i-1) : null;
+        foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null;
         
         final int dw = mDisplay.getWidth();
         final int dh = mDisplay.getHeight();
@@ -1271,9 +1348,10 @@
                 
                 // First, if this window is at the current index, then all
                 // is well.
-                if (wallpaper == w) {
-                    i--;
-                    w = i > 0 ? (WindowState)localmWindows.get(i-1) : null;
+                if (wallpaper == foundW) {
+                    foundI--;
+                    foundW = foundI > 0
+                            ? (WindowState)localmWindows.get(foundI-1) : null;
                     continue;
                 }
                 
@@ -1283,16 +1361,16 @@
                 int oldIndex = localmWindows.indexOf(wallpaper);
                 if (oldIndex >= 0) {
                     localmWindows.remove(oldIndex);
-                    if (oldIndex < i) {
-                        i--;
+                    if (oldIndex < foundI) {
+                        foundI--;
                     }
                 }
                 
                 // Now stick it in.
                 if (DEBUG_WALLPAPER) Log.v(TAG, "Moving wallpaper " + wallpaper
-                        + " from " + oldIndex + " to " + i);
+                        + " from " + oldIndex + " to " + foundI);
                 
-                localmWindows.add(i, wallpaper);
+                localmWindows.add(foundI, wallpaper);
                 changed = true;
             }
         }
@@ -2246,6 +2324,16 @@
                                 ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
                                 : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
                         break;
+                    case WindowManagerPolicy.TRANSIT_WALLPAPER_ACTIVITY_OPEN:
+                        animAttr = enter
+                                ? com.android.internal.R.styleable.WindowAnimation_wallpaperActivityOpenEnterAnimation
+                                : com.android.internal.R.styleable.WindowAnimation_wallpaperActivityOpenExitAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_WALLPAPER_ACTIVITY_CLOSE:
+                        animAttr = enter
+                                ? com.android.internal.R.styleable.WindowAnimation_wallpaperActivityCloseEnterAnimation
+                                : com.android.internal.R.styleable.WindowAnimation_wallpaperActivityCloseExitAnimation;
+                        break;
                 }
                 a = loadAnimation(lp, animAttr);
                 if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
@@ -6874,7 +6962,8 @@
             
             // Wallpapers are animated based on the "real" window they
             // are currently targeting.
-            if (mAttrs.type == TYPE_WALLPAPER && mWallpaperTarget != null) {
+            if (mAttrs.type == TYPE_WALLPAPER && mUpcomingWallpaperTarget == null
+                    && mWallpaperTarget != null) {
                 if (mWallpaperTarget.mHasLocalTransformation) {
                     attachedTransformation = mWallpaperTarget.mTransformation;
                 }
@@ -7511,7 +7600,7 @@
                 if (w == mInputMethodTarget) {
                     setInputMethodAnimLayerAdjustment(adj);
                 }
-                if (w == mWallpaperTarget) {
+                if (w == mWallpaperTarget && mUpcomingWallpaperTarget == null) {
                     setWallpaperAnimLayerAdjustmentLocked(adj);
                 }
             }
@@ -8636,6 +8725,60 @@
 
                         mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
 
+                        boolean wallpaperMoved = adjustWallpaperWindowsLocked();
+                        if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                "Old wallpaper target=" + mWallpaperTarget
+                                + ", upcoming target=" + mUpcomingWallpaperTarget);
+                        if (mUpcomingWallpaperTarget != mWallpaperTarget &&
+                                mUpcomingWallpaperTarget != null &&
+                                mWallpaperTarget != null) {
+                            // Need to determine if both the closing and
+                            // opening app token sets are wallpaper targets,
+                            // in which case special animations are needed
+                            // (since the wallpaper needs to stay static
+                            // behind them).
+                            int found = 0;
+                            NN = mOpeningApps.size();
+                            for (i=0; i<NN; i++) {
+                                AppWindowToken wtoken = mOpeningApps.get(i);
+                                if (mUpcomingWallpaperTarget.mAppToken == wtoken) {
+                                    found |= 1;
+                                }
+                                if (mWallpaperTarget.mAppToken == wtoken) {
+                                    found |= 1;
+                                }
+                            }
+                            NN = mClosingApps.size();
+                            for (i=0; i<NN; i++) {
+                                AppWindowToken wtoken = mClosingApps.get(i);
+                                if (mUpcomingWallpaperTarget.mAppToken == wtoken) {
+                                    found |= 2;
+                                }
+                                if (mWallpaperTarget.mAppToken == wtoken) {
+                                    found |= 2;
+                                }
+                            }
+                            
+                            if (found == 3) {
+                                if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                        "Wallpaper animation!");
+                                switch (transit) {
+                                    case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+                                    case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+                                    case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+                                        transit = WindowManagerPolicy.TRANSIT_WALLPAPER_ACTIVITY_OPEN;
+                                        break;
+                                    case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+                                    case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+                                    case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+                                        transit = WindowManagerPolicy.TRANSIT_WALLPAPER_ACTIVITY_CLOSE;
+                                        break;
+                                }
+                                if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+                                        "New transit: " + transit);
+                            }
+                        }
+                        
                         // We need to figure out which animation to use...
                         WindowManager.LayoutParams lp = findAnimations(mAppTokens,
                                 mOpeningApps, mClosingApps);
@@ -8671,7 +8814,6 @@
                         // This has changed the visibility of windows, so perform
                         // a new layout to get them all up-to-date.
                         mLayoutNeeded = true;
-                        adjustWallpaperWindowsLocked();
                         if (!moveInputMethodWindowsIfNeededLocked(true)) {
                             assignLayersLocked();
                         }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index afc8b62..bda2d22 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -20,9 +20,8 @@
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.provider.Contacts;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.text.TextUtils;
 import android.telephony.TelephonyManager;
 import android.telephony.PhoneNumberUtils;
@@ -134,44 +133,39 @@
                 int columnIndex;
 
                 // Look for the name
-                columnIndex = cursor.getColumnIndex(People.NAME);
+                columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
                 if (columnIndex != -1) {
                     info.name = cursor.getString(columnIndex);
                 }
 
                 // Look for the number
-                columnIndex = cursor.getColumnIndex(Phones.NUMBER);
+                columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
                 if (columnIndex != -1) {
                     info.phoneNumber = cursor.getString(columnIndex);
                 }
 
                 // Look for the label/type combo
-                columnIndex = cursor.getColumnIndex(Phones.LABEL);
+                columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
                 if (columnIndex != -1) {
-                    int typeColumnIndex = cursor.getColumnIndex(Phones.TYPE);
+                    int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
                     if (typeColumnIndex != -1) {
                         info.numberType = cursor.getInt(typeColumnIndex);
                         info.numberLabel = cursor.getString(columnIndex);
-                        info.phoneLabel = Contacts.Phones.getDisplayLabel(context,
+                        info.phoneLabel = Phone.getDisplayLabel(context,
                                 info.numberType, info.numberLabel)
                                 .toString();
                     }
                 }
 
                 // Look for the person ID
-                columnIndex = cursor.getColumnIndex(Phones.PERSON_ID);
+                columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
                 if (columnIndex != -1) {
                     info.person_id = cursor.getLong(columnIndex);
-                } else {
-                    columnIndex = cursor.getColumnIndex(People._ID);
-                    if (columnIndex != -1) {
-                        info.person_id = cursor.getLong(columnIndex);
-                    }
                 }
 
                 // look for the custom ringtone, create from the string stored
                 // in the database.
-                columnIndex = cursor.getColumnIndex(People.CUSTOM_RINGTONE);
+                columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
                 if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
                     info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
                 } else {
@@ -180,7 +174,7 @@
 
                 // look for the send to voicemail flag, set it to true only
                 // under certain circumstances.
-                columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL);
+                columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
                 info.shouldSendToVoicemail = (columnIndex != -1) &&
                         ((cursor.getInt(columnIndex)) == 1);
                 info.contactExists = true;
@@ -256,8 +250,7 @@
             }
         }
 
-        Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL,
-                                              Uri.encode(number));
+        Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number);
 
         CallerInfo info = getCallerInfo(context, contactUri);
 
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index f81f42a..ef456f0 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -24,7 +24,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.provider.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -303,7 +303,7 @@
     public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
             OnQueryCompleteListener listener, Object cookie) {
         //contruct the URI object and start Query.
-        Uri contactRef = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, number);
+        Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number);
 
         CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
         c.allocate(context, contactRef);
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 29e89b5..cc981c9 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -456,16 +456,19 @@
         if (dataEnabled[id] != enable) {
             dataEnabled[id] = enable;
 
+            // count the total number of enabled APN's
+            // if we just enabled the first APN, start our Data connection,
+            // if we disabled the last, stop our data connection
             if (enable) {
                 enabledCount++;
+                if (enabledCount == 1) {
+                    setPrivateDataEnabled(true);
+                }
             } else {
                 enabledCount--;
-            }
-
-            if (enabledCount == 0) {
-                setPrivateDataEnabled(false);
-            } else if (enabledCount == 1) {
-                setPrivateDataEnabled(true);
+                if (enabledCount == 0) {
+                    setPrivateDataEnabled(false);
+                }
             }
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index c2bed88..6657060 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -595,7 +595,7 @@
 
         // this is common for all radio technologies
         if (!mIccCardStatus.getCardState().isCardPresent()) {
-            return IccCard.State.NOT_READY;
+            return IccCard.State.ABSENT;
         }
 
         RadioState currentRadioState = mPhone.mCM.getRadioState();
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 9c04305..1597427 100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -29,6 +29,7 @@
 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.util.HexDump;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -67,6 +68,7 @@
  */
 public class SmsMessage extends SmsMessageBase {
     static final String LOG_TAG = "CDMA";
+    private final static Boolean DBG_SMS = false;
 
     /**
      *  Status of a previously submitted SMS.
@@ -541,6 +543,11 @@
             return;
         }
         mBearerData = BearerData.decode(mEnvelope.bearerData);
+        if (DBG_SMS) {
+            Log.d(LOG_TAG, "MT raw BearerData = '" +
+                      HexDump.toHexString(mEnvelope.bearerData) + "'");
+            Log.d(LOG_TAG, "MT (decoded) BearerData = " + mBearerData);
+        }
         messageRef = mBearerData.messageId;
         if (mBearerData.userData != null) {
             userData = mBearerData.userData.payload;
@@ -644,14 +651,17 @@
         bearerData.reportReq = false;
 
         bearerData.userData = userData;
-        bearerData.hasUserDataHeader = (userData.userDataHeader != null);
+
+        byte[] encodedBearerData = BearerData.encode(bearerData);
+        if (DBG_SMS) {
+            Log.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
+            Log.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
+        }
+        if (encodedBearerData == null) return null;
 
         int teleservice = bearerData.hasUserDataHeader ?
                 SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
 
-        byte[] encodedBearerData = BearerData.encode(bearerData);
-        if (encodedBearerData == null) return null;
-
         SmsEnvelope envelope = new SmsEnvelope();
         envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
         envelope.teleService = teleservice;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index fefeb12..721729d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -484,7 +484,7 @@
             Gsm7bitCodingResult result = new Gsm7bitCodingResult();
             result.data = new byte[fullData.length - 1];
             System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
-            result.septets = fullData[0];
+            result.septets = fullData[0] & 0x00FF;
             return result;
         } catch (com.android.internal.telephony.EncodeException ex) {
             throw new CodingException("7bit GSM encode failed: " + ex);
@@ -498,6 +498,7 @@
         int udhSeptets = ((udhBytes * 8) + 6) / 7;
         Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
         uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+        uData.msgEncodingSet = true;
         uData.numFields = gcr.septets;
         uData.payload = gcr.data;
         uData.payload[0] = (byte)udhData.length;
@@ -512,6 +513,8 @@
         int udhCodeUnits = (udhBytes + 1) / 2;
         int udhPadding = udhBytes % 2;
         int payloadCodeUnits = payload.length / 2;
+        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+        uData.msgEncodingSet = true;
         uData.numFields = udhCodeUnits + payloadCodeUnits;
         uData.payload = new byte[uData.numFields * 2];
         uData.payload[0] = (byte)udhData.length;
@@ -606,14 +609,16 @@
          * copies by passing outStream directly.
          */
         encodeUserDataPayload(bData.userData);
+        bData.hasUserDataHeader = bData.userData.userDataHeader != null;
+
         if (bData.userData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) {
             throw new CodingException("encoded user data too large (" +
                                       bData.userData.payload.length +
                                       " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
         }
 
-        /**
-         * XXX/TODO: figure out what the right answer is WRT padding bits
+        /*
+         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
          *
          *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
          *   userData.paddingBits = 0; // XXX this seems better, but why?
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 9b6e19d..d93852c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -154,7 +154,7 @@
         builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset"));
         builder.append(", msgType=" + msgType);
         builder.append(", paddingBits=" + paddingBits);
-        builder.append(", numFields=" + (int)numFields);
+        builder.append(", numFields=" + numFields);
         builder.append(", userDataHeader=" + userDataHeader);
         builder.append(", payload='" + HexDump.toHexString(payload) + "'");
         builder.append(", payloadStr='" + payloadStr + "'");