Merge "Let restored scale be in effect."
diff --git a/api/current.xml b/api/current.xml
index 6da4a33..1055707 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -108717,32 +108717,6 @@
 <parameter name="callbackImmediately" type="boolean">
 </parameter>
 </method>
-<method name="setRingbackToneEnabled"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
-<method name="setRingtoneEnabled"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
 <method name="setSpeakerMode"
  return="void"
  abstract="false"
@@ -109441,25 +109415,6 @@
 <exception name="SipException" type="android.net.sip.SipException">
 </exception>
 </method>
-<method name="takeAudioCall"
- return="android.net.sip.SipAudioCall"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="incomingCallIntent" type="android.content.Intent">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-<parameter name="ringtoneEnabled" type="boolean">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
 <method name="unregister"
  return="void"
  abstract="false"
@@ -112680,7 +112635,7 @@
  visibility="public"
 >
 </method>
-<method name="isTagDiscoveryEnabled"
+<method name="isEnabled"
  return="boolean"
  abstract="false"
  native="false"
@@ -152842,6 +152797,17 @@
  visibility="public"
 >
 </field>
+<field name="DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EMAIL"
  type="java.lang.String"
  transient="false"
diff --git a/core/java/android/animation/TimeInterpolator.java b/core/java/android/animation/TimeInterpolator.java
index 8d795a8..0f5d8bf 100644
--- a/core/java/android/animation/TimeInterpolator.java
+++ b/core/java/android/animation/TimeInterpolator.java
@@ -23,7 +23,7 @@
 public interface TimeInterpolator {
 
     /**
-     * Maps a value representing the elapsed fraciton of an animation to a value that represents
+     * Maps a value representing the elapsed fraction of an animation to a value that represents
      * the interpolated fraction. This interpolated value is then multiplied by the change in
      * value of an animation to derive the animated value at the current elapsed animation time.
      *
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 79754b8..d60a598 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -523,7 +523,6 @@
             for (int i = 0; i < numValues; ++i) {
                 mValues[i].init();
             }
-            mCurrentIteration = 0;
             mInitialized = true;
         }
     }
@@ -933,6 +932,7 @@
             // This sets the initial value of the animation, prior to actually starting it running
             setCurrentPlayTime(getCurrentPlayTime());
         }
+        mCurrentIteration = 0;
         mPlayingState = STOPPED;
         mStartedDelay = false;
         sPendingAnimations.add(this);
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index d7a0412..c98128c 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -162,15 +162,10 @@
         
         String path = null;
         
-        if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) == 0) {
-            // If the application does not have (Java) code, then no ClassLoader
-            // has been set up for it.  We will need to do our own search for
-            // the native code.
-            File libraryFile = new File(ai.applicationInfo.nativeLibraryDir,
-                    System.mapLibraryName(libname));
-            if (libraryFile.exists()) {
-                path = libraryFile.getPath();
-            }
+        File libraryFile = new File(ai.applicationInfo.nativeLibraryDir,
+                System.mapLibraryName(libname));
+        if (libraryFile.exists()) {
+            path = libraryFile.getPath();
         }
         
         if (path == null) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 643e747..bb0ed6a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -252,9 +252,6 @@
     public static final int FLAG_RESTORE_ANY_VERSION = 1<<17;
 
     /**
-     * Value for {@link #flags}: Set to true if the application has been
-     * installed using the forward lock option.
-     *
      * Value for {@link #flags}: Set to true if the application is
      * currently installed on external/removable/unprotected storage.  Such
      * applications may not be available if their storage is not currently
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 2c5c909..98bf632 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -177,12 +177,12 @@
      *  <h4>{@link android.hardware.Sensor#TYPE_ROTATION_VECTOR Sensor.TYPE_ROTATION_VECTOR}:</h4>
      *  The rotation vector represents the orientation of the device as a combination of an angle
      *  and an axis, in which the device has rotated through an angle theta around an axis
-     *  <x, y, z>. The three elements of the rotation vector are
-     *  <x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>, such that the magnitude of the rotation
+     *  &lt;x, y, z>. The three elements of the rotation vector are
+     *  &lt;x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>, such that the magnitude of the rotation
      *  vector is equal to sin(theta/2), and the direction of the rotation vector is equal to the
      *  direction of the axis of rotation. The three elements of the rotation vector are equal to
      *  the last three components of a unit quaternion
-     *  <cos(theta/2), x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>.  Elements of the rotation
+     *  &lt;cos(theta/2), x*sin(theta/2), y*sin(theta/2), z*sin(theta/2)>.  Elements of the rotation
      *  vector are unitless.  The x,y, and z axis are defined in the same way as the acceleration
      *  sensor.
      *
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 2171434..13b97d6 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -28,6 +28,7 @@
     String getType(int nativeHandle);
     byte[] getUid(int nativeHandle);
     boolean isNdef(int nativeHandle);
+    boolean isPresent(int nativeHandle);
     byte[] transceive(int nativeHandle, in byte[] data);
 
     int getLastError(int nativeHandle);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index bc3c6d9..6884abb 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -227,14 +227,17 @@
     }
 
     /**
-     * Return true if this NFC Adapter is enabled to discover new tags.
+     * Return true if this NFC Adapter has any features enabled.
      * <p>
      * If this method returns false, then applications should request the user
      * turn on NFC tag discovery in Settings.
+     * <p>
+     * If this method returns false, the NFC hardware is guaranteed not to
+     * perform or respond to any NFC communication.
      *
      * @return true if this NFC Adapter is enabled to discover new tags
      */
-    public boolean isTagDiscoveryEnabled() {
+    public boolean isEnabled() {
         try {
             return mService.isEnabled();
         } catch (RemoteException e) {
@@ -244,12 +247,14 @@
     }
 
     /**
+     * Enable NFC hardware.
+     * <p>
      * NOTE: may block for ~second or more.  Poor API.  Avoid
      * calling from the UI thread.
      *
      * @hide
      */
-    public boolean enableTagDiscovery() {
+    public boolean enable() {
         try {
             return mService.enable();
         } catch (RemoteException e) {
@@ -259,12 +264,16 @@
     }
 
     /**
+     * Disable NFC hardware.
+     * No NFC features will work after this call, and the hardware
+     * will not perform or respond to any NFC communication.
+     * <p>
      * NOTE: may block for ~second or more.  Poor API.  Avoid
      * calling from the UI thread.
      *
      * @hide
      */
-    public boolean disableTagDiscovery() {
+    public boolean disable() {
         try {
             return mService.disable();
         } catch (RemoteException e) {
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/RawTagConnection.java
index 265eb1b..1261db1 100644
--- a/core/java/android/nfc/RawTagConnection.java
+++ b/core/java/android/nfc/RawTagConnection.java
@@ -95,10 +95,16 @@
      * returns true.
      */
     public boolean isConnected() {
-        // TODO(nxp): update mIsConnected when tag goes out of range -
-        //            but do not do an active prescence check in
-        //            isConnected()
-        return mIsConnected;
+        if (!mIsConnected) {
+            return false;
+        }
+
+        try {
+            return mTagService.isPresent(mTag.mNativeHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service died", e);
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index baf3505..08f9346 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -32,7 +32,6 @@
 import android.database.DatabaseUtils;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.net.Uri.Builder;
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
@@ -1444,6 +1443,12 @@
          * against the cost of transferring large amounts of denormalized data
          * from the Provider.
          * </p>
+         * <p>
+         * To reduce the amount of data duplication the contacts provider and directory
+         * providers implementing this protocol are allowed to provide common Contacts
+         * and RawContacts fields in the first row returned for each raw contact only and
+         * leave them as null in subsequent rows.
+         * </p>
          */
         public static final class Entity implements BaseColumns, ContactsColumns,
                 ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
@@ -1838,7 +1843,8 @@
      * constituent data rows in a single database transaction
      * and causes at most one aggregation pass.
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
      * ...
      * int rawContactInsertIndex = ops.size();
      * ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
@@ -2725,7 +2731,9 @@
      * <p>
      * The same done using ContentProviderOperations:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
      *          .withValue(Data.RAW_CONTACT_ID, rawContactId)
      *          .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
@@ -2742,7 +2750,9 @@
      * Just as with insert, update can be done incrementally or as a batch,
      * the batch mode being the preferred method:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
      *          .withSelection(Data._ID + "=?", new String[]{String.valueOf(dataId)})
      *          .withValue(Email.DATA, "somebody@android.com")
@@ -2757,7 +2767,9 @@
      * Just as with insert and update, deletion can be done either using the
      * {@link ContentResolver#delete} method or using a ContentProviderOperation:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newDelete(Data.CONTENT_URI)
      *          .withSelection(Data._ID + "=?", new String[]{String.valueOf(dataId)})
      *          .build());
@@ -4050,7 +4062,9 @@
          * <p>A data kind representing the contact's nickname. For example, for
          * Bob Parr ("Mr. Incredible"):
          * <pre>
-         * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+         * ArrayList&lt;ContentProviderOperation&gt; ops =
+         *          new ArrayList&lt;ContentProviderOperation&gt;();
+         *
          * ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
          *          .withValue(Data.RAW_CONTACT_ID, rawContactId)
          *          .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
@@ -6645,14 +6659,41 @@
 
             /**
              * The extra field that allows the client to supply multiple rows of
-             * arbitrary data for the contact (insert or edit). It is a list of
-             * ContentValues, one per data row. Supplying this extra is
-             * basically equivalent to inserting multiple rows into the
-             * {@link Data} table, except the user gets a chance to see and edit
-             * them before saving. Each ContentValues object must have a value
-             * for {@link Data#MIMETYPE}.
+             * arbitrary data for a single contact created using the {@link Intent#ACTION_INSERT}
+             * or edited using {@link Intent#ACTION_EDIT}. It is an ArrayList of
+             * {@link ContentValues}, one per data row. Supplying this extra is
+             * similar to inserting multiple rows into the {@link Data} table,
+             * except the user gets a chance to see and edit them before saving.
+             * Each ContentValues object must have a value for {@link Data#MIMETYPE}.
+             * If supplied values are not visible in the editor UI, they will be
+             * dropped.  Duplicate data will dropped.  Some fields
+             * like {@link CommonDataKinds.Email#TYPE Email.TYPE} may be automatically
+             * adjusted to comply with the constraints of the specific account type.
+             * For example, an Exchange contact can only have one phone numbers of type Home,
+             * so the contact editor may choose a different type for this phone number to
+             * avoid dropping the valueable part of the row, which is the phone number.
+             * <p>
+             * Example:
+             * <pre>
+             *  ArrayList&lt;ContentValues&gt; data = new ArrayList&lt;ContentValues&gt;();
              *
-             * @hide
+             *  ContentValues row1 = new ContentValues();
+             *  row1.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+             *  row1.put(Organization.COMPANY, "Android");
+             *  data.add(row1);
+             *
+             *  ContentValues row2 = new ContentValues();
+             *  row2.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+             *  row2.put(Email.TYPE, Email.TYPE_CUSTOM);
+             *  row2.put(Email.LABEL, "Green Bot");
+             *  row2.put(Email.ADDRESS, "android@android.com");
+             *  data.add(row2);
+             *
+             *  Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+             *  intent.putParcelableArrayListExtra(Insert.DATA, data);
+             *
+             *  startActivity(intent);
+             * </pre>
              */
             public static final String DATA = "data";
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 17d4aa4..62d2ff2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1642,86 +1642,6 @@
         public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
 
         /**
-         * Whether nfc is enabled/disabled
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_ON = "nfc_on";
-
-        /**
-         * Whether nfc secure element is enabled/disabled
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_SECURE_ELEMENT_ON = "nfc_secure_element_on";
-
-        /**
-         * Whether nfc secure element is enabled/disabled
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_SECURE_ELEMENT_ID = "nfc_secure_element_id";
-
-        /**
-         * LLCP LTO value
-         * @hide
-         */
-        public static final String NFC_LLCP_LTO = "nfc_llcp_lto";
-
-        /**
-         * LLCP MIU value
-         * @hide
-         */
-        public static final String NFC_LLCP_MIU = "nfc_llcp_miu";
-
-        /**
-         * LLCP WKS value
-         * @hide
-         */
-        public static final String NFC_LLCP_WKS = "nfc_llcp_wks";
-
-        /**
-         * LLCP OPT value
-         * @hide
-         */
-        public static final String NFC_LLCP_OPT = "nfc_llcp_opt";
-
-        /**
-         * NFC Discovery Reader A
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_DISCOVERY_A = "nfc_discovery_a";
-
-        /**
-         * NFC Discovery Reader B
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_DISCOVERY_B = "nfc_discovery_b";
-
-        /**
-         * NFC Discovery Reader Felica
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_DISCOVERY_F = "nfc_discovery_felica";
-
-        /**
-         * NFC Discovery Reader 15693
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_DISCOVERY_15693 = "nfc_discovery_15693";
-
-        /**
-         * NFC Discovery NFCIP
-         * 0=disabled. 1=enabled.
-         * @hide
-         */
-        public static final String NFC_DISCOVERY_NFCIP = "nfc_discovery_nfcip";
-
-        /**
          * Show pointer location on screen?
          * 0 = no
          * 1 = yes
@@ -1895,18 +1815,6 @@
             USE_PTP_INTERFACE,
             SIP_CALL_OPTIONS,
             SIP_RECEIVE_CALLS,
-            NFC_ON,
-            NFC_SECURE_ELEMENT_ON,
-            NFC_SECURE_ELEMENT_ID,
-            NFC_LLCP_LTO,
-            NFC_LLCP_MIU,
-            NFC_LLCP_WKS,
-            NFC_LLCP_OPT,
-            NFC_DISCOVERY_A,
-            NFC_DISCOVERY_B,
-            NFC_DISCOVERY_F,
-            NFC_DISCOVERY_15693,
-            NFC_DISCOVERY_NFCIP,
         };
 
         // Settings moved to Settings.Secure
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index b1160f0..e7c2231 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -39,12 +39,6 @@
     abstract void end();
 
     /**
-     * Frees resources taken by this display list. This method must be called
-     * before releasing all references.
-     */
-    abstract void destroy();
-
-    /**
      * Indicates whether this display list can be replayed or not.
      * 
      * @return True if the display list can be replayed, false otherwise.
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 93598cd..ebf8505 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -53,6 +53,14 @@
     private DragEvent() {
     }
 
+    private void init(int action, float x, float y, ClipDescription description, ClipData data) {
+        mAction = action;
+        mX = x;
+        mY = y;
+        mClipDescription = description;
+        mClipData = data;
+    }
+
     static DragEvent obtain() {
         return DragEvent.obtain(0, 0f, 0f, null, null);
     }
@@ -62,7 +70,9 @@
         final DragEvent ev;
         synchronized (gRecyclerLock) {
             if (gRecyclerTop == null) {
-                return new DragEvent();
+                ev = new DragEvent();
+                ev.init(action, x, y, description, data);
+                return ev;
             }
             ev = gRecyclerTop;
             gRecyclerTop = ev.mNext;
@@ -72,11 +82,7 @@
         ev.mRecycled = false;
         ev.mNext = null;
 
-        ev.mAction = action;
-        ev.mX = x;
-        ev.mY = y;
-        ev.mClipDescription = description;
-        ev.mClipData = data;
+        ev.init(action, x, y, description, data);
 
         return ev;
     }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index d78c68a..5d9bd1e 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -34,6 +34,11 @@
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
+import android.util.Finalizers;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * An implementation of Canvas on top of OpenGL ES 2.0.
@@ -84,22 +89,35 @@
        
         if (mRenderer == 0) {
             throw new IllegalStateException("Could not create GLES20Canvas renderer");
+        } else {
+            new CanvasFinalizer(this);
         }
     }
 
     private native int nCreateRenderer();    
     private native int nCreateDisplayListRenderer();    
 
-    @Override
-    public synchronized void destroy() {
-        if (mRenderer != 0) {
+    private static native void nDestroyRenderer(int renderer);
+
+    private static class CanvasFinalizer extends Finalizers.ReclaimableReference<GLES20Canvas> {
+        private static final Set<CanvasFinalizer> sFinalizers = Collections.synchronizedSet(
+                new HashSet<CanvasFinalizer>());
+
+        private int mRenderer;
+
+        CanvasFinalizer(GLES20Canvas canvas) {
+            super(canvas, Finalizers.getQueue());
+            mRenderer = canvas.mRenderer;
+            sFinalizers.add(this);
+        }
+
+        @Override
+        public void reclaim() {
             nDestroyRenderer(mRenderer);
-            mRenderer = 0;
+            sFinalizers.remove(this);
         }
     }
 
-    private native void nDestroyRenderer(int renderer);
-
     ///////////////////////////////////////////////////////////////////////////
     // Canvas management
     ///////////////////////////////////////////////////////////////////////////
@@ -178,11 +196,11 @@
 
     private native int nCreateDisplayList(int renderer);
     
-    void destroyDisplayList(int displayList) {
+    static void destroyDisplayList(int displayList) {
         nDestroyDisplayList(displayList);
     }
 
-    private native void nDestroyDisplayList(int displayList);
+    private static native void nDestroyDisplayList(int displayList);
 
     @Override
     public void drawDisplayList(DisplayList displayList) {
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 2886bf3..11e6d30 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -16,6 +16,12 @@
 
 package android.view;
 
+import android.util.Finalizers;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * An implementation of display list for OpenGL ES 2.0.
  */
@@ -33,8 +39,6 @@
             throw new IllegalStateException("Recording has already started");
         }
 
-        destroyCanvas();
-
         mCanvas = new GLES20Canvas(true, true);
         mStarted = true;
         mRecorded = false;
@@ -42,16 +46,6 @@
         return mCanvas;
     }
 
-    private void destroyCanvas() {
-        if (mCanvas != null) {
-            mCanvas.destroyDisplayList(mNativeDisplayList);
-            mCanvas.destroy();
-
-            mCanvas = null;
-            mNativeDisplayList = 0;
-        }
-    }
-
     @Override
     void end() {
         if (mCanvas != null) {
@@ -59,16 +53,31 @@
             mRecorded = true;
 
             mNativeDisplayList = mCanvas.getDisplayList();
+            new DisplayListFinalizer(this);
         }
     }
 
     @Override
-    void destroy() {
-        destroyCanvas();
-    }
-
-    @Override
     boolean isReady() {
         return !mStarted && mRecorded;
     }
+
+    private static class DisplayListFinalizer extends Finalizers.ReclaimableReference<DisplayList> {
+        private static final Set<DisplayListFinalizer> sFinalizers = Collections.synchronizedSet(
+                new HashSet<DisplayListFinalizer>());
+
+        private int mNativeDisplayList;
+
+        DisplayListFinalizer(GLES20DisplayList displayList) {
+            super(displayList, Finalizers.getQueue());
+            mNativeDisplayList = displayList.mNativeDisplayList;
+            sFinalizers.add(this);
+        }
+
+        @Override
+        public void reclaim() {
+            GLES20Canvas.destroyDisplayList(mNativeDisplayList);
+            sFinalizers.remove(this);
+        }
+    }
 }
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 22d2fe6..8b8d15e 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -34,14 +34,6 @@
     }
     
     /**
-     * This method <strong>must</strong> be called before releasing a
-     * reference to a hardware canvas. This method is responsible for
-     * freeing native resources associated with the hardware. Not
-     * invoking this method properly can result in memory leaks.
-     */    
-    public abstract void destroy();
-
-    /**
      * Invoked before any drawing operation is performed in this canvas.
      */
     abstract void onPreDraw();
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 0247f6a..6f4abef 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -414,7 +414,6 @@
         @Override
         void destroy(boolean full) {
             if (full && mCanvas != null) {
-                mCanvas.destroy();
                 mCanvas = null;
             }
             
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index baa749a..0456463 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7417,7 +7417,6 @@
         if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
             return null;
         }
-        
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
             return null;
         }
@@ -7425,10 +7424,6 @@
         if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED &&
                 ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDisplayList == null)) {
 
-            if (mDisplayList != null) {
-                mDisplayList.destroy();
-            }
-
             mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
 
             final HardwareCanvas canvas = mDisplayList.start();
@@ -7456,8 +7451,6 @@
                 canvas.onPostDraw();
 
                 mDisplayList.end();
-
-                canvas.destroy();                
             }
         }
 
@@ -7532,7 +7525,6 @@
             mUnscaledDrawingCache = null;
         }
         if (mDisplayList != null) {
-            mDisplayList.destroy();
             mDisplayList = null;
         }
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f34fd63..7b2703b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1700,10 +1700,10 @@
         }
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
-     * 
+     *
      * @hide
      */
     @Override
@@ -1715,10 +1715,10 @@
             children[i].dispatchStartTemporaryDetach();
         }
     }
-    
+
     /**
      * {@inheritDoc}
-     * 
+     *
      * @hide
      */
     @Override
@@ -1915,7 +1915,7 @@
         if (skipChildren) {
             for (int i = 0; i < count; i++) {
                 getChildAt(i).setVisibility(visibilities[i]);
-            }        
+            }
         }
 
         return b;
@@ -2045,7 +2045,7 @@
      *
      * @param i The current iteration.
      * @return The index of the child to draw this iteration.
-     * 
+     *
      * @see #setChildrenDrawingOrderEnabled(boolean)
      * @see #isChildrenDrawingOrderEnabled()
      */
@@ -2186,7 +2186,7 @@
                 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
             return more;
         }
-        
+
         float alpha = child.getAlpha();
         // Bail out early if the view does not need to be drawn
         if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
@@ -2360,7 +2360,7 @@
         final View[] children = mChildren;
         final int count = mChildrenCount;
         for (int i = 0; i < count; i++) {
-            
+
             children[i].setSelected(selected);
         }
     }
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 54c4b36..aa68a74 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -154,6 +154,11 @@
         int textColorNormal = textColor.getDefaultColor();
         mPaint.setColor(textColorNormal);
         mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
+        // to show mOverlayDrawable properly
+        if (mList.getWidth() > 0 && mList.getHeight() > 0) {
+            onSizeChanged(mList.getWidth(), mList.getHeight(), 0, 0);
+        }
         
         mState = STATE_NONE;
     }
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index d98f316..7ab95a9 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -152,7 +152,7 @@
      * @param context Context used for contained views.
      */
     public ListPopupWindow(Context context) {
-        this(context, null, 0, 0);
+        this(context, null, com.android.internal.R.attr.listPopupWindowStyle, 0);
     }
 
     /**
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 594b1e6..0469e7b 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -26,8 +26,8 @@
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
 /**
@@ -53,7 +53,8 @@
 public class TabWidget extends LinearLayout implements OnFocusChangeListener {
     private OnTabSelectionChanged mSelectionChangedListener;
 
-    private int mSelectedTab = 0;
+    // This value will be set to 0 as soon as the first tab is added to TabHost.
+    private int mSelectedTab = -1;
 
     private Drawable mLeftStrip;
     private Drawable mRightStrip;
@@ -98,14 +99,18 @@
 
     @Override
     protected int getChildDrawingOrder(int childCount, int i) {
-        // Always draw the selected tab last, so that drop shadows are drawn
-        // in the correct z-order.
-        if (i == childCount - 1) {
-            return mSelectedTab;
-        } else if (i >= mSelectedTab) {
-            return i + 1;
-        } else {
+        if (mSelectedTab == -1) {
             return i;
+        } else {
+            // Always draw the selected tab last, so that drop shadows are drawn
+            // in the correct z-order.
+            if (i == childCount - 1) {
+                return mSelectedTab;
+            } else if (i >= mSelectedTab) {
+                return i + 1;
+            } else {
+                return i;
+            }
         }
     }
 
@@ -340,7 +345,9 @@
             return;
         }
 
-        getChildTabViewAt(mSelectedTab).setSelected(false);
+        if (mSelectedTab != -1) {
+            getChildTabViewAt(mSelectedTab).setSelected(false);
+        }
         mSelectedTab = index;
         getChildTabViewAt(mSelectedTab).setSelected(true);
         mStripMoved = true;
@@ -354,7 +361,9 @@
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         event.setItemCount(getTabCount());
         event.setCurrentItemIndex(mSelectedTab);
-        getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
+        if (mSelectedTab != -1) {
+            getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
+        }
         return true;
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d071dd2..be056da 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -990,6 +990,22 @@
         setTypeface(tf, styleIndex);
     }
 
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (enabled == isEnabled()) {
+            return;
+        }
+
+        if (!enabled) {
+            // Hide the soft input if the currently active TextView is disabled
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null && imm.isActive(this)) {
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+        }
+        super.setEnabled(enabled);
+    }
+
     /**
      * Sets the typeface and style in which the text should be displayed,
      * and turns on the fake bold and italic bits in the Paint if the
@@ -4640,7 +4656,7 @@
     }
     
     @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (onCheckIsTextEditor()) {
+        if (onCheckIsTextEditor() && isEnabled()) {
             if (mInputMethodState == null) {
                 mInputMethodState = new InputMethodState();
             }
@@ -6910,7 +6926,8 @@
             return superResult;
         }
 
-        if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
+        if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
+                && mText instanceof Spannable && mLayout != null) {
             if (mInsertionPointCursorController != null) {
                 mInsertionPointCursorController.onTouchEvent(event);
             }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5c1dbaf..224f9a5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3993,8 +3993,8 @@
                 // we have gone through a significant charge (from a very low
                 // level to a now very high level).
                 if (oldStatus == BatteryManager.BATTERY_STATUS_FULL
-                        || level >= 100
-                        || (mDischargeCurrentLevel < 20 && level > 90)) {
+                        || level >= 95
+                        || (mDischargeCurrentLevel < 30 && level >= 90)) {
                     doWrite = true;
                     resetAllStatsLocked();
                     mDischargeStartLevel = level;
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
new file mode 100644
index 0000000..15b9290
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -0,0 +1,778 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Stack;
+
+/**
+ * An asynchronous channel between two handlers.
+ *
+ * The handlers maybe in the same process or in another process. There
+ * are two protocol styles that can be used with an AysncChannel. The
+ * first is a simple request/reply protocol where the server does
+ * not need to know which client is issuing the request.
+ *
+ * In a simple request/reply protocol the client/source sends requests to the
+ * server/destination. And the server uses the replyToMessage methods.
+ * In this usage model there is no need for the destination to
+ * use the connect methods. The typical sequence of operations is:
+ *
+ *   1) Client calls AsyncChannel#connect
+ *   2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+
+ *   3) Client calls AsyncChannel#sendMessage(msgX)
+ *   4) Server receives and processes msgX
+ *   5) Server optionally calls AsyncChannel#replyToMessage(msgY)
+ *      and if sent Client receives and processes msgY
+ *   6) Loop to step 3 until done
+ *
+ *   7) When done Client calls {@link AsyncChannel#disconnect(int)}
+ *   8) Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ *
+ * A second usage model is where the server/destination needs to know
+ * which client it's connected too. For example the server needs to
+ * send unsolicited messages back to the client. Or the server keeps
+ * different state for each client. In this model the server will also
+ * use the connect methods. The typical sequence of operation is:
+ *
+ *   1)  Client calls AsyncChannel#connect
+ *   2)  Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ *   3)  Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)
+ *   4)  Server receives CMD_CHANNEL_FULL_CONNECTION
+ *   5)  Server calls AsyncChannel#connect
+ *   6)  Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ *   7)  Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)
+ *   8)  Client receives CMD_CHANNEL_FULLY_CONNECTED
+ *
+ *   9)  Client/Server uses AsyncChannel#sendMessage/replyToMessage
+ *       to communicate and perform work
+ *   10) Loop to step 9 until done
+ *
+ *   11) When done Client/Server calls {@link AsyncChannel#disconnect(int)}
+ *   12) Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ */
+public class AsyncChannel {
+    /** Log tag */
+    private static final String TAG = "AsyncChannel";
+
+    /** Enable to turn on debugging */
+    private static final boolean DBG = false;
+
+    /**
+     * Command sent when the channel is half connected. Half connected
+     * means that the channel can be used to send commends to the destination
+     * but the destination is unaware that the channel exists. The first
+     * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
+     * it is desired to establish a long term connection, but any command maybe
+     * sent.
+     *
+     * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *             1 : STATUS_BINDING_UNSUCCESSFUL
+     * msg.arg2 == token parameter
+     * msg.replyTo == dstMessenger if successful
+     */
+    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+
+    /**
+     * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
+     * This is used to initiate a long term connection with the destination and
+     * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
+     *
+     * msg.replyTo = srcMessenger.
+     */
+    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+
+    /**
+     * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
+     * This signifies the acceptance or rejection of the channel by the sender.
+     *
+     * msg.arg1 == 0 : Accept connection
+     *               : All other values signify the destination rejected the connection
+     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
+     */
+    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+
+    /**
+     * Command sent when one side or the other wishes to disconnect. The sender
+     * may or may not be able to receive a reply depending upon the protocol and
+     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
+     * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
+     * when the channel is closed.
+     *
+     * msg.replyTo = messenger that is disconnecting
+     */
+    public static final int CMD_CHANNEL_DISCONNECT = -4;
+
+    /**
+     * Command sent when the channel becomes disconnected. This is sent when the
+     * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
+     *
+     * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *               : All other values signify failure and the channel state is indeterminate
+     * msg.arg2 == token parameter
+     * msg.replyTo = messenger disconnecting or null if it was never connected.
+     */
+    public static final int CMD_CHANNEL_DISCONNECTED = -5;
+
+    /** Successful status always 0, !0 is an unsuccessful status */
+    public static final int STATUS_SUCCESSFUL = 0;
+
+    /** Error attempting to bind on a connect */
+    public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
+
+    /** Service connection */
+    private AsyncChannelConnection mConnection;
+
+    /** Context for source */
+    private Context mSrcContext;
+
+    /** Handler for source */
+    private Handler mSrcHandler;
+
+    /** Messenger for source */
+    private Messenger mSrcMessenger;
+
+    /** Messenger for destination */
+    private Messenger mDstMessenger;
+
+    /**
+     * AsyncChannel constructor
+     */
+    public AsyncChannel() {
+    }
+
+    /**
+     * Connect handler to named package/class.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstPackageName is the destination package name
+     * @param dstClassName is the fully qualified class name (i.e. contains
+     *            package name)
+     * @param token unique id for this connection
+     */
+    private void connectSrcHandlerToPackage(
+            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
+            int token) {
+        if (DBG) log("connect srcHandler to dst Package & class E");
+
+        mConnection = new AsyncChannelConnection(token);
+
+        /* Initialize the source information */
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(srcHandler);
+
+        /*
+         * Initialize destination information to null they will
+         * be initialized when the AsyncChannelConnection#onServiceConnected
+         * is called
+         */
+        mDstMessenger = null;
+
+        /* Send intent to create the connection */
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClassName(dstPackageName, dstClassName);
+        boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        if (!result) {
+            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token);
+        }
+
+        if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+    }
+
+    /**
+     * Connect handler to named package/class.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstPackageName is the destination package name
+     * @param dstClassName is the fully qualified class name (i.e. contains
+     *            package name)
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
+            String dstClassName, int token) {
+        if (DBG) log("connect srcHandler to dst Package & class E");
+
+        final class ConnectAsync implements Runnable {
+            Context mSrcCtx;
+            Handler mSrcHdlr;
+            String mDstPackageName;
+            String mDstClassName;
+            int mToken;
+
+            ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
+                    String dstClassName, int token) {
+                mSrcCtx = srcContext;
+                mSrcHdlr = srcHandler;
+                mDstPackageName = dstPackageName;
+                mDstClassName = dstClassName;
+                mToken = token;
+            }
+
+            @Override
+            public void run() {
+                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
+                        mToken);
+            }
+        }
+
+        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
+                token);
+        new Thread(ca).start();
+
+        if (DBG) log("connect srcHandler to dst Package & class X");
+    }
+
+    /**
+     * Connect handler to a class
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext
+     * @param srcHandler
+     * @param klass is the class to send messages to.
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
+        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
+    }
+
+    /**
+     * Connect handler and messenger.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext
+     * @param srcHandler
+     * @param dstMessenger
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
+        if (DBG) log("connect srcHandler to the dstMessenger  E");
+
+        // Initialize source fields
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(mSrcHandler);
+
+        // Initialize destination fields
+        mDstMessenger = dstMessenger;
+
+        if (DBG) log("tell source we are half connected");
+
+        // Tell source we are half connected
+        replyHalfConnected(STATUS_SUCCESSFUL, token);
+
+        if (DBG) log("connect srcHandler to the dstMessenger X");
+    }
+
+    /**
+     * Connect two local Handlers.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
+        connect(srcContext, srcHandler, new Messenger(dstHandler), token);
+    }
+
+    /**
+     * Connect service and messenger.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcAsyncService
+     * @param dstMessenger
+     * @param token returned in msg.arg2
+     */
+    public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
+        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
+    }
+
+    /**
+     * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
+     */
+    public void disconnected() {
+        mSrcHandler = null;
+        mSrcMessenger = null;
+        mDstMessenger = null;
+        mConnection = null;
+    }
+
+    /**
+     * Disconnect
+     */
+    public void disconnect(int token) {
+        if (mConnection != null) {
+            mConnection.setToken(token);
+            mSrcContext.unbindService(mConnection);
+        }
+        if (mSrcHandler != null) {
+            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+            msg.arg1 = STATUS_SUCCESSFUL;
+            msg.arg2 = token;
+            msg.replyTo = mDstMessenger;
+            mSrcHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Send a message to the destination handler.
+     *
+     * @param msg
+     */
+    public void sendMessage(Message msg) {
+        msg.replyTo = mSrcMessenger;
+        try {
+            mDstMessenger.send(msg);
+        } catch (RemoteException e) {
+            log("TODO: handle sendMessage RemoteException" + e);
+        }
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     */
+    public void sendMessage(int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     */
+    public void sendMessage(int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     */
+    public void sendMessage(int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     */
+    public void sendMessage(int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param obj
+     */
+    public void sendMessage(int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        sendMessage(msg);
+    }
+
+    /**
+     * Reply to srcMsg sending dstMsg
+     *
+     * @param srcMsg
+     * @param dstMsg
+     */
+    public void replyToMessage(Message srcMsg, Message dstMsg) {
+        try {
+            srcMsg.replyTo.send(dstMsg);
+        } catch (RemoteException e) {
+            log("TODO: handle replyToMessage RemoteException" + e);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     */
+    public void replyToMessage(Message srcMsg, int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     * @param arg2
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param obj
+     */
+    public void replyToMessage(Message srcMsg, int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param msg to send
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(Message msg) {
+        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param obj
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Helper class to send messages synchronously
+     */
+    private static class SyncMessenger {
+        /** A stack of SyncMessengers */
+        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
+        /** A number of SyncMessengers created */
+        private static int sCount = 0;
+        /** The handler thread */
+        private HandlerThread mHandlerThread;
+        /** The handler that will receive the result */
+        private SyncHandler mHandler;
+        /** The messenger used to send the message */
+        private Messenger mMessenger;
+
+        /** private constructor */
+        private SyncMessenger() {
+        }
+
+        /** Synchronous Handler class */
+        private class SyncHandler extends Handler {
+            /** The object used to wait/notify */
+            private Object mLockObject = new Object();
+            /** The resulting message */
+            private Message mResultMsg;
+
+            /** Constructor */
+            private SyncHandler(Looper looper) {
+                super(looper);
+            }
+
+            /** Handle of the reply message */
+            @Override
+            public void handleMessage(Message msg) {
+                mResultMsg = Message.obtain();
+                mResultMsg.copyFrom(msg);
+                synchronized(mLockObject) {
+                    mLockObject.notify();
+                }
+            }
+        }
+
+        /**
+         * @return the SyncMessenger
+         */
+        private static SyncMessenger obtain() {
+            SyncMessenger sm;
+            synchronized (sStack) {
+                if (sStack.isEmpty()) {
+                    sm = new SyncMessenger();
+                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
+                    sm.mHandlerThread.start();
+                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
+                    sm.mMessenger = new Messenger(sm.mHandler);
+                } else {
+                    sm = sStack.pop();
+                }
+            }
+            return sm;
+        }
+
+        /**
+         * Recycle this object
+         */
+        private void recycle() {
+            synchronized (sStack) {
+                sStack.push(this);
+            }
+        }
+
+        /**
+         * Send a message synchronously.
+         *
+         * @param msg to send
+         * @return result message or null if an error occurs
+         */
+        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
+            SyncMessenger sm = SyncMessenger.obtain();
+            try {
+                msg.replyTo = sm.mMessenger;
+                dstMessenger.send(msg);
+                synchronized (sm.mHandler.mLockObject) {
+                    sm.mHandler.mLockObject.wait();
+                }
+            } catch (InterruptedException e) {
+                sm.mHandler.mResultMsg = null;
+            } catch (RemoteException e) {
+                sm.mHandler.mResultMsg = null;
+            }
+            Message resultMsg = sm.mHandler.mResultMsg;
+            sm.recycle();
+            return resultMsg;
+        }
+    }
+
+    /**
+     * Reply to the src handler that we're half connected.
+     *
+     * @param status to be stored in msg.arg1
+     */
+    private void replyHalfConnected(int status, int token) {
+        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
+        msg.arg1 = status;
+        msg.arg2 = token;
+        msg.replyTo = mDstMessenger;
+        mSrcHandler.sendMessage(msg);
+    }
+
+    /**
+     * ServiceConnection to receive call backs.
+     */
+    class AsyncChannelConnection implements ServiceConnection {
+        private int mToken;
+
+        AsyncChannelConnection(int token) {
+            mToken = token;
+        }
+
+        /**
+         * @param token
+         */
+        public void setToken(int token) {
+            mToken = token;
+        }
+
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mDstMessenger = new Messenger(service);
+            replyHalfConnected(STATUS_SUCCESSFUL, mToken);
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+            msg.arg1 = STATUS_SUCCESSFUL;
+            msg.arg2 = mToken;
+            msg.replyTo = mDstMessenger;
+            mSrcHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Log the string.
+     *
+     * @param s
+     */
+    private static void log(String s) {
+        Slog.d(TAG, s);
+    }
+}
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
new file mode 100644
index 0000000..54d3c42
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Slog;
+
+/**
+ * A service that receives Intents and IBinder transactions
+ * as messages via an AsyncChannel.
+ * <p>
+ * The Start Intent arrives as CMD_ASYNC_SERVICE_ON_START_INTENT with msg.arg1 = flags,
+ * msg.arg2 = startId, and msg.obj = intent.
+ * <p>
+ */
+abstract public class AsyncService extends Service {
+    private static final String TAG = "AsyncService";
+
+    protected static final boolean DBG = true;
+
+    /** The command sent when a onStartCommand is invoked */
+    public static final int CMD_ASYNC_SERVICE_ON_START_INTENT = IBinder.LAST_CALL_TRANSACTION;
+
+    /** The command sent when a onDestroy is invoked */
+    public static final int CMD_ASYNC_SERVICE_DESTROY = IBinder.LAST_CALL_TRANSACTION + 1;
+
+    /** Messenger transport */
+    protected Messenger mMessenger;
+
+    /** Message Handler that will receive messages */
+    Handler mHandler;
+
+    public static final class AsyncServiceInfo {
+        /** Message Handler that will receive messages */
+        public Handler mHandler;
+
+        /**
+         * The flags returned by onStartCommand on how to restart.
+         * For instance @see android.app.Service#START_STICKY
+         */
+        public int mRestartFlags;
+    }
+
+    AsyncServiceInfo mAsyncServiceInfo;
+
+    /**
+     * Create the service's handler returning AsyncServiceInfo.
+     *
+     * @return AsyncServiceInfo
+     */
+    abstract public AsyncServiceInfo createHandler();
+
+    /**
+     * Get the handler
+     */
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    /**
+     * onCreate
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAsyncServiceInfo = createHandler();
+        mHandler = mAsyncServiceInfo.mHandler;
+        mMessenger = new Messenger(mHandler);
+    }
+
+    /**
+     * Sends the CMD_ASYNC_SERVICE_ON_START_INTENT message.
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (DBG) Slog.d(TAG, "onStartCommand");
+
+        Message msg = mHandler.obtainMessage();
+        msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT;
+        msg.arg1 = flags;
+        msg.arg2 = startId;
+        msg.obj = intent;
+        mHandler.sendMessage(msg);
+
+        return mAsyncServiceInfo.mRestartFlags;
+    }
+
+    /**
+     * Called when service is destroyed. After returning the
+     * service is dead an no more processing should be expected
+     * to occur.
+     */
+    @Override
+    public void onDestroy() {
+        if (DBG) Slog.d(TAG, "onDestroy");
+
+        Message msg = mHandler.obtainMessage();
+        msg.what = CMD_ASYNC_SERVICE_DESTROY;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Returns the Messenger's binder.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+}
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index aaaa194..7920b72 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -1094,7 +1094,9 @@
      * @param msg that couldn't be handled.
      */
     protected void unhandledMessage(Message msg) {
-        Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
+        if (false) {
+            Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
+        }
     }
 
     /**
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 880fb6e..f8a80b0 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -255,14 +255,21 @@
 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

 #ifdef USE_OPENGL_RENDERER

     if (android::uirenderer::Caches::hasInstance()) {

-        android::uirenderer::Caches::getInstance().textureCache.remove(bitmap);

+        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);

     }

-#endif

+#else // !USE_OPENGL_RENDERER

     delete bitmap;

+#endif

 }

 

 static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {

+#ifdef USE_OPENGL_RENDERER

+    if (android::uirenderer::Caches::hasInstance()) {

+        android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);

+    }

+#else // !USE_OPENGL_RENDERER

     bitmap->setPixels(NULL, NULL);

+#endif // USE_OPENGL_RENDERER

 }

 

 // These must match the int values in Bitmap.java

diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index cafceab..6667756 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -15,11 +15,6 @@
 ** limitations under the License.
 */
 
-// This file was generated from the C++ include file: SkMatrix.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -29,13 +24,21 @@
 
 #include "Matrix.h"
 
+#include <Caches.h>
+
 namespace android {
 
 class SkMatrixGlue {
 public:
 
     static void finalizer(JNIEnv* env, jobject clazz, SkMatrix* obj) {
+#ifdef USE_OPENGL_RENDERER
+        if (android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().resourceCache.destructor(obj);
+        }
+#else // !USE_OPENGL_RENDERER
         delete obj;
+#endif
     }
 
     static SkMatrix* create(JNIEnv* env, jobject clazz, const SkMatrix* src) {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index ca9f371..339c1a4 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -15,11 +15,6 @@
 ** limitations under the License.
 */
 
-// This file was generated from the C++ include file: SkPaint.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -35,6 +30,7 @@
 #include "TextLayout.h"
 
 // temporary for debugging
+#include <Caches.h>
 #include <utils/Log.h>
 
 namespace android {
@@ -67,7 +63,13 @@
     };
 
     static void finalizer(JNIEnv* env, jobject clazz, SkPaint* obj) {
+#ifdef USE_OPENGL_RENDERER
+        if (android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().resourceCache.destructor(obj);
+        }
+#else // !USE_OPENGL_RENDERER
         delete obj;
+#endif
     }
 
     static SkPaint* init(JNIEnv* env, jobject clazz) {
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index ee44747..79051c2 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -53,13 +53,13 @@
 
 static void Shader_destructor(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader)
 {
+    shader->safeUnref();
+    // skiaShader == NULL when not !USE_OPENGL_RENDERER, so no need to delete it outside the ifdef
 #ifdef USE_OPENGL_RENDERER
     if (android::uirenderer::Caches::hasInstance()) {
-        android::uirenderer::Caches::getInstance().gradientCache.remove(shader);
+        android::uirenderer::Caches::getInstance().resourceCache.destructor(skiaShader);
     }
 #endif
-    delete skiaShader;
-    shader->safeUnref();
 }
 
 static bool Shader_getLocalMatrix(JNIEnv* env, jobject, const SkShader* shader, SkMatrix* matrix)
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 7696bb3..53028c3 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "AndroidUnicode"
 
-#include <jni.h>
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include "utils/misc.h"
 #include "utils/Log.h"
@@ -25,14 +25,6 @@
 
 namespace android {
     
-static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
-    jclass excClazz = env->FindClass(exc);
-    LOG_ASSERT(excClazz, "Unable to find class %s", exc);
-
-    env->ThrowNew(excClazz, msg);
-}
-
 static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, 
                     jbyteArray infoArray, int n, jboolean haveInfo)
 {
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 5d8d419..6b90541 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "AndroidUnicode"
 
-#include <jni.h>
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include "utils/misc.h"
 #include "utils/Log.h"
@@ -50,14 +50,6 @@
 
 namespace android {
     
-static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
-    jclass excClazz = env->FindClass(exc);
-    LOG_ASSERT(excClazz, "Unable to find class %s", exc);
-
-    env->ThrowNew(excClazz, msg);
-}
-
 static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, jbyteArray destArray, int count)
 {
     jchar* src = env->GetCharArrayElements(srcArray, NULL);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index cfa9a27..6d848a4 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -87,7 +87,7 @@
 #endif
 }
 
-static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject canvas,
+static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer) {
     RENDERER_LOGD("Destroy OpenGLRenderer");
     delete renderer;
@@ -415,7 +415,7 @@
 }
 
 static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
-        jobject canvas, DisplayList* displayList) {
+        jobject clazz, DisplayList* displayList) {
     delete displayList;
 }
 
diff --git a/core/res/res/raw-ar/loaderror.html b/core/res/res/raw-ar/loaderror.html
index edcd63a..aa9805c 100644
--- a/core/res/res/raw-ar/loaderror.html
+++ b/core/res/res/raw-ar/loaderror.html
@@ -2,7 +2,7 @@
     <head>
         <title>صفحة الويب غير متوفرة</title>
         <style type="text/css">
-            body { margin-top: 0px; padding-top: 0px; }
+            body { margin-top: 0px; padding-top: 0px; direction: rtl; }
             h2   { margin-top: 5px; padding-top: 0px; }
         </style>
 
diff --git a/core/res/res/raw-ar/nodomain.html b/core/res/res/raw-ar/nodomain.html
index bc070f6..2e5849f 100644
--- a/core/res/res/raw-ar/nodomain.html
+++ b/core/res/res/raw-ar/nodomain.html
@@ -2,7 +2,7 @@
     <head>
         <title>صفحة الويب غير متوفرة</title>
         <style type="text/css">
-            body { margin-top: 0px; padding-top: 0px; }
+            body { margin-top: 0px; padding-top: 0px; direction: rtl; }
             h2   { margin-top: 5px; padding-top: 0px; }
         </style>
 
diff --git a/core/res/res/raw-iw/loaderror.html b/core/res/res/raw-iw/loaderror.html
index 8155432..8d5a53f 100644
--- a/core/res/res/raw-iw/loaderror.html
+++ b/core/res/res/raw-iw/loaderror.html
@@ -2,7 +2,7 @@
     <head>
         <title>דף אינטרנט לא זמין</title>
         <style type="text/css">
-            body { margin-top: 0px; padding-top: 0px; }
+            body { margin-top: 0px; padding-top: 0px; direction: rtl; }
             h2   { margin-top: 5px; padding-top: 0px; }
         </style>
 
diff --git a/core/res/res/raw-iw/nodomain.html b/core/res/res/raw-iw/nodomain.html
index f7b9f42..0dcd7f8 100644
--- a/core/res/res/raw-iw/nodomain.html
+++ b/core/res/res/raw-iw/nodomain.html
@@ -2,7 +2,7 @@
     <head>
         <title>דף אינטרנט לא זמין</title>
         <style type="text/css">
-            body { margin-top: 0px; padding-top: 0px; }
+            body { margin-top: 0px; padding-top: 0px; direction: rtl; }
             h2   { margin-top: 5px; padding-top: 0px; }
         </style>
 
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index 813651e..9504d04 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
-         interface.  This name is in the ComponentName flattened format (package/class)  -->
-    <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.tablet.TabletStatusBarService</string>
     <bool name="config_statusBarCanHide">false</bool>
 
     <!-- see comment in values/config.xml -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index de2b930..a5c9a40 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
-         interface.  This name is in the ComponentName flattened format (package/class)  -->
-    <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string>
     <bool name="config_statusBarCanHide">true</bool>
 
     <!-- Do not translate. Defines the slots for the right-hand side icons.  That is to say, the
diff --git a/core/tests/coretests/src/android/os/AsyncChannelTest.java b/core/tests/coretests/src/android/os/AsyncChannelTest.java
new file mode 100644
index 0000000..43c8290
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AsyncChannelTest.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for AsyncChannel.
+ */
+public class AsyncChannelTest extends TestCase {
+    private static final boolean DBG = true;
+    private static final boolean WAIT_FOR_DEBUGGER = true;
+    private static final String TAG = "AsyncChannelTest";
+
+    @SmallTest
+    public void test1() throws Exception {
+        if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+        Log.d(TAG, "test1");
+        assertTrue(1 == 1);
+    }
+}
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index 495fd80..063a10f 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -42,7 +42,7 @@
 
 <p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release including user
 features, developer features, API changes, and bug
-fixes. For  information on developer features and API changes, see the 
+fixes. For  information on developer features and API changes, see the
 <a href="#api">Framework API</a> section.</p>
 
 <p>For developers, the Android {@sdkPlatformVersion} platform is available as a
@@ -62,7 +62,7 @@
 
 <h2 id="features">Platform Highlights</h2>
 
-<p>For a list of new user features and platform highlights, see the <a 
+<p>For a list of new user features and platform highlights, see the <a
 href="http://developer.android.com/sdk/android-2.2-highlights.html">Android
 2.2 Platform Highlights</a> document.</p>
 
@@ -316,8 +316,8 @@
 
 <ul>
   <li> New <code>android:backupAgent</code> attribute of the
-<code>&lt;application&gt;</code> element. Specifies the component name of the 
-BackupAgent subclass provided by the application to handle backup/restore 
+<code>&lt;application&gt;</code> element. Specifies the component name of the
+BackupAgent subclass provided by the application to handle backup/restore
 operations, if any.</li>
   <li> New <code>android:restoreAnyVersion</code> attribute of the
 <code>&lt;application&gt;</code> element. Boolean value that indicates whether
@@ -436,6 +436,11 @@
 <p>Localized UI strings match the locales that are accessible
 through Settings.</p>
 
+<p class="note"><strong>Note:</strong> Android supports more locales than are listed above. However,
+the entire collection of locale strings cannot fit on a single system image, so the above list is
+only what's included in the system image for the SDK. All of Android's supported locales are
+available in the <a href="http://source.android.com/">Android Open Source Project</a>.</p>
+
 <h2 id="skins">Emulator Skins</h2>
 
 <p>The downloadable platform includes a set of emulator skins that you can use
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index cff38b2..bb469e5 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -152,6 +152,7 @@
     bool exceedsFileSizeLimit();
     bool use32BitFileOffset() const;
     bool exceedsFileDurationLimit();
+    bool isFileStreamable() const;
     void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK);
 
     MPEG4Writer(const MPEG4Writer &);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 5bf0ccc..f314b09 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -20,6 +20,7 @@
 		PathCache.cpp \
 		Program.cpp \
 		ProgramCache.cpp \
+		ResourceCache.cpp \
 		SkiaColorFilter.cpp \
 		SkiaShader.cpp \
 		TextureCache.cpp \
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index e6e494d..0c704da 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -34,6 +34,7 @@
 #include "TextDropShadowCache.h"
 #include "FboCache.h"
 #include "Line.h"
+#include "ResourceCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -138,6 +139,7 @@
     TextDropShadowCache dropShadowCache;
     FboCache fboCache;
     GammaFontRenderer fontRenderer;
+    ResourceCache resourceCache;
 
     Line line;
 }; // class Caches
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index b3517c4..a43f164 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -100,39 +100,31 @@
     mTFPlayback.reset(&recorder.mTFRecorder);
     mTFPlayback.setupBuffer(mReader);
 
-    const SkTDArray<const SkFlatBitmap*>& bitmaps = recorder.getBitmaps();
-    mBitmapCount = bitmaps.count();
-    if (mBitmapCount > 0) {
-        mBitmaps = new SkBitmap[mBitmapCount];
-        for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin();
-                flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
-            const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
-            int index = flatBitmap->index() - 1;
-            flatBitmap->unflatten(&mBitmaps[index], &mRCPlayback);
-        }
-    }
+    Caches& caches = Caches::getInstance();
 
-    const SkTDArray<const SkFlatMatrix*>& matrices = recorder.getMatrices();
-    mMatrixCount = matrices.count();
-    if (mMatrixCount > 0) {
-        mMatrices = new SkMatrix[mMatrixCount];
-        for (const SkFlatMatrix** matrixPtr = matrices.begin();
-                matrixPtr != matrices.end(); matrixPtr++) {
-            const SkFlatMatrix* flatMatrix = *matrixPtr;
-            flatMatrix->unflatten(&mMatrices[flatMatrix->index() - 1]);
-        }
+    const Vector<SkBitmap*> &bitmapResources = recorder.getBitmapResources();
+    for (size_t i = 0; i < bitmapResources.size(); i++) {
+        SkBitmap* resource = bitmapResources.itemAt(i);
+        mBitmapResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
     }
-
-    const SkTDArray<const SkFlatPaint*>& paints = recorder.getPaints();
-    mPaintCount = paints.count();
-    if (mPaintCount > 0) {
-        mPaints = new SkPaint[mPaintCount];
-        for (const SkFlatPaint** flatPaintPtr = paints.begin();
-                flatPaintPtr != paints.end(); flatPaintPtr++) {
-            const SkFlatPaint* flatPaint = *flatPaintPtr;
-            int index = flatPaint->index() - 1;
-            flatPaint->unflatten(&mPaints[index], &mRCPlayback, &mTFPlayback);
-        }
+    const Vector<SkMatrix*> &matrixResources = recorder.getMatrixResources();
+    for (size_t i = 0; i < matrixResources.size(); i++) {
+        SkMatrix* resource = matrixResources.itemAt(i);
+        mMatrixResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
+    }
+    const Vector<SkPaint*> &paintResources = recorder.getPaintResources();
+    for (size_t i = 0; i < paintResources.size(); i++) {
+        SkPaint* resource = paintResources.itemAt(i);
+        mPaintResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
+    }
+    const Vector<SkiaShader*> &shaderResources = recorder.getShaderResources();
+    for (size_t i = 0; i < shaderResources.size(); i++) {
+        SkiaShader* resource = shaderResources.itemAt(i);
+        mShaderResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
     }
 
     mPathHeap = recorder.mPathHeap;
@@ -143,23 +135,32 @@
     sk_free((void*) mReader.base());
 
     Caches& caches = Caches::getInstance();
-    for (int i = 0; i < mBitmapCount; i++) {
-        caches.textureCache.remove(&mBitmaps[i]);
+
+    for (size_t i = 0; i < mBitmapResources.size(); i++) {
+        SkBitmap* resource = mBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
     }
-
-    delete[] mBitmaps;
-    delete[] mMatrices;
-    delete[] mPaints;
-
+    mBitmapResources.clear();
+    for (size_t i = 0; i < mMatrixResources.size(); i++) {
+        SkMatrix* resource = mMatrixResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mMatrixResources.clear();
+    for (size_t i = 0; i < mPaintResources.size(); i++) {
+        SkPaint* resource = mPaintResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mPaintResources.clear();
+    for (size_t i = 0; i < mShaderResources.size(); i++) {
+        SkiaShader* resource = mShaderResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mShaderResources.clear();
     mPathHeap->safeUnref();
 }
 
 void DisplayList::init() {
-    mBitmaps = NULL;
-    mMatrices = NULL;
-    mPaints = NULL;
     mPathHeap = NULL;
-    mBitmapCount = mMatrixCount = mPaintCount = 0;
 }
 
 void DisplayList::replay(OpenGLRenderer& renderer) {
@@ -280,7 +281,7 @@
             }
             break;
             case SetupShader: {
-                // TODO: Implement
+                renderer.setupShader(getShader());
             }
             break;
             case ResetColorFilter: {
@@ -309,7 +310,6 @@
 
 DisplayListRenderer::DisplayListRenderer():
         mHeap(HEAP_BLOCK_SIZE), mWriter(MIN_WRITER_SIZE) {
-    mBitmapIndex = mMatrixIndex = mPaintIndex = 1;
     mPathHeap = NULL;
 }
 
@@ -323,15 +323,33 @@
         mPathHeap = NULL;
     }
 
-    mBitmaps.reset();
-    mMatrices.reset();
-    mPaints.reset();
-
     mWriter.reset();
     mHeap.reset();
 
     mRCRecorder.reset();
     mTFRecorder.reset();
+
+    Caches& caches = Caches::getInstance();
+    for (size_t i = 0; i < mBitmapResources.size(); i++) {
+        SkBitmap* resource = mBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mBitmapResources.clear();
+    for (size_t i = 0; i < mMatrixResources.size(); i++) {
+        SkMatrix* resource = mMatrixResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mMatrixResources.clear();
+    for (size_t i = 0; i < mPaintResources.size(); i++) {
+        SkPaint* resource = mPaintResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mPaintResources.clear();
+    for (size_t i = 0; i < mShaderResources.size(); i++) {
+        SkiaShader* resource = mShaderResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mShaderResources.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -380,7 +398,7 @@
 }
 
 int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
-        const SkPaint* p, int flags) {
+        SkPaint* p, int flags) {
     addOp(DisplayList::SaveLayer);
     addBounds(left, top, right, bottom);
     addPaint(p);
@@ -427,15 +445,15 @@
 }
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
-        const SkPaint* paint) {
+        SkPaint* paint) {
     addOp(DisplayList::DrawBitmap);
     addBitmap(bitmap);
     addPoint(left, top);
     addPaint(paint);
 }
 
-void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix,
-        const SkPaint* paint) {
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
+        SkPaint* paint) {
     addOp(DisplayList::DrawBitmapMatrix);
     addBitmap(bitmap);
     addMatrix(matrix);
@@ -444,7 +462,7 @@
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
         float srcRight, float srcBottom, float dstLeft, float dstTop,
-        float dstRight, float dstBottom, const SkPaint* paint) {
+        float dstRight, float dstBottom, SkPaint* paint) {
     addOp(DisplayList::DrawBitmapRect);
     addBitmap(bitmap);
     addBounds(srcLeft, srcTop, srcRight, srcBottom);
@@ -454,7 +472,7 @@
 
 void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-        float left, float top, float right, float bottom, const SkPaint* paint) {
+        float left, float top, float right, float bottom, SkPaint* paint) {
     addOp(DisplayList::DrawPatch);
     addBitmap(bitmap);
     addInts(xDivs, width);
@@ -471,7 +489,7 @@
 }
 
 void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
-        const SkPaint* paint) {
+        SkPaint* paint) {
     addOp(DisplayList::DrawRect);
     addBounds(left, top, right, bottom);
     addPaint(paint);
@@ -483,7 +501,7 @@
     addPaint(paint);
 }
 
-void DisplayListRenderer::drawLines(float* points, int count, const SkPaint* paint) {
+void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
     addOp(DisplayList::DrawLines);
     addFloats(points, count);
     addPaint(paint);
@@ -504,8 +522,8 @@
 }
 
 void DisplayListRenderer::setupShader(SkiaShader* shader) {
-    // TODO: Implement
-    OpenGLRenderer::setupShader(shader);
+    addOp(DisplayList::SetupShader);
+    addShader(shader);
 }
 
 void DisplayListRenderer::resetColorFilter() {
@@ -531,58 +549,5 @@
     OpenGLRenderer::setupShadow(radius, dx, dy, color);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Recording management
-///////////////////////////////////////////////////////////////////////////////
-
-int DisplayListRenderer::find(SkTDArray<const SkFlatPaint*>& paints, const SkPaint* paint) {
-    if (paint == NULL) {
-        return 0;
-    }
-
-    SkFlatPaint* flat = SkFlatPaint::Flatten(&mHeap, *paint, mPaintIndex,
-            &mRCRecorder, &mTFRecorder);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(),
-            paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void) mHeap.unalloc(flat);
-        return paints[index]->index();
-    }
-
-    index = ~index;
-    *paints.insert(index) = flat;
-    return mPaintIndex++;
-}
-
-int DisplayListRenderer::find(SkTDArray<const SkFlatMatrix*>& matrices, const SkMatrix* matrix) {
-    if (matrix == NULL) {
-        return 0;
-    }
-
-    SkFlatMatrix* flat = SkFlatMatrix::Flatten(&mHeap, *matrix, mMatrixIndex);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(),
-            matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void) mHeap.unalloc(flat);
-        return matrices[index]->index();
-    }
-    index = ~index;
-    *matrices.insert(index) = flat;
-    return mMatrixIndex++;
-}
-
-int DisplayListRenderer::find(SkTDArray<const SkFlatBitmap*>& bitmaps, const SkBitmap& bitmap) {
-    SkFlatBitmap* flat = SkFlatBitmap::Flatten(&mHeap, bitmap, mBitmapIndex, &mRCRecorder);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(),
-            bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void) mHeap.unalloc(flat);
-        return bitmaps[index]->index();
-    }
-    index = ~index;
-    *bitmaps.insert(index) = flat;
-    return mBitmapIndex++;
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 9e6d5b1..c8cd801 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -128,8 +128,11 @@
     };
 
     SkBitmap* getBitmap() {
-        int index = getInt();
-        return &mBitmaps[index - 1];
+        return (SkBitmap*) getInt();
+    }
+
+    SkiaShader* getShader() {
+        return (SkiaShader*) getInt();
     }
 
     inline int getIndex() {
@@ -141,11 +144,7 @@
     }
 
     SkMatrix* getMatrix() {
-        int index = getInt();
-        if (index == 0) {
-            return NULL;
-        }
-        return &mMatrices[index - 1];
+        return (SkMatrix*) getInt();
     }
 
     SkPath* getPath() {
@@ -153,11 +152,7 @@
     }
 
     SkPaint* getPaint() {
-        int index = getInt();
-        if (index == 0) {
-            return NULL;
-        }
-        return &mPaints[index - 1];
+        return (SkPaint*) getInt();
     }
 
     inline float getFloat() {
@@ -186,14 +181,10 @@
 
     PathHeap* mPathHeap;
 
-    SkBitmap* mBitmaps;
-    int mBitmapCount;
-
-    SkMatrix* mMatrices;
-    int mMatrixCount;
-
-    SkPaint* mPaints;
-    int mPaintCount;
+    Vector<SkBitmap*> mBitmapResources;
+    Vector<SkMatrix*> mMatrixResources;
+    Vector<SkPaint*> mPaintResources;
+    Vector<SkiaShader*> mShaderResources;
 
     mutable SkFlattenableReadBuffer mReader;
 
@@ -224,7 +215,7 @@
     void restoreToCount(int saveCount);
 
     int saveLayer(float left, float top, float right, float bottom,
-            const SkPaint* p, int flags);
+            SkPaint* p, int flags);
 
     void translate(float dx, float dy);
     void rotate(float degrees);
@@ -235,18 +226,18 @@
 
     bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
-    void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
     void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint);
+            float dstRight, float dstBottom, SkPaint* paint);
     void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
             const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-            float left, float top, float right, float bottom, const SkPaint* paint);
+            float left, float top, float right, float bottom, SkPaint* paint);
     void drawColor(int color, SkXfermode::Mode mode);
-    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
     void drawPath(SkPath* path, SkPaint* paint);
-    void drawLines(float* points, int count, const SkPaint* paint);
+    void drawLines(float* points, int count, SkPaint* paint);
     void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
 
     void resetShader();
@@ -268,16 +259,20 @@
         return mWriter;
     }
 
-    const SkTDArray<const SkFlatBitmap*>& getBitmaps() const {
-        return mBitmaps;
+    const Vector<SkBitmap*>& getBitmapResources() const {
+        return mBitmapResources;
     }
 
-    const SkTDArray<const SkFlatMatrix*>& getMatrices() const {
-        return mMatrices;
+    const Vector<SkMatrix*>& getMatrixResources() const {
+        return mMatrixResources;
     }
 
-    const SkTDArray<const SkFlatPaint*>& getPaints() const {
-        return mPaints;
+    const Vector<SkPaint*>& getPaintResources() const {
+        return mPaintResources;
+    }
+
+    const Vector<SkiaShader*>& getShaderResources() const {
+        return mShaderResources;
     }
 
 private:
@@ -338,34 +333,40 @@
         addInt(mPathHeap->append(*path));
     }
 
-    int find(SkTDArray<const SkFlatPaint*>& paints, const SkPaint* paint);
-
-    inline void addPaint(const SkPaint* paint) {
-        addInt(find(mPaints, paint));
+    inline void addPaint(SkPaint* paint) {
+        addInt((int)paint);
+        mPaintResources.add(paint);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(paint);
     }
 
-    int find(SkTDArray<const SkFlatMatrix*>& matrices, const SkMatrix* matrix);
-
-    inline void addMatrix(const SkMatrix* matrix) {
-        addInt(find(mMatrices, matrix));
+    inline void addMatrix(SkMatrix* matrix) {
+        addInt((int)matrix);
+        mMatrixResources.add(matrix);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(matrix);
     }
 
-    int find(SkTDArray<const SkFlatBitmap*>& bitmaps, const SkBitmap& bitmap);
+    inline void addBitmap(SkBitmap* bitmap) {
+        addInt((int)bitmap);
+        mBitmapResources.add(bitmap);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(bitmap);
+    }
 
-    inline void addBitmap(const SkBitmap* bitmap) {
-        addInt(find(mBitmaps, *bitmap));
+    inline void addShader(SkiaShader* shader) {
+        addInt((int)shader);
+        mShaderResources.add(shader);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(shader);
     }
 
     SkChunkAlloc mHeap;
 
-    int mBitmapIndex;
-    SkTDArray<const SkFlatBitmap*> mBitmaps;
-
-    int mMatrixIndex;
-    SkTDArray<const SkFlatMatrix*> mMatrices;
-
-    int mPaintIndex;
-    SkTDArray<const SkFlatPaint*> mPaints;
+    Vector<SkBitmap*> mBitmapResources;
+    Vector<SkMatrix*> mMatrixResources;
+    Vector<SkPaint*> mPaintResources;
+    Vector<SkiaShader*> mShaderResources;
 
     PathHeap* mPathHeap;
     SkWriter32 mWriter;
diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp
index d492e23..fe75ca2 100644
--- a/libs/hwui/OpenGLDebugRenderer.cpp
+++ b/libs/hwui/OpenGLDebugRenderer.cpp
@@ -42,21 +42,21 @@
 }
 
 int OpenGLDebugRenderer::saveLayer(float left, float top, float right, float bottom,
-        const SkPaint* p, int flags) {
+        SkPaint* p, int flags) {
     mPrimitivesCount++;
     StopWatch w("saveLayer");
     return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags);
 }
 
 void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
-        const SkPaint* paint) {
+        SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawBitmap");
     OpenGLRenderer::drawBitmap(bitmap, left, top, paint);
 }
 
-void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix,
-        const SkPaint* paint) {
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
+        SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawBitmapMatrix");
     OpenGLRenderer::drawBitmap(bitmap, matrix, paint);
@@ -64,7 +64,7 @@
 
 void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
         float srcRight, float srcBottom, float dstLeft, float dstTop,
-        float dstRight, float dstBottom, const SkPaint* paint) {
+        float dstRight, float dstBottom, SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawBitmapRect");
     OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
@@ -73,7 +73,7 @@
 
 void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-        float left, float top, float right, float bottom, const SkPaint* paint) {
+        float left, float top, float right, float bottom, SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawPatch");
     OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
@@ -87,7 +87,7 @@
 }
 
 void OpenGLDebugRenderer::drawRect(float left, float top, float right, float bottom,
-        const SkPaint* paint) {
+        SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawRect");
     OpenGLRenderer::drawRect(left, top, right, bottom, paint);
@@ -99,7 +99,7 @@
     OpenGLRenderer::drawPath(path, paint);
 }
 
-void OpenGLDebugRenderer::drawLines(float* points, int count, const SkPaint* paint) {
+void OpenGLDebugRenderer::drawLines(float* points, int count, SkPaint* paint) {
     mPrimitivesCount++;
     StopWatch w("drawLines");
     OpenGLRenderer::drawLines(points, count, paint);
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
index 4997ef3..ce6a4aa 100644
--- a/libs/hwui/OpenGLDebugRenderer.h
+++ b/libs/hwui/OpenGLDebugRenderer.h
@@ -38,20 +38,20 @@
     void finish();
 
     int saveLayer(float left, float top, float right, float bottom,
-            const SkPaint* p, int flags);
+            SkPaint* p, int flags);
 
-    void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
     void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint);
+            float dstRight, float dstBottom, SkPaint* paint);
     void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
             const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-            float left, float top, float right, float bottom, const SkPaint* paint);
+            float left, float top, float right, float bottom, SkPaint* paint);
     void drawColor(int color, SkXfermode::Mode mode);
-    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
     void drawPath(SkPath* path, SkPaint* paint);
-    void drawLines(float* points, int count, const SkPaint* paint);
+    void drawLines(float* points, int count, SkPaint* paint);
     void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 633d778..17ef598 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -252,7 +252,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
-        const SkPaint* p, int flags) {
+        SkPaint* p, int flags) {
     const GLuint previousFbo = mSnapshot->fbo;
     const int count = saveSnapshot(flags);
 
@@ -623,7 +623,7 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
-void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
     const float right = left + bitmap->width();
     const float bottom = top + bitmap->height();
 
@@ -639,7 +639,7 @@
     drawTextureRect(left, top, right, bottom, texture, paint);
 }
 
-void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
     Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
     const mat4 transform(*matrix);
     transform.mapRect(r);
@@ -659,7 +659,7 @@
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
          float srcLeft, float srcTop, float srcRight, float srcBottom,
          float dstLeft, float dstTop, float dstRight, float dstBottom,
-         const SkPaint* paint) {
+         SkPaint* paint) {
     if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
         return;
     }
@@ -693,7 +693,7 @@
 
 void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-        float left, float top, float right, float bottom, const SkPaint* paint) {
+        float left, float top, float right, float bottom, SkPaint* paint) {
     if (quickReject(left, top, right, bottom)) {
         return;
     }
@@ -719,7 +719,7 @@
     }
 }
 
-void OpenGLRenderer::drawLines(float* points, int count, const SkPaint* paint) {
+void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
     if (mSnapshot->invisible) return;
 
     int alpha;
@@ -791,7 +791,7 @@
     drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
 }
 
-void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
+void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
     if (quickReject(left, top, right, bottom)) {
         return;
     }
@@ -1206,7 +1206,7 @@
 }
 
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
-        const Texture* texture, const SkPaint* paint) {
+        const Texture* texture, SkPaint* paint) {
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
@@ -1334,7 +1334,7 @@
     TextureVertex::setUV(v++, u2, v2);
 }
 
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
     if (paint) {
         if (!mExtensions.hasFramebufferFetch()) {
             const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1d8a3d9..b7615fe 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -79,7 +79,7 @@
     virtual void restoreToCount(int saveCount);
 
     virtual int saveLayer(float left, float top, float right, float bottom,
-            const SkPaint* p, int flags);
+            SkPaint* p, int flags);
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
             int alpha, int flags);
 
@@ -96,18 +96,18 @@
     bool quickReject(float left, float top, float right, float bottom);
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
-    virtual void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
-    virtual void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint);
+            float dstRight, float dstBottom, SkPaint* paint);
     virtual void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
             const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-            float left, float top, float right, float bottom, const SkPaint* paint);
+            float left, float top, float right, float bottom, SkPaint* paint);
     virtual void drawColor(int color, SkXfermode::Mode mode);
-    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+    virtual void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
     virtual void drawPath(SkPath* path, SkPaint* paint);
-    virtual void drawLines(float* points, int count, const SkPaint* paint);
+    virtual void drawLines(float* points, int count, SkPaint* paint);
     virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint);
 
@@ -231,7 +231,7 @@
      * @param paint The paint containing the alpha, blending mode, etc.
      */
     void drawTextureRect(float left, float top, float right, float bottom,
-            const Texture* texture, const SkPaint* paint);
+            const Texture* texture, SkPaint* paint);
 
     /**
      * Draws a textured mesh with the specified texture. If the indices are omitted,
@@ -357,7 +357,7 @@
      * @param alpha Where to store the resulting alpha
      * @param mode Where to store the resulting xfermode
      */
-    inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+    inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
 
     /**
      * Binds the specified texture with the specified wrap modes.
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
new file mode 100644
index 0000000..20b8d6c
--- /dev/null
+++ b/libs/hwui/ResourceCache.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SkPixelRef.h>
+#include "ResourceCache.h"
+#include "Caches.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Resource cache
+///////////////////////////////////////////////////////////////////////////////
+
+void ResourceCache::logCache() {
+    LOGD("ResourceCache: cacheReport:");
+    for (size_t i = 0; i < mCache->size(); ++i) {
+        ResourceReference* ref = mCache->valueAt(i);
+        LOGD("  ResourceCache: mCache(%d): resource, ref = 0x%p, 0x%p",
+                i, mCache->keyAt(i), mCache->valueAt(i));
+        LOGD("  ResourceCache: mCache(%d): refCount, recycled, destroyed, type = %d, %d, %d, %d",
+                i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
+    }
+}
+
+ResourceCache::ResourceCache() {
+    mCache = new KeyedVector<void *, ResourceReference *>();
+}
+
+ResourceCache::~ResourceCache() {
+    delete mCache;
+}
+
+void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
+    for (size_t i = 0; i < mCache->size(); ++i) {
+        void* ref = mCache->valueAt(i);
+    }
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL || mCache->size() == 0) {
+        ref = new ResourceReference(resourceType);
+        mCache->add(resource, ref);
+    }
+    ref->refCount++;
+}
+
+void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
+    bitmapResource->pixelRef()->safeRef();
+    bitmapResource->getColorTable()->safeRef();
+    incrementRefcount((void*)bitmapResource, kBitmap);
+}
+
+void ResourceCache::incrementRefcount(SkMatrix* matrixResource) {
+    incrementRefcount((void*)matrixResource, kMatrix);
+}
+
+void ResourceCache::incrementRefcount(SkPaint* paintResource) {
+    incrementRefcount((void*)paintResource, kPaint);
+}
+
+void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
+    shaderResource->getSkShader()->safeRef();
+    incrementRefcount((void*)shaderResource, kShader);
+}
+
+void ResourceCache::decrementRefcount(void* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
+        return;
+    }
+    ref->refCount--;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+    }
+}
+
+void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
+    bitmapResource->pixelRef()->safeUnref();
+    bitmapResource->getColorTable()->safeUnref();
+    decrementRefcount((void*)bitmapResource);
+}
+
+void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
+    shaderResource->getSkShader()->safeUnref();
+    decrementRefcount((void*)shaderResource);
+}
+
+void ResourceCache::recycle(SkBitmap* resource) {
+    if (mCache->indexOfKey(resource) < 0) {
+        // not tracking this resource; just recycle the pixel data
+        resource->setPixels(NULL, NULL);
+        return;
+    }
+    recycle((void*) resource);
+}
+
+void ResourceCache::recycle(void* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
+        return;
+    }
+    ref->recycled = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+    }
+}
+
+void ResourceCache::destructor(SkBitmap* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        if (Caches::hasInstance()) {
+            Caches::getInstance().textureCache.remove(resource);
+        }
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::destructor(SkMatrix* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::destructor(SkPaint* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::destructor(SkiaShader* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        if (Caches::hasInstance()) {
+            Caches::getInstance().gradientCache.remove(resource->getSkShader());
+        }
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::deleteResourceReference(void* resource, ResourceReference* ref) {
+    if (ref->recycled && ref->resourceType == kBitmap) {
+        ((SkBitmap*) resource)->setPixels(NULL, NULL);
+    }
+    if (ref->destroyed) {
+        switch (ref->resourceType) {
+            case kBitmap:
+            {
+                SkBitmap* bitmap = (SkBitmap*)resource;
+                if (Caches::hasInstance()) {
+                    Caches::getInstance().textureCache.remove(bitmap);
+                }
+                delete bitmap;
+            }
+            break;
+            case kMatrix:
+                delete (SkMatrix*) resource;
+                break;
+            case kPaint:
+                delete (SkPaint*) resource;
+                break;
+            case kShader:
+                SkiaShader* shader = (SkiaShader*)resource;
+                if (Caches::hasInstance()) {
+                    Caches::getInstance().gradientCache.remove(shader->getSkShader());
+                }
+                delete shader;
+                break;
+        }
+    }
+    mCache->removeItem(resource);
+    delete ref;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
new file mode 100644
index 0000000..cda2718
--- /dev/null
+++ b/libs/hwui/ResourceCache.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_RESOURCE_CACHE_H
+#define ANDROID_UI_RESOURCE_CACHE_H
+
+#include <SkBitmap.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkiaShader.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Type of Resource being cached
+ */
+enum ResourceType {
+    kBitmap,
+    kMatrix,
+    kPaint,
+    kShader,
+};
+
+class ResourceReference {
+public:
+
+    ResourceReference() { refCount = 0; recycled = false; destroyed = false;}
+    ResourceReference(ResourceType type) {
+        refCount = 0; recycled = false; destroyed = false; resourceType = type;
+    }
+
+    int refCount;
+    bool recycled;
+    bool destroyed;
+    ResourceType resourceType;
+};
+
+class ResourceCache {
+    KeyedVector<void *, ResourceReference *>* mCache;
+public:
+    ResourceCache();
+    ~ResourceCache();
+    void incrementRefcount(SkBitmap* resource);
+    void incrementRefcount(SkMatrix* resource);
+    void incrementRefcount(SkPaint* resource);
+    void incrementRefcount(SkiaShader* resource);
+    void incrementRefcount(const void* resource, ResourceType resourceType);
+    void decrementRefcount(void* resource);
+    void decrementRefcount(SkBitmap* resource);
+    void decrementRefcount(SkiaShader* resource);
+    void recycle(void* resource);
+    void recycle(SkBitmap* resource);
+    void destructor(SkBitmap* resource);
+    void destructor(SkMatrix* resource);
+    void destructor(SkPaint* resource);
+    void destructor(SkiaShader* resource);
+private:
+    void deleteResourceReference(void* resource, ResourceReference* ref);
+    void incrementRefcount(void* resource, ResourceType resourceType);
+    void logCache();
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_RESOURCE_CACHE_H
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 2565e65..011991a 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -60,6 +60,10 @@
     virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
             GLuint* textureUnit);
 
+    inline SkShader *getSkShader() {
+        return mKey;
+    }
+
     inline bool blend() const {
         return mBlend;
     }
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp
index 7ebde78..1f62ac5 100644
--- a/libs/utils/StreamingZipInflater.cpp
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_NDEBUG 1
 #define LOG_TAG "szipinf"
 #include <utils/Log.h>
 
@@ -157,7 +158,7 @@
             */
             int result = Z_OK;
             if (mStreamNeedsInit) {
-                LOGI("Initializing zlib to inflate");
+                LOGD("Initializing zlib to inflate");
                 result = inflateInit2(&mInflateState, -MAX_WBITS);
                 mStreamNeedsInit = false;
             }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d172eef..8c6c963 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -412,6 +412,12 @@
             LOGI("interrupting the connection process");
             mConnectingDataSource->disconnect();
         }
+
+        if (mFlags & PREPARING_CONNECTED) {
+            // We are basically done preparing, we're just buffering
+            // enough data to start playback, we can safely interrupt that.
+            finishAsyncPrepare_l();
+        }
     }
 
     while (mFlags & PREPARING) {
@@ -1123,7 +1129,7 @@
     }
 
     if (mAudioPlayer != NULL) {
-        LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
+        LOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
 
         // If we don't have a video time, seek audio to the originally
         // requested seek time instead.
@@ -1625,7 +1631,7 @@
     }
 
     mPrepareResult = err;
-    mFlags &= ~(PREPARING|PREPARE_CANCELLED);
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
     mAsyncPrepareEvent = NULL;
     mPreparedCondition.broadcast();
 }
@@ -1673,6 +1679,8 @@
         }
     }
 
+    mFlags |= PREPARING_CONNECTED;
+
     if (mCachedSource != NULL || mRTSPController != NULL) {
         postBufferingEvent_l();
     } else {
@@ -1692,7 +1700,7 @@
     }
 
     mPrepareResult = OK;
-    mFlags &= ~(PREPARING|PREPARE_CANCELLED);
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
     mFlags |= PREPARED;
     mAsyncPrepareEvent = NULL;
     mPreparedCondition.broadcast();
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e53b0a0..515c22f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -309,11 +309,10 @@
     //
     // Statistical analysis shows that metadata usually accounts
     // for a small portion of the total file size, usually < 0.6%.
-    // Currently, lets set to 0.4% for now.
 
-    // The default MIN_MOOV_BOX_SIZE is set to 0.4% x 1MB,
+    // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
     // where 1MB is the common file size limit for MMS application.
-    // The default MAX _MOOV_BOX_SIZE value is based on about 4
+    // The default MAX _MOOV_BOX_SIZE value is based on about 3
     // minute video recording with a bit rate about 3 Mbps, because
     // statistics also show that most of the video captured are going
     // to be less than 3 minutes.
@@ -321,20 +320,33 @@
     // If the estimation is wrong, we will pay the price of wasting
     // some reserved space. This should not happen so often statistically.
     static const int32_t factor = mUse32BitOffset? 1: 2;
-    static const int64_t MIN_MOOV_BOX_SIZE = 4 * 1024;  // 4 KB
+    static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024;  // 3 KB
     static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
     int64_t size = MIN_MOOV_BOX_SIZE;
 
+    // Max file size limit is set
     if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
-        size = mMaxFileSizeLimitBytes * 4 / 1000;
-    } else if (mMaxFileDurationLimitUs != 0) {
-        if (bitRate <= 0) {
-            // We could not estimate the file size since bitRate is not set.
-            size = MIN_MOOV_BOX_SIZE;
-        } else {
-            size = ((mMaxFileDurationLimitUs * bitRate * 4) / 1000 / 8000000);
+        size = mMaxFileSizeLimitBytes * 6 / 1000;
+    }
+
+    // Max file duration limit is set
+    if (mMaxFileDurationLimitUs != 0) {
+        if (bitRate > 0) {
+            int64_t size2 =
+                ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000);
+            if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
+                // When both file size and duration limits are set,
+                // we use the smaller limit of the two.
+                if (size > size2) {
+                    size = size2;
+                }
+            } else {
+                // Only max file duration limit is set
+                size = size2;
+            }
         }
     }
+
     if (size < MIN_MOOV_BOX_SIZE) {
         size = MIN_MOOV_BOX_SIZE;
     }
@@ -787,6 +799,10 @@
     write(data, 1, size, mFile);
 }
 
+bool MPEG4Writer::isFileStreamable() const {
+    return mStreamableFile;
+}
+
 bool MPEG4Writer::exceedsFileSizeLimit() {
     // No limit
     if (mMaxFileSizeLimitBytes == 0) {
@@ -799,7 +815,7 @@
         nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
     }
 
-    return (nTotalBytesEstimate  + 1024 >= mMaxFileSizeLimitBytes);
+    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
 }
 
 bool MPEG4Writer::exceedsFileDurationLimit() {
@@ -887,12 +903,16 @@
 
     int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);
 
-    mEstimatedTrackSizeBytes = mMdatSizeBytes +             // media data size
-                               mNumStscTableEntries * 12 +  // stsc box size
-                               mNumStssTableEntries * 4 +   // stss box size
-                               mNumSttsTableEntries * 8 +   // stts box size
-                               stcoBoxSizeBytes +           // stco box size
-                               stszBoxSizeBytes;            // stsz box size
+    mEstimatedTrackSizeBytes = mMdatSizeBytes;  // media data size
+    if (!mOwner->isFileStreamable()) {
+        // Reserved free space is not large enough to hold
+        // all meta data and thus wasted.
+        mEstimatedTrackSizeBytes += mNumStscTableEntries * 12 +  // stsc box size
+                                    mNumStssTableEntries * 4 +   // stss box size
+                                    mNumSttsTableEntries * 8 +   // stts box size
+                                    stcoBoxSizeBytes +           // stco box size
+                                    stszBoxSizeBytes;            // stsz box size
+    }
 }
 
 void MPEG4Writer::Track::addOneStscTableEntry(
@@ -2194,6 +2214,9 @@
                         CHECK(mCodecSpecificData);
                         CHECK(mCodecSpecificDataSize > 0);
 
+                        // Make sure all sizes encode to a single byte.
+                        CHECK(mCodecSpecificDataSize + 23 < 128);
+
                         mOwner->writeInt32(0);     // version=0, flags=0
                         mOwner->writeInt8(0x03);   // ES_DescrTag
                         mOwner->writeInt8(23 + mCodecSpecificDataSize);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 560d0f1..2e368b6 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -744,7 +744,8 @@
         mQuirks &= ~kOutputBuffersAreUnreadable;
     }
 
-    if (!mIsEncoder
+    if (mNativeWindow != NULL
+        && !mIsEncoder
         && !strncasecmp(mMIME, "video/", 6)
         && !strncmp(mComponentName, "OMX.", 4)) {
         status_t err = initNativeWindow();
@@ -1606,7 +1607,7 @@
 }
 
 status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
-    if (mNativeWindow != 0 && portIndex == kPortIndexOutput) {
+    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
         return allocateOutputBuffersFromNativeWindow();
     }
 
@@ -1676,6 +1677,7 @@
 
         info.mBuffer = buffer;
         info.mOwnedByComponent = false;
+        info.mOwnedByNativeWindow = false;
         info.mMem = mem;
         info.mMediaBuffer = NULL;
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 0837be8..f4a2024 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -111,6 +111,11 @@
         AUDIO_AT_EOS        = 256,
         VIDEO_AT_EOS        = 512,
         AUTO_LOOPING        = 1024,
+
+        // We are basically done preparing but are currently buffering
+        // sufficient data to begin playback and finish the preparation phase
+        // for good.
+        PREPARING_CONNECTED = 2048,
     };
 
     mutable Mutex mLock;
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index f928c06..824fb65 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -23,11 +23,13 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/MediaErrors.h>
 
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <netdb.h>
+#include <openssl/md5.h>
 #include <sys/socket.h>
 
 namespace android {
@@ -37,6 +39,7 @@
 
 ARTSPConnection::ARTSPConnection()
     : mState(DISCONNECTED),
+      mAuthType(NONE),
       mSocket(-1),
       mConnectionID(0),
       mNextCSeq(0),
@@ -114,10 +117,13 @@
 
 // static
 bool ARTSPConnection::ParseURL(
-        const char *url, AString *host, unsigned *port, AString *path) {
+        const char *url, AString *host, unsigned *port, AString *path,
+        AString *user, AString *pass) {
     host->clear();
     *port = 0;
     path->clear();
+    user->clear();
+    pass->clear();
 
     if (strncasecmp("rtsp://", url, 7)) {
         return false;
@@ -133,6 +139,24 @@
         path->setTo(slashPos);
     }
 
+    ssize_t atPos = host->find("@");
+
+    if (atPos >= 0) {
+        // Split of user:pass@ from hostname.
+
+        AString userPass(*host, 0, atPos);
+        host->erase(0, atPos + 1);
+
+        ssize_t colonPos = userPass.find(":");
+
+        if (colonPos < 0) {
+            *user = userPass;
+        } else {
+            user->setTo(userPass, 0, colonPos);
+            pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
+        }
+    }
+
     const char *colonPos = strchr(host->c_str(), ':');
 
     if (colonPos != NULL) {
@@ -187,7 +211,12 @@
 
     AString host, path;
     unsigned port;
-    if (!ParseURL(url.c_str(), &host, &port, &path)) {
+    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
+            || (mUser.size() > 0 && mPass.size() == 0)) {
+        // If we have a user name but no password we have to give up
+        // right here, since we currently have no way of asking the user
+        // for this information.
+
         LOGE("Malformed rtsp url %s", url.c_str());
 
         reply->setInt32("result", ERROR_MALFORMED);
@@ -197,6 +226,10 @@
         return;
     }
 
+    if (mUser.size() > 0) {
+        LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
+    }
+
     struct hostent *ent = gethostbyname(host.c_str());
     if (ent == NULL) {
         LOGE("Unknown host %s", host.c_str());
@@ -262,6 +295,11 @@
     reply->setInt32("result", OK);
     mState = DISCONNECTED;
 
+    mUser.clear();
+    mPass.clear();
+    mAuthType = NONE;
+    mNonce.clear();
+
     reply->post();
 }
 
@@ -335,6 +373,12 @@
     AString request;
     CHECK(msg->findString("request", &request));
 
+    // Just in case we need to re-issue the request with proper authentication
+    // later, stash it away.
+    reply->setString("original-request", request.c_str(), request.size());
+
+    addAuthentication(&request);
+
     // Find the boundary between headers and the body.
     ssize_t i = request.find("\r\n\r\n");
     CHECK_GE(i, 0);
@@ -347,7 +391,7 @@
 
     request.insert(cseqHeader, i + 2);
 
-    LOGV("%s", request.c_str());
+    LOGV("request: '%s'", request.c_str());
 
     size_t numBytesSent = 0;
     while (numBytesSent < request.size()) {
@@ -612,6 +656,30 @@
         }
     }
 
+    if (response->mStatusCode == 401) {
+        if (mAuthType == NONE && mUser.size() > 0
+                && parseAuthMethod(response)) {
+            ssize_t i;
+            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
+            CHECK_GE(i, 0);
+
+            sp<AMessage> reply = mPendingRequests.valueAt(i);
+            mPendingRequests.removeItemsAt(i);
+
+            AString request;
+            CHECK(reply->findString("original-request", &request));
+
+            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+            msg->setMessage("reply", reply);
+            msg->setString("request", request.c_str(), request.size());
+
+            LOGI("re-sending request with authentication headers...");
+            onSendRequest(msg);
+
+            return true;
+        }
+    }
+
     return notifyResponseListener(response);
 }
 
@@ -628,26 +696,47 @@
     return true;
 }
 
-bool ARTSPConnection::notifyResponseListener(
-        const sp<ARTSPResponse> &response) {
+status_t ARTSPConnection::findPendingRequest(
+        const sp<ARTSPResponse> &response, ssize_t *index) const {
+    *index = 0;
+
     ssize_t i = response->mHeaders.indexOfKey("cseq");
 
     if (i < 0) {
-        return true;
+        // This is an unsolicited server->client message.
+        return OK;
     }
 
     AString value = response->mHeaders.valueAt(i);
 
     unsigned long cseq;
     if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
-        return false;
+        return ERROR_MALFORMED;
     }
 
     i = mPendingRequests.indexOfKey(cseq);
 
     if (i < 0) {
-        // Unsolicited response?
-        TRESPASS();
+        return -ENOENT;
+    }
+
+    *index = i;
+
+    return OK;
+}
+
+bool ARTSPConnection::notifyResponseListener(
+        const sp<ARTSPResponse> &response) {
+    ssize_t i;
+    status_t err = findPendingRequest(response, &i);
+
+    if (err == OK && i < 0) {
+        // An unsolicited server response is not a problem.
+        return true;
+    }
+
+    if (err != OK) {
+        return false;
     }
 
     sp<AMessage> reply = mPendingRequests.valueAt(i);
@@ -660,4 +749,150 @@
     return true;
 }
 
+bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
+    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
+
+    if (i < 0) {
+        return false;
+    }
+
+    AString value = response->mHeaders.valueAt(i);
+
+    if (!strncmp(value.c_str(), "Basic", 5)) {
+        mAuthType = BASIC;
+    } else {
+        CHECK(!strncmp(value.c_str(), "Digest", 6));
+        mAuthType = DIGEST;
+
+        i = value.find("nonce=");
+        CHECK_GE(i, 0);
+        CHECK_EQ(value.c_str()[i + 6], '\"');
+        ssize_t j = value.find("\"", i + 7);
+        CHECK_GE(j, 0);
+
+        mNonce.setTo(value, i + 7, j - i - 7);
+    }
+
+    return true;
+}
+
+static void H(const AString &s, AString *out) {
+    out->clear();
+
+    MD5_CTX m;
+    MD5_Init(&m);
+    MD5_Update(&m, s.c_str(), s.size());
+
+    uint8_t key[16];
+    MD5_Final(key, &m);
+
+    for (size_t i = 0; i < 16; ++i) {
+        char nibble = key[i] >> 4;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+
+        nibble = key[i] & 0x0f;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+    }
+}
+
+static void GetMethodAndURL(
+        const AString &request, AString *method, AString *url) {
+    ssize_t space1 = request.find(" ");
+    CHECK_GE(space1, 0);
+
+    ssize_t space2 = request.find(" ", space1 + 1);
+    CHECK_GE(space2, 0);
+
+    method->setTo(request, 0, space1);
+    url->setTo(request, space1 + 1, space2 - space1);
+}
+
+void ARTSPConnection::addAuthentication(AString *request) {
+    if (mAuthType == NONE) {
+        return;
+    }
+
+    // Find the boundary between headers and the body.
+    ssize_t i = request->find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    if (mAuthType == BASIC) {
+        AString tmp;
+        tmp.append(mUser);
+        tmp.append(":");
+        tmp.append(mPass);
+
+        AString out;
+        encodeBase64(tmp.c_str(), tmp.size(), &out);
+
+        AString fragment;
+        fragment.append("Authorization: Basic ");
+        fragment.append(out);
+        fragment.append("\r\n");
+
+        request->insert(fragment, i + 2);
+
+        return;
+    }
+
+    CHECK_EQ((int)mAuthType, (int)DIGEST);
+
+    AString method, url;
+    GetMethodAndURL(*request, &method, &url);
+
+    AString A1;
+    A1.append(mUser);
+    A1.append(":");
+    A1.append("Streaming Server");
+    A1.append(":");
+    A1.append(mPass);
+
+    AString A2;
+    A2.append(method);
+    A2.append(":");
+    A2.append(url);
+
+    AString HA1, HA2;
+    H(A1, &HA1);
+    H(A2, &HA2);
+
+    AString tmp;
+    tmp.append(HA1);
+    tmp.append(":");
+    tmp.append(mNonce);
+    tmp.append(":");
+    tmp.append(HA2);
+
+    AString digest;
+    H(tmp, &digest);
+
+    AString fragment;
+    fragment.append("Authorization: Digest ");
+    fragment.append("nonce=\"");
+    fragment.append(mNonce);
+    fragment.append("\", ");
+    fragment.append("username=\"");
+    fragment.append(mUser);
+    fragment.append("\", ");
+    fragment.append("uri=\"");
+    fragment.append(url);
+    fragment.append("\", ");
+    fragment.append("response=\"");
+    fragment.append(digest);
+    fragment.append("\"");
+    fragment.append("\r\n");
+
+    request->insert(fragment, i + 2);
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 96e0d5b..19be2a6 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -42,6 +42,10 @@
 
     void observeBinaryData(const sp<AMessage> &reply);
 
+    static bool ParseURL(
+            const char *url, AString *host, unsigned *port, AString *path,
+            AString *user, AString *pass);
+
 protected:
     virtual ~ARTSPConnection();
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -62,9 +66,18 @@
         kWhatObserveBinaryData  = 'obin',
     };
 
+    enum AuthType {
+        NONE,
+        BASIC,
+        DIGEST
+    };
+
     static const int64_t kSelectTimeoutUs;
 
     State mState;
+    AString mUser, mPass;
+    AuthType mAuthType;
+    AString mNonce;
     int mSocket;
     int32_t mConnectionID;
     int32_t mNextCSeq;
@@ -90,8 +103,11 @@
     sp<ABuffer> receiveBinaryData();
     bool notifyResponseListener(const sp<ARTSPResponse> &response);
 
-    static bool ParseURL(
-            const char *url, AString *host, unsigned *port, AString *path);
+    bool parseAuthMethod(const sp<ARTSPResponse> &response);
+    void addAuthentication(AString *request);
+
+    status_t findPendingRequest(
+            const sp<ARTSPResponse> &response, ssize_t *index) const;
 
     static bool ParseSingleUnsignedLong(
             const char *from, unsigned long *x);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 612caff..880aa85 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -57,12 +57,20 @@
 
     size_t i = 0;
     for (;;) {
-        ssize_t eolPos = desc.find("\r\n", i);
+        ssize_t eolPos = desc.find("\n", i);
+
         if (eolPos < 0) {
             break;
         }
 
-        AString line(desc, i, eolPos - i);
+        AString line;
+        if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') {
+            // We accept both '\n' and '\r\n' line endings, if it's
+            // the latter, strip the '\r' as well.
+            line.setTo(desc, i, eolPos - i - 1);
+        } else {
+            line.setTo(desc, i, eolPos - i);
+        }
 
         if (line.size() < 2 || line.c_str()[1] != '=') {
             return false;
@@ -141,7 +149,7 @@
             }
         }
 
-        i = eolPos + 2;
+        i = eolPos + 1;
     }
 
     return true;
@@ -245,7 +253,7 @@
         return false;
     }
 
-    if (value == "npt=now-") {
+    if (value == "npt=now-" || value == "npt=0-") {
         return false;
     }
 
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 081ae32..0bbadc1 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -23,6 +23,7 @@
 	$(JNI_H_INCLUDE) \
 	$(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/frameworks/base/media/libstagefright/include \
+        $(TOP)/external/openssl/include
 
 LOCAL_MODULE:= libstagefright_rtsp
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 6943608..9bb8c46 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -96,6 +96,7 @@
           mNetLooper(new ALooper),
           mConn(new ARTSPConnection),
           mRTPConn(new ARTPConnection),
+          mOriginalSessionURL(url),
           mSessionURL(url),
           mSetupTracksSuccessful(false),
           mSeekPending(false),
@@ -113,6 +114,23 @@
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
                           PRIORITY_HIGHEST);
+
+        // Strip any authentication info from the session url, we don't
+        // want to transmit user/pass in cleartext.
+        AString host, path, user, pass;
+        unsigned port;
+        if (ARTSPConnection::ParseURL(
+                    mSessionURL.c_str(), &host, &port, &path, &user, &pass)
+                && user.size() > 0) {
+            mSessionURL.clear();
+            mSessionURL.append("rtsp://");
+            mSessionURL.append(host);
+            mSessionURL.append(":");
+            mSessionURL.append(StringPrintf("%u", port));
+            mSessionURL.append(path);
+
+            LOGI("rewritten session url: '%s'", mSessionURL.c_str());
+        }
     }
 
     void connect(const sp<AMessage> &doneMsg) {
@@ -126,7 +144,7 @@
         mConn->observeBinaryData(notify);
 
         sp<AMessage> reply = new AMessage('conn', id());
-        mConn->connect(mSessionURL.c_str(), reply);
+        mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
 
     void disconnect(const sp<AMessage> &doneMsg) {
@@ -312,7 +330,7 @@
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
                     sp<AMessage> reply = new AMessage('conn', id());
-                    mConn->connect(mSessionURL.c_str(), reply);
+                    mConn->connect(mOriginalSessionURL.c_str(), reply);
                 } else {
                     (new AMessage('quit', id()))->post();
                 }
@@ -922,7 +940,7 @@
         CHECK(GetAttribute(range.c_str(), "npt", &val));
         float npt1, npt2;
 
-        if (val == "now-") {
+        if (val == "now-" || val == "0-") {
             // This is a live stream and therefore not seekable.
             return;
         } else {
@@ -992,6 +1010,7 @@
     sp<ARTSPConnection> mConn;
     sp<ARTPConnection> mRTPConn;
     sp<ASessionDescription> mSessionDesc;
+    AString mOriginalSessionURL;  // This one still has user:pass@
     AString mSessionURL;
     AString mBaseURL;
     AString mSessionID;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 239dc05..569cf96 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -840,7 +840,7 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 2
 static char const * const gVendorString     = "Google Inc.";
-static char const * const gVersionString    = "1.2 Android Driver 1.1.0";
+static char const * const gVersionString    = "1.2 Android Driver 1.2.0";
 static char const * const gClientApiString  = "OpenGL ES";
 static char const * const gExtensionsString =
         "EGL_KHR_image_base "
@@ -947,6 +947,7 @@
         { EGL_RED_SIZE,         5 },
         { EGL_DEPTH_SIZE,       0 },
         { EGL_CONFIG_ID,        0 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -958,6 +959,7 @@
         { EGL_RED_SIZE,         5 },
         { EGL_DEPTH_SIZE,      16 },
         { EGL_CONFIG_ID,        1 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -970,6 +972,7 @@
         { EGL_RED_SIZE,         8 },
         { EGL_DEPTH_SIZE,       0 },
         { EGL_CONFIG_ID,        6 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -981,6 +984,7 @@
         { EGL_RED_SIZE,         8 },
         { EGL_DEPTH_SIZE,      16 },
         { EGL_CONFIG_ID,        7 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -992,6 +996,7 @@
         { EGL_GREEN_SIZE,       8 },
         { EGL_RED_SIZE,         8 },
         { EGL_DEPTH_SIZE,       0 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
         { EGL_CONFIG_ID,        2 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
@@ -1004,6 +1009,7 @@
         { EGL_RED_SIZE,         8 },
         { EGL_DEPTH_SIZE,      16 },
         { EGL_CONFIG_ID,        3 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -1016,6 +1022,7 @@
         { EGL_RED_SIZE,         0 },
         { EGL_DEPTH_SIZE,       0 },
         { EGL_CONFIG_ID,        4 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
 
@@ -1026,6 +1033,7 @@
         { EGL_GREEN_SIZE,       0 },
         { EGL_RED_SIZE,         0 },
         { EGL_DEPTH_SIZE,      16 },
+        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
         { EGL_CONFIG_ID,        5 },
         { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
 };
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index a0f720a..8b4136a 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -33,7 +33,7 @@
 // ----------------------------------------------------------------------------
 
 static char const * const gVendorString     = "Android";
-static char const * const gRendererString   = "Android PixelFlinger 1.3";
+static char const * const gRendererString   = "Android PixelFlinger 1.4";
 static char const * const gVersionString    = "OpenGL ES-CM 1.0";
 static char const * const gExtensionsString =
     "GL_OES_byte_coordinates "              // OK
diff --git a/opengl/tests/configdump/Android.mk b/opengl/tests/configdump/Android.mk
new file mode 100644
index 0000000..3f7c915
--- /dev/null
+++ b/opengl/tests/configdump/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	configdump.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libEGL \
+    libGLESv1_CM
+
+LOCAL_MODULE:= test-opengl-configdump
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/configdump/configdump.cpp b/opengl/tests/configdump/configdump.cpp
new file mode 100644
index 0000000..69b9eb6
--- /dev/null
+++ b/opengl/tests/configdump/configdump.cpp
@@ -0,0 +1,89 @@
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+
+#define ATTRIBUTE(_attr) { _attr, #_attr }
+
+struct Attribute {
+    EGLint attribute;
+    char const* name;
+};
+
+Attribute attributes[] = {
+        ATTRIBUTE( EGL_BUFFER_SIZE ),
+        ATTRIBUTE( EGL_ALPHA_SIZE ),
+        ATTRIBUTE( EGL_BLUE_SIZE ),
+        ATTRIBUTE( EGL_GREEN_SIZE ),
+        ATTRIBUTE( EGL_RED_SIZE ),
+        ATTRIBUTE( EGL_DEPTH_SIZE ),
+        ATTRIBUTE( EGL_STENCIL_SIZE ),
+        ATTRIBUTE( EGL_CONFIG_CAVEAT ),
+        ATTRIBUTE( EGL_CONFIG_ID ),
+        ATTRIBUTE( EGL_LEVEL ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_HEIGHT ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_WIDTH ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_PIXELS ),
+        ATTRIBUTE( EGL_NATIVE_RENDERABLE ),
+        ATTRIBUTE( EGL_NATIVE_VISUAL_ID ),
+        ATTRIBUTE( EGL_NATIVE_VISUAL_TYPE ),
+        ATTRIBUTE( EGL_SAMPLES ),
+        ATTRIBUTE( EGL_SAMPLE_BUFFERS ),
+        ATTRIBUTE( EGL_SURFACE_TYPE ),
+        ATTRIBUTE( EGL_TRANSPARENT_TYPE ),
+        ATTRIBUTE( EGL_TRANSPARENT_BLUE_VALUE ),
+        ATTRIBUTE( EGL_TRANSPARENT_GREEN_VALUE ),
+        ATTRIBUTE( EGL_TRANSPARENT_RED_VALUE ),
+        ATTRIBUTE( EGL_BIND_TO_TEXTURE_RGB ),
+        ATTRIBUTE( EGL_BIND_TO_TEXTURE_RGBA ),
+        ATTRIBUTE( EGL_MIN_SWAP_INTERVAL ),
+        ATTRIBUTE( EGL_MAX_SWAP_INTERVAL ),
+        ATTRIBUTE( EGL_LUMINANCE_SIZE ),
+        ATTRIBUTE( EGL_ALPHA_MASK_SIZE ),
+        ATTRIBUTE( EGL_COLOR_BUFFER_TYPE ),
+        ATTRIBUTE( EGL_RENDERABLE_TYPE ),
+        ATTRIBUTE( EGL_MATCH_NATIVE_PIXMAP ),
+        ATTRIBUTE( EGL_CONFORMANT ),
+};
+
+
+int main(int argc, char** argv)
+{
+    EGLConfig* configs;
+    EGLint n;
+
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    eglInitialize(dpy, 0, 0);
+    eglGetConfigs(dpy, NULL, 0, &n);
+    configs = new EGLConfig[n];
+    eglGetConfigs(dpy, configs, n, &n);
+
+    for (EGLint i=0 ; i<n ; i++) {
+        printf("EGLConfig[%d]\n", i);
+        for (int attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) {
+            EGLint value;
+            eglGetConfigAttrib(dpy, configs[i], attributes[attr].attribute, &value);
+            printf("\t%-32s: %10d (0x%08x)\n", attributes[attr].name, value, value);
+        }
+    }
+
+    delete [] configs;
+    eglTerminate(dpy);
+    return 0;
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5fde3c3..d824f30 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -14,15 +14,13 @@
         android:allowClearUserData="false"
         android:label="@string/app_label"
         android:icon="@drawable/ic_launcher_settings">
-                 
-        <service
-            android:name=".statusbar.PhoneStatusBarService"
-            android:exported="false"
-            />
 
-        <service
-            android:name=".statusbar.tablet.TabletStatusBarService"
-            android:exported="false"
+        <!-- Broadcast receiver that gets the broadcast at boot time and starts
+             up everything else.
+             TODO: Should have an android:permission attribute
+             -->
+        <service android:name="SystemUIService"
+            android:exported="true"
             />
 
         <activity android:name=".usb.UsbStorageActivity"
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png
new file mode 100644
index 0000000..90dffa9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png
new file mode 100644
index 0000000..dcce495
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png
new file mode 100644
index 0000000..93b0d23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png
new file mode 100644
index 0000000..9eb9dc6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 295c79b..8824028 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -34,7 +34,6 @@
             android:src="@drawable/ic_sysbar_open"
             android:background="@drawable/ic_sysbar_icon_bg"
             android:paddingLeft="6dip"
-            android:onClick="notificationIconsClicked"
             />
 
         <LinearLayout
@@ -136,6 +135,27 @@
             android:layout_alignParentRight="true"
             android:orientation="horizontal"
             >
+            <com.android.systemui.statusbar.tablet.ShirtPocket
+                android:id="@+id/pocket"
+                android:layout_width="71dip"
+                android:layout_height="match_parent"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:paddingLeft="4dip"
+                android:paddingRight="4dip"
+                android:animateLayoutChanges="true"
+                android:clickable="true"
+                android:descendantFocusability="blocksDescendants"
+                android:gravity="center"
+                >
+                <ImageView 
+                    android:id="@+id/pocket_icon"
+                    android:src="@drawable/ic_sysbar_pocket"
+                    android:visibility="invisible"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    />
+            </com.android.systemui.statusbar.tablet.ShirtPocket>
             <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
@@ -153,7 +173,6 @@
                 android:background="@drawable/ic_sysbar_icon_bg"
                 android:paddingLeft="4dip"
                 android:paddingRight="4dip"
-                android:onClick="recentButtonClicked"
                 />
             <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
                 android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
index 2d74672..d4f0e50 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -22,7 +22,6 @@
     android:layout_height="match_parent"
     android:layout_centerInParent="true"
     android:clickable="true"
-    android:onClick="systemInfoClicked"
     >
     <com.android.systemui.statusbar.Clock
         style="@*android:style/TextAppearance.StatusBar.Icon"
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml
new file mode 100644
index 0000000..e4a6da4
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@*android:drawable/dialog_full_holo_dark"
+    >
+    <TextView
+        android:id="@+id/description"
+        android:textAppearance="@android:style/TextAppearance.Small"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:textStyle="bold"
+        android:maxLines="1"
+        android:layout_alignParentBottom="true"
+        />
+
+    <FrameLayout
+        android:id="@+id/preview"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:layout_above="@+id/description"
+        android:descendantFocusability="blocksDescendants"
+        >
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerInside"
+            android:visibility="gone"
+            />
+        <TextView
+            android:id="@+id/alt"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:textAppearance="@android:style/TextAppearance.Large"
+            android:gravity="center"
+            android:visibility="gone"
+            />
+    </FrameLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-xlarge/config.xml
index 4cf5d18d11..6df883c 100644
--- a/packages/SystemUI/res/values-xlarge/config.xml
+++ b/packages/SystemUI/res/values-xlarge/config.xml
@@ -21,5 +21,10 @@
      for different hardware and product builds. -->
 <resources>
     <integer name="config_status_bar_position">1</integer>
+
+    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
+     interface.  This name is in the ComponentName flattened format (package/class)  -->
+    <string name="config_statusBarComponent">com.android.systemui.statusbar.tablet.TabletStatusBarService</string>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ac00c69..4570d8e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -31,5 +31,9 @@
     -->
     <integer name="config_status_bar_position">0</integer>
 
+    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
+     interface.  This name is in the ComponentName flattened format (package/class)  -->
+    <string name="config_statusBarComponent">com.android.systemui.statusbar.PhoneStatusBarService</string>
+
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
new file mode 100644
index 0000000..53fe2ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.content.Context;
+
+public abstract class SystemUI {
+    public Context mContext;
+
+    public abstract void start();
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
new file mode 100644
index 0000000..dda86d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Slog;
+
+public class SystemUIService extends Service {
+    static final String TAG = "SystemUIService";
+
+    /**
+     * The class names of the stuff to start.
+     */
+    final Object[] SERVICES = new Object[] {
+            R.string.config_statusBarComponent,
+        };
+
+    /**
+     * Hold a reference on the stuff we start.
+     */
+    SystemUI[] mServices;
+
+    private Class chooseClass(Object o) {
+        if (o instanceof Integer) {
+            final String cl = getString((Integer)o);
+            try {
+                return getClassLoader().loadClass(cl);
+            } catch (ClassNotFoundException ex) {
+                throw new RuntimeException(ex);
+            }
+        } else if (o instanceof Class) {
+            return (Class)o;
+        } else {
+            throw new RuntimeException("Unknown system ui service: " + o);
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        final int N = SERVICES.length;
+        mServices = new SystemUI[N];
+        for (int i=0; i<N; i++) {
+            Class cl = chooseClass(SERVICES[i]);
+            Slog.d(TAG, "loading: " + cl);
+            try {
+                mServices[i] = (SystemUI)cl.newInstance();
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            } catch (InstantiationException ex) {
+                throw new RuntimeException(ex);
+            }
+            mServices[i].mContext = this;
+            Slog.d(TAG, "running: " + mServices[i]);
+            mServices[i].start();
+        }
+    }
+
+    /**
+     * Nobody binds to us.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump StatusBar from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        for (SystemUI ui: mServices) {
+            pw.println("dumping service: " + ui.getClass().getName());
+            ui.dump(fd, pw, args);
+        }
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index fc7b534..bf58b37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -35,7 +34,6 @@
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
@@ -195,27 +193,23 @@
     }
 
     @Override
-    public void onCreate() {
-        mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+    public void start() {
+        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
+                .getDefaultDisplay();
 
-        super.onCreate();
+        super.start();
 
         addIntruderView();
 
         // Lastly, call to the icon policy to install/update all the icons.
-        mIconPolicy = new StatusBarPolicy(this);
-    }
-
-    @Override
-    public void onDestroy() {
-        // we're never destroyed
+        mIconPolicy = new StatusBarPolicy(mContext);
     }
 
     // ================================================================================
     // Constructing the view
     // ================================================================================
     protected View makeStatusBarView() {
-        final Context context = this;
+        final Context context = mContext;
 
         Resources res = context.getResources();
 
@@ -293,7 +287,7 @@
     }
 
     private void addIntruderView() {
-        final Resources res = getResources();
+        final Resources res = mContext.getResources();
         final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
 
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -317,7 +311,7 @@
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
         if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
                 + " icon=" + icon);
-        StatusBarIconView view = new StatusBarIconView(this, slot);
+        StatusBarIconView view = new StatusBarIconView(mContext, slot);
         view.set(icon);
         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
     }
@@ -434,7 +428,7 @@
             oldEntry.notification = notification;
             try {
                 // Reapply the RemoteViews
-                contentView.reapply(this, oldEntry.content);
+                contentView.reapply(mContext, oldEntry.content);
                 // update the contentIntent
                 final PendingIntent contentIntent = notification.notification.contentIntent;
                 if (contentIntent != null) {
@@ -507,7 +501,8 @@
         }
 
         // create the row view
-        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
 
         // bind the click event to the content area
@@ -525,7 +520,7 @@
         View expanded = null;
         Exception exception = null;
         try {
-            expanded = remoteViews.apply(this, content);
+            expanded = remoteViews.apply(mContext, content);
         }
         catch (RuntimeException e) {
             exception = e;
@@ -564,7 +559,7 @@
         final View content = views[1];
         final View expanded = views[2];
         // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(this,
+        final StatusBarIconView iconView = new StatusBarIconView(mContext,
                 notification.pkg + "/0x" + Integer.toHexString(notification.id));
         final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
                     notification.notification.iconLevel, notification.notification.number);
@@ -1050,7 +1045,7 @@
                 overlay.setSourceBounds(
                         new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
                 try {
-                    mIntent.send(PhoneStatusBarService.this, 0, overlay);
+                    mIntent.send(mContext, 0, overlay);
                 } catch (PendingIntent.CanceledException e) {
                     // the stack trace isn't very helpful here.  Just log the exception message.
                     Slog.w(TAG, "Sending contentIntent failed: " + e);
@@ -1151,7 +1146,7 @@
     };
 
     private Animation loadAnim(int id, Animation.AnimationListener listener) {
-        Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+        Animation anim = AnimationUtils.loadAnimation(mContext, id);
         if (listener != null) {
             anim.setAnimationListener(listener);
         }
@@ -1163,15 +1158,7 @@
                 + " " + v.getWidth() + "x" + v.getHeight() + ")";
     }
 
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump StatusBar from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
             pw.println("Current Status Bar state:");
             pw.println("  mExpanded=" + mExpanded
@@ -1519,12 +1506,13 @@
      * meantime, just update the things that we know change.
      */
     void updateResources() {
-        Resources res = getResources();
+        final Context context = mContext;
+        final Resources res = context.getResources();
 
-        mClearButton.setText(getText(R.string.status_bar_clear_all_button));
-        mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
-        mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
-        mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+        mClearButton.setText(context.getText(R.string.status_bar_clear_all_button));
+        mOngoingTitle.setText(context.getText(R.string.status_bar_ongoing_events_title));
+        mLatestTitle.setText(context.getText(R.string.status_bar_latest_events_title));
+        mNoNotificationsTitle.setText(context.getText(R.string.status_bar_no_notifications_title));
 
         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
 
@@ -1540,7 +1528,8 @@
     }
 
     void vibrate() {
-        android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
+                Context.VIBRATOR_SERVICE);
         vib.vibrate(250);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index e0019b5..fb74e70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -699,7 +699,6 @@
         NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra(
                 ConnectivityManager.EXTRA_NETWORK_INFO));
         int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
-        Slog.d(TAG, "got CONNECTIVITY_ACTION - info=" + info + ", status = " + connectionStatus);
 
         int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index 5594a47..ae1fdbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -40,10 +40,12 @@
 import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 
+import com.android.systemui.SystemUI;
 import com.android.systemui.R;
 
-public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
+public abstract class StatusBarService extends SystemUI implements CommandQueue.Callbacks {
     static final String TAG = "StatusBarService";
+    private static final boolean SPEW = false;
 
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
@@ -52,16 +54,7 @@
     protected abstract View makeStatusBarView();
     protected abstract int getStatusBarGravity();
 
-    /**
-     * Nobody binds to us.
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    @Override
-    public void onCreate() {
+    public void start() {
         // First set up our views and stuff.
         View sb = makeStatusBarView();
 
@@ -106,7 +99,7 @@
         }
 
         // Put up the view
-        final Resources res = getResources();
+        final Resources res = mContext.getResources();
         final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
 
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -121,11 +114,12 @@
         // TODO lp.windowAnimations = R.style.Animation_StatusBar;
         WindowManagerImpl.getDefault().addView(sb, lp);
 
-        Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity) 
-                    + " icons=" + iconList.size()
-                    + " lights=" + (switches[0]?"on":"off")
-                    + " menu=" + (switches[1]?"visible":"invisible")
-                    );
+        if (SPEW) {
+            Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity) 
+                   + " icons=" + iconList.size()
+                   + " lights=" + (switches[0]?"on":"off")
+                   + " menu=" + (switches[1]?"visible":"invisible")
+                   );
+        }
     }
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
new file mode 100644
index 0000000..658038c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.Slog;
+import android.view.View;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.graphics.Paint;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.view.WindowManagerImpl;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+
+import com.android.systemui.R;
+
+public class ShirtPocket extends FrameLayout {
+    private static final boolean DEBUG = false;
+    private static final String  TAG = "StatusBar/ShirtPocket";
+
+    private ClipData mClipping = null;
+
+    private View mWindow = null;
+    private ImageView mIcon;
+    private ImageView mPreviewIcon;
+    private TextView mDescription;
+    private TextView mAltText;
+
+    public ShirtPocket(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setupWindow();
+    }
+
+    // TODO: "pin area" panel, dragging things out
+    ObjectAnimator mAnimHide, mAnimShow;
+    
+    protected void onAttachedToWindow() {
+        // Drag API notes: we must be visible to receive drag events
+        setVisibility(View.VISIBLE);
+
+        mIcon = (ImageView) findViewById(R.id.pocket_icon);
+        refreshStatusIcon();
+
+        setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if (mClipping != null) {
+                    if (mWindow.getVisibility() == View.VISIBLE) hideWindow(); 
+                    else showWindow();
+                }
+            }
+        });
+    }
+
+    private void refreshStatusIcon() {
+        setClickable(mClipping != null);
+        mIcon.setImageResource(mClipping == null
+                ? R.drawable.ic_sysbar_pocket_hidden
+                : R.drawable.ic_sysbar_pocket_holding);
+        mIcon.setVisibility(mClipping == null ? View.INVISIBLE : View.VISIBLE);
+    }
+    
+    private void showWindow() {
+        getHandler().post(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.VISIBLE);
+                refreshStatusIcon();
+            }
+        });
+    }
+
+    private void hideWindow() {
+        getHandler().post(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.GONE);
+                refreshStatusIcon();
+            }
+        });
+    }
+    
+    private void hideWindowInJustASec() {
+        getHandler().postDelayed(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.GONE);
+                refreshStatusIcon();
+            }
+        },
+        250);
+    }
+
+    private void stash(ClipData clipping) {
+        mClipping = clipping;
+        if (mClipping != null) {
+            Bitmap icon = mClipping.getIcon();
+            mDescription.setText(mClipping.getLabel());
+            if (icon != null) {
+                mPreviewIcon.setImageBitmap(icon);
+                mPreviewIcon.setVisibility(View.VISIBLE);
+                mAltText.setVisibility(View.GONE);
+            } else {
+                mPreviewIcon.setVisibility(View.GONE);
+                mAltText.setVisibility(View.VISIBLE);
+                if (mClipping.getItemCount() > 0) {
+                    // TODO: figure out how to visualize every kind of ClipData!
+                    mAltText.setText(mClipping.getItem(0).coerceToText(getContext()));
+                }
+            }
+        }
+    }
+
+    private boolean isInViewContentArea(View v, int x, int y) {
+        final int l = v.getPaddingLeft();
+        final int r = v.getWidth() - v.getPaddingRight();
+        final int t = v.getPaddingTop();
+        final int b = v.getHeight() - v.getPaddingBottom();
+        return x >= l && x < r && y >= t && y < b;
+    }
+
+    View.OnTouchListener mWindowTouchListener = new View.OnTouchListener() {
+        public boolean onTouch(View v, MotionEvent ev) {
+            final int action = ev.getAction();
+            if (action == MotionEvent.ACTION_OUTSIDE
+                    || (action == MotionEvent.ACTION_DOWN
+                        && !isInViewContentArea(mWindow, (int)ev.getX(), (int)ev.getY()))) {
+                hideWindow();
+                return true;
+            } else if (action == MotionEvent.ACTION_DOWN) {
+                Slog.d(TAG, "ACTION_DOWN");
+                final ClipData clip = mClipping;
+                if (clip != null) {
+                    final Bitmap icon = clip.getIcon();
+                    DragThumbnailBuilder thumb;
+                    if (icon != null) {
+                        thumb = new DragThumbnailBuilder(v) {
+                            public void onProvideThumbnailMetrics(Point thumbnailSize, Point thumbnailTouchPoint) {
+                                thumbnailSize.set(icon.getWidth(), icon.getHeight());
+                                thumbnailTouchPoint.set(thumbnailSize.x / 2, thumbnailSize.y / 2);
+                            }
+                            public void onDrawThumbnail(Canvas canvas) {
+                                canvas.drawBitmap(icon, 0, 0, new Paint());
+                            }
+                        };
+                    } else {
+                        // uhhh, what now?
+                        thumb = new DragThumbnailBuilder(mWindow.findViewById(R.id.preview));
+                    }
+
+                    ShirtPocket.this.startDrag(clip, thumb, false);
+
+                    // TODO: only discard the clipping if it was accepted
+                    stash(null);
+
+                    hideWindowInJustASec(); // will refresh the icon
+
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    private void setupWindow() {
+        mWindow = View.inflate(getContext(), R.layout.sysbar_panel_pocket, null);
+
+        mPreviewIcon = (ImageView) mWindow.findViewById(R.id.icon);
+        mDescription = (TextView) mWindow.findViewById(R.id.description);
+        mAltText = (TextView) mWindow.findViewById(R.id.alt);
+
+        mWindow.setVisibility(View.GONE);
+        mWindow.setOnTouchListener(mWindowTouchListener);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                400,
+                250,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+//        int pos[] = new int[2];
+//        getLocationOnScreen(pos);
+//        lp.x = pos[1];
+//        lp.y = 0;
+        lp.setTitle("ShirtPocket");
+        lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
+
+        WindowManagerImpl.getDefault().addView(mWindow, lp);
+
+    }
+
+    public boolean onDragEvent(DragEvent event) {
+        if (DEBUG) Slog.d(TAG, "onDragEvent: " + event);
+        if (mIcon != null) {
+            switch (event.getAction()) {
+                // We want to appear whenever a potential drag takes off from anywhere in the UI.
+                case DragEvent.ACTION_DRAG_STARTED:
+                    mIcon.setImageResource(mClipping == null
+                            ? R.drawable.ic_sysbar_pocket
+                            : R.drawable.ic_sysbar_pocket_holding);
+                    mIcon.setVisibility(View.VISIBLE);
+                    break;
+                case DragEvent.ACTION_DRAG_ENTERED:
+                    if (DEBUG) Slog.d(TAG, "entered!");
+                    mIcon.setImageResource(R.drawable.ic_sysbar_pocket_drag);
+                    break;
+                case DragEvent.ACTION_DRAG_EXITED:
+                    if (DEBUG) Slog.d(TAG, "exited!");
+                    mIcon.setImageResource(mClipping == null
+                            ? R.drawable.ic_sysbar_pocket
+                            : R.drawable.ic_sysbar_pocket_holding);
+                    break;
+                case DragEvent.ACTION_DROP:
+                    if (DEBUG) Slog.d(TAG, "dropped!");
+                    stash(event.getClipData());
+                    refreshStatusIcon();
+                    break;
+                case DragEvent.ACTION_DRAG_ENDED:
+                    refreshStatusIcon();
+                    break;
+            }
+        }
+        return true; // we want everything, thank you
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 340e269..6426e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.tablet;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 import android.app.ActivityManagerNative;
 import android.app.PendingIntent;
 import android.app.Notification;
@@ -80,6 +83,7 @@
     View mSystemInfo;
     View mNavigationArea;
     View mMenuButton;
+    View mRecentButton;
 
     NotificationPanel mNotificationPanel;
     SystemPanel mSystemPanel;
@@ -107,11 +111,13 @@
     boolean mNotificationsOn = true;
 
     protected void addPanelWindows() {
-        final Resources res = getResources();
+        final Context context = mContext;
+
+        final Resources res = context.getResources();
         final int barHeight= res.getDimensionPixelSize(
             com.android.internal.R.dimen.status_bar_height);
 
-        mNotificationPanel = (NotificationPanel)View.inflate(this,
+        mNotificationPanel = (NotificationPanel)View.inflate(context,
                 R.layout.sysbar_panel_notifications, null);
         mNotificationPanel.setVisibility(View.GONE);
         mNotificationPanel.setOnTouchListener(
@@ -132,7 +138,7 @@
 
         WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
 
-        mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
+        mSystemPanel = (SystemPanel) View.inflate(context, R.layout.sysbar_panel_system, null);
         mSystemPanel.setVisibility(View.GONE);
         mSystemPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_SYSTEM_PANEL,
                     mSystemPanel));
@@ -155,17 +161,18 @@
     }
 
     @Override
-    public void onCreate() {
-        super.onCreate(); // will add the main bar view
+    public void start() {
+        super.start(); // will add the main bar view
     }
 
     protected View makeStatusBarView() {
-        Resources res = getResources();
+        final Context context = mContext;
+        final Resources res = context.getResources();
 
         mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
 
         final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
-                this, R.layout.status_bar, null);
+                context, R.layout.status_bar, null);
         mStatusBarView = sb;
 
         sb.setHandler(mHandler);
@@ -174,14 +181,19 @@
         mCurtains = sb.findViewById(R.id.lights_out);
         mSystemInfo = sb.findViewById(R.id.systemInfo);
 
+        mSystemInfo.setOnClickListener(mOnClickListener);
         mSystemInfo.setOnLongClickListener(new SetLightsOnListener(false));
 
+        mRecentButton = sb.findViewById(R.id.recent);
+        mRecentButton.setOnClickListener(mOnClickListener);
+
         SetLightsOnListener on = new SetLightsOnListener(true);
         mCurtains.setOnClickListener(on);
         mCurtains.setOnLongClickListener(on);
 
         // the button to open the notification area
         mNotificationTrigger = sb.findViewById(R.id.expand);
+        mNotificationTrigger.setOnClickListener(mOnClickListener);
 
         // the more notifications icon
         mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
@@ -189,15 +201,15 @@
         // the clear and dnd buttons
         mNotificationButtons = sb.findViewById(R.id.notificationButtons);
         mClearButton = (TextView)mNotificationButtons.findViewById(R.id.clear_all_button);
-        mClearButton.setOnClickListener(mClearButtonListener);
+        mClearButton.setOnClickListener(mOnClickListener);
         mDoNotDisturbButton = (TextView)mNotificationButtons.findViewById(R.id.do_not_disturb);
-        mDoNotDisturbButton.setOnClickListener(mDoNotDisturbButtonListener);
+        mDoNotDisturbButton.setOnClickListener(mOnClickListener);
 
 
         // where the icons go
         mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
 
-        mTicker = new TabletTicker((Context)this, (FrameLayout)sb.findViewById(R.id.ticker));
+        mTicker = new TabletTicker(context, (FrameLayout)sb.findViewById(R.id.ticker));
 
         // System info (center)
         mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
@@ -366,7 +378,7 @@
             oldEntry.notification = notification;
             try {
                 // Reapply the RemoteViews
-                contentView.reapply(this, oldEntry.content);
+                contentView.reapply(mContext, oldEntry.content);
                 // update the contentIntent
                 final PendingIntent contentIntent = notification.notification.contentIntent;
                 if (contentIntent != null) {
@@ -551,37 +563,6 @@
         */
     }
 
-    public void notificationIconsClicked(View v) {
-        if (DEBUG) Slog.d(TAG, "clicked notification icons");
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
-            int msg = (mNotificationPanel.getVisibility() == View.GONE) 
-                ? MSG_OPEN_NOTIFICATION_PANEL
-                : MSG_CLOSE_NOTIFICATION_PANEL;
-            mHandler.removeMessages(msg);
-            mHandler.sendEmptyMessage(msg);
-        }
-    }
-
-    public void systemInfoClicked(View v) {
-        if (DEBUG) Slog.d(TAG, "clicked system info");
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
-            int msg = (mSystemPanel.getVisibility() == View.GONE) 
-                ? MSG_OPEN_SYSTEM_PANEL
-                : MSG_CLOSE_SYSTEM_PANEL;
-            mHandler.removeMessages(msg);
-            mHandler.sendEmptyMessage(msg);
-        }
-    }
-
-    public void recentButtonClicked(View v) {
-        if (DEBUG) Slog.d(TAG, "clicked recent apps");
-        Intent intent = new Intent();
-        intent.setClass(this, RecentApplicationsActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        startActivity(intent);
-    }
-
     /**
      * Cancel this notification and tell the status bar service about the failure. Hold no locks.
      */
@@ -594,23 +575,66 @@
         }
     }
 
-    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
         public void onClick(View v) {
-            try {
-                mBarService.onClearAllNotifications();
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
+            if (v == mClearButton) {
+                onClickClearButton();
+            } else if (v == mDoNotDisturbButton) {
+                onClickDoNotDisturb();
+            } else if (v == mNotificationTrigger) {
+                onClickNotificationTrigger();
+            } else if (v == mSystemInfo) {
+                onClickSystemInfo();
+            } else if (v == mRecentButton) {
+                onClickRecentButton();
             }
-            animateCollapse();
         }
     };
 
-    private View.OnClickListener mDoNotDisturbButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            mNotificationsOn = !mNotificationsOn;
-            animateCollapse();
+    void onClickClearButton() {
+        try {
+            mBarService.onClearAllNotifications();
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
         }
-    };
+        animateCollapse();
+    }
+
+    void onClickDoNotDisturb() {
+        mNotificationsOn = !mNotificationsOn;
+        animateCollapse();
+    }
+
+    public void onClickNotificationTrigger() {
+        if (DEBUG) Slog.d(TAG, "clicked notification icons");
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            int msg = (mNotificationPanel.getVisibility() == View.GONE) 
+                ? MSG_OPEN_NOTIFICATION_PANEL
+                : MSG_CLOSE_NOTIFICATION_PANEL;
+            mHandler.removeMessages(msg);
+            mHandler.sendEmptyMessage(msg);
+        }
+    }
+
+    public void onClickSystemInfo() {
+        if (DEBUG) Slog.d(TAG, "clicked system info");
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            int msg = (mSystemPanel.getVisibility() == View.GONE) 
+                ? MSG_OPEN_SYSTEM_PANEL
+                : MSG_CLOSE_SYSTEM_PANEL;
+            mHandler.removeMessages(msg);
+            mHandler.sendEmptyMessage(msg);
+        }
+    }
+
+    public void onClickRecentButton() {
+        if (DEBUG) Slog.d(TAG, "clicked recent apps");
+        Intent intent = new Intent();
+        intent.setClass(mContext, RecentApplicationsActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        mContext.startActivity(intent);
+    }
 
     private class NotificationClicker implements View.OnClickListener {
         private PendingIntent mIntent;
@@ -642,7 +666,7 @@
                 overlay.setSourceBounds(
                         new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
                 try {
-                    mIntent.send(TabletStatusBarService.this, 0, overlay);
+                    mIntent.send(mContext, 0, overlay);
                 } catch (PendingIntent.CanceledException e) {
                     // the stack trace isn't very helpful here.  Just log the exception message.
                     Slog.w(TAG, "Sending contentIntent failed: " + e);
@@ -685,7 +709,7 @@
             Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
         }
         // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(this,
+        final StatusBarIconView iconView = new StatusBarIconView(mContext,
                 notification.pkg + "/0x" + Integer.toHexString(notification.id));
         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
 
@@ -743,7 +767,8 @@
         }
 
         // create the row view
-        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
         View vetoButton = row.findViewById(R.id.veto);
         if (entry.notification.isClearable()) {
@@ -780,7 +805,7 @@
         View expanded = null;
         Exception exception = null;
         try {
-            expanded = remoteViews.apply(this, content);
+            expanded = remoteViews.apply(mContext, content);
         }
         catch (RuntimeException e) {
             exception = e;
@@ -853,10 +878,16 @@
     private void setViewVisibility(View v, int vis, int anim) {
         if (v.getVisibility() != vis) {
             //Slog.d(TAG, "setViewVisibility vis=" + (vis == View.VISIBLE) + " v=" + v);
-            v.setAnimation(AnimationUtils.loadAnimation((Context)this, anim));
+            v.setAnimation(AnimationUtils.loadAnimation(mContext, anim));
             v.setVisibility(vis);
         }
     }
+
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("mDisabled=0x");
+        pw.println(Integer.toHexString(mDisabled));
+    }
 }
 
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index d19f318..165107c 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -97,8 +97,8 @@
 public class KeyguardViewMediator implements KeyguardViewCallback,
         KeyguardUpdateMonitor.SimStateCallback {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
-    private final static boolean DEBUG = false && Config.LOGD;
-    private final static boolean DBG_WAKE = DEBUG || true;
+    private final static boolean DEBUG = false;
+    private final static boolean DBG_WAKE = false;
 
     private final static String TAG = "KeyguardViewMediator";
 
@@ -642,7 +642,7 @@
      * @see #onWakeKeyWhenKeyguardShowingTq(int)
      */
     private void wakeWhenReadyLocked(int keyCode) {
-        if (DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
+        if (true || DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
 
         /**
          * acquire the handoff lock that will keep the cpu running.  this will
@@ -1169,5 +1169,3 @@
         }
     }
 }
-
-
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 50b3abe..90ebf3f 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -664,11 +664,11 @@
                 // backup.
                 RandomAccessFile in = null;
                 try {
-                    Slog.i(TAG, "Found stale backup journal, scheduling:");
+                    Slog.i(TAG, "Found stale backup journal, scheduling");
                     in = new RandomAccessFile(f, "r");
                     while (true) {
                         String packageName = in.readUTF();
-                        Slog.i(TAG, "    + " + packageName);
+                        Slog.i(TAG, "  " + packageName);
                         dataChangedImpl(packageName);
                     }
                 } catch (EOFException e) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 15eaa9e..8066fa7 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -700,9 +700,6 @@
     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
             String tag, int id, Notification notification, int[] idOut)
     {
-        Slog.d(TAG, "enqueueNotificationWithTag: calling uid=" + callingUid 
-                + ", pid=" + callingPid);
-        
         checkIncomingCall(pkg);
 
         // Limit the number of notifications that any given package except the android
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 666bb26..c50a01e 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -2500,7 +2500,9 @@
             return;
         }
 
-        Log.d(TAG, "Scanning app dir " + dir);
+        if (false) {
+            Log.d(TAG, "Scanning app dir " + dir);
+        }
 
         int i;
         for (i=0; i<files.length; i++) {
@@ -2866,10 +2868,8 @@
                 TAG, "Scanning package " + pkg.packageName);
         if (mPackages.containsKey(pkg.packageName)
                 || mSharedLibraries.containsKey(pkg.packageName)) {
-            Slog.w(TAG, "*************************************************");
             Slog.w(TAG, "Application package " + pkg.packageName
                     + " already installed.  Skipping duplicate.");
-            Slog.w(TAG, "*************************************************");
             mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
             return null;
         }
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 66e02146..400b31f 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -54,7 +54,7 @@
 public class StatusBarManagerService extends IStatusBarService.Stub
 {
     static final String TAG = "StatusBarManagerService";
-    static final boolean SPEW = true;
+    static final boolean SPEW = false;
 
     final Context mContext;
     Handler mHandler = new Handler();
@@ -111,22 +111,6 @@
     }
 
     // ================================================================================
-    // Constructing the view
-    // ================================================================================
-
-    public void systemReady() {
-    }
-
-    public void systemReady2() {
-        ComponentName cn = ComponentName.unflattenFromString(
-                mContext.getString(com.android.internal.R.string.config_statusBarComponent));
-        Intent intent = new Intent();
-        intent.setComponent(cn);
-        Slog.i(TAG, "Starting service: " + cn);
-        mContext.startService(intent);
-    }
-
-    // ================================================================================
     // From IStatusBarService
     // ================================================================================
     public void expand() {
@@ -161,7 +145,6 @@
         synchronized (mDisableRecords) {
             manageDisableListLocked(what, token, pkg);
             final int net = gatherDisableActionsLocked();
-            Slog.d(TAG, "disable... net=0x" + Integer.toHexString(net));
             if (net != mDisabled) {
                 mDisabled = net;
                 mHandler.post(new Runnable() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ec12e80..46797c5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -27,9 +27,11 @@
 import android.accounts.AccountManagerService;
 import android.app.ActivityManagerNative;
 import android.bluetooth.BluetoothAdapter;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentService;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
@@ -502,9 +504,6 @@
             notification.systemReady();
         }
 
-        if (statusBar != null) {
-            statusBar.systemReady();
-        }
         wm.systemReady();
 
         // Update the configuration for this context by hand, because we're going
@@ -523,7 +522,7 @@
         }
 
         // These are needed to propagate to the runnable below.
-        final StatusBarManagerService statusBarF = statusBar;
+        final Context contextF = context;
         final BatteryService batteryF = battery;
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
@@ -548,7 +547,7 @@
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
-                if (statusBarF != null) statusBarF.systemReady2();
+                startSystemUi(contextF);
                 if (batteryF != null) batteryF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
@@ -578,6 +577,14 @@
         Looper.loop();
         Slog.d(TAG, "System ServerThread is exiting!");
     }
+
+    static final void startSystemUi(Context context) {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName("com.android.systemui",
+                    "com.android.systemui.SystemUIService"));
+        Slog.d(TAG, "Starting service: " + intent);
+        context.startService(intent);
+    }
 }
 
 public class SystemServer {
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 2b4845b..f5b7ca9 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -65,7 +65,9 @@
 
     private final Context mContext;
 
-    private final ArrayList<Record> mRecords = new ArrayList();
+    // access should be inside synchronized (mRecords) for these two fields
+    private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
+    private final ArrayList<Record> mRecords = new ArrayList<Record>();
 
     private final IBatteryStats mBatteryStats;
 
@@ -158,7 +160,11 @@
                 r.events = events;
                 if (notifyNow) {
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-                        sendServiceState(r, mServiceState);
+                        try {
+                            r.callback.onServiceStateChanged(new ServiceState(mServiceState));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                     if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                         try {
@@ -184,7 +190,11 @@
                         }
                     }
                     if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-                        sendCellLocation(r, mCellLocation);
+                        try {
+                            r.callback.onCellLocationChanged(new Bundle(mCellLocation));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                     if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                         try {
@@ -238,7 +248,6 @@
         if (!checkNotifyPermission("notifyCallState()")) {
             return;
         }
-        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
         synchronized (mRecords) {
             mCallState = state;
             mCallIncomingNumber = incomingNumber;
@@ -247,11 +256,11 @@
                     try {
                         r.callback.onCallStateChanged(state, incomingNumber);
                     } catch (RemoteException ex) {
-                        removeList.add(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            for (IBinder b : removeList) remove(b);
+            handleRemoveListLocked();
         }
         broadcastCallStateChanged(state, incomingNumber);
     }
@@ -260,13 +269,19 @@
         if (!checkNotifyPermission("notifyServiceState()")){
             return;
         }
+        Slog.i(TAG, "notifyServiceState: " + state);
         synchronized (mRecords) {
             mServiceState = state;
             for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-                    sendServiceState(r, state);
+                    try {
+                        r.callback.onServiceStateChanged(new ServiceState(state));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
                 }
             }
+            handleRemoveListLocked();
         }
         broadcastServiceStateChanged(state);
     }
@@ -275,12 +290,15 @@
         if (!checkNotifyPermission("notifySignalStrength()")) {
             return;
         }
-        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
         synchronized (mRecords) {
             mSignalStrength = signalStrength;
             for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
-                    sendSignalStrength(r, signalStrength);
+                    try {
+                        r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
                 }
                 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                     try {
@@ -288,11 +306,11 @@
                         r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
                                 : gsmSignalStrength));
                     } catch (RemoteException ex) {
-                        removeList.add(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            for (IBinder b : removeList) remove(b);
+            handleRemoveListLocked();
         }
         broadcastSignalStrengthChanged(signalStrength);
     }
@@ -301,7 +319,6 @@
         if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
             return;
         }
-        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
         synchronized (mRecords) {
             mMessageWaiting = mwi;
             for (Record r : mRecords) {
@@ -309,11 +326,11 @@
                     try {
                         r.callback.onMessageWaitingIndicatorChanged(mwi);
                     } catch (RemoteException ex) {
-                        removeList.add(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            for (IBinder b : removeList) remove(b);
+            handleRemoveListLocked();
         }
     }
 
@@ -321,7 +338,6 @@
         if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
             return;
         }
-        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
         synchronized (mRecords) {
             mCallForwarding = cfi;
             for (Record r : mRecords) {
@@ -329,11 +345,11 @@
                     try {
                         r.callback.onCallForwardingIndicatorChanged(cfi);
                     } catch (RemoteException ex) {
-                        removeList.add(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            for (IBinder b : removeList) remove(b);
+            handleRemoveListLocked();
         }
     }
 
@@ -341,7 +357,7 @@
         if (!checkNotifyPermission("notifyDataActivity()" )) {
             return;
         }
-        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
+        handleRemoveListLocked();
         synchronized (mRecords) {
             mDataActivity = state;
             for (Record r : mRecords) {
@@ -349,11 +365,11 @@
                     try {
                         r.callback.onDataActivity(state);
                     } catch (RemoteException ex) {
-                        removeList.add(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            for (IBinder b : removeList) remove(b);
+            handleRemoveListLocked();
         }
     }
 
@@ -363,6 +379,9 @@
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
+        Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
+                + isDataConnectivityPossible + " reason=" + reason
+                + " apn=" + apn + " networkType=" + networkType);
         synchronized (mRecords) {
             boolean modified = false;
             if (state == TelephonyManager.DATA_CONNECTED) {
@@ -392,17 +411,16 @@
                 modified = true;
             }
             if (modified) {
-                ArrayList<IBinder> removeList = new ArrayList<IBinder>();
                 for (Record r : mRecords) {
                     if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
                         try {
                             r.callback.onDataConnectionStateChanged(state, networkType);
                         } catch (RemoteException ex) {
-                            removeList.add(r.binder);
+                            mRemoveList.add(r.binder);
                         }
                     }
                 }
-                for (IBinder b : removeList) remove(b);
+                handleRemoveListLocked();
             }
         }
         broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
@@ -438,36 +456,15 @@
             mCellLocation = cellLocation;
             for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-                    sendCellLocation(r, cellLocation);
+                    try {
+                        r.callback.onCellLocationChanged(new Bundle(cellLocation));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+
                 }
             }
-        }
-    }
-
-    /**
-     * Copy the service state object so they can't mess it up in the local calls
-     */
-    private void sendServiceState(Record r, ServiceState state) {
-        try {
-            r.callback.onServiceStateChanged(new ServiceState(state));
-        } catch (RemoteException ex) {
-            remove(r.binder);
-        }
-    }
-
-    private void sendCellLocation(Record r, Bundle cellLocation) {
-        try {
-            r.callback.onCellLocationChanged(new Bundle(cellLocation));
-        } catch (RemoteException ex) {
-            remove(r.binder);
-        }
-    }
-
-    private void sendSignalStrength(Record r, SignalStrength signalStrength) {
-        try {
-            r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
-        } catch (RemoteException ex) {
-            remove(r.binder);
+            handleRemoveListLocked();
         }
     }
 
@@ -628,4 +625,13 @@
                     android.Manifest.permission.READ_PHONE_STATE, null);
         }
     }
+
+    private void handleRemoveListLocked() {
+        if (mRemoveList.size() > 0) {
+            for (IBinder b: mRemoveList) {
+                remove(b);
+            }
+            mRemoveList.clear();
+        }
+    }
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 1241a7e..4f75366 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -65,6 +65,7 @@
 import java.io.PrintWriter;
 
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.AsyncChannel;
 import com.android.server.am.BatteryStatsService;
 import com.android.internal.R;
 
@@ -198,6 +199,45 @@
     private int mNumScansSinceNetworkStateChange;
 
     /**
+     * Asynchronous channel to WifiStateMachine
+     */
+    private AsyncChannel mChannel;
+
+    /**
+     * TODO: Possibly change WifiService into an AsyncService.
+     */
+    private class WifiServiceHandler extends Handler {
+        private AsyncChannel mWshChannel;
+
+        WifiServiceHandler(android.os.Looper looper, Context context) {
+            super(looper);
+            mWshChannel = new AsyncChannel();
+            mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        mChannel = mWshChannel;
+                    } else {
+                        Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
+                                msg.arg1);
+                        mChannel = null;
+                    }
+                    break;
+                }
+                default: {
+                    Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
+                    break;
+                }
+            }
+        }
+    }
+    WifiServiceHandler mHandler;
+
+    /**
      * Temporary for computing UIDS that are responsible for starting WIFI.
      * Protected by mWifiStateTracker lock.
      */
@@ -218,6 +258,7 @@
 
         HandlerThread wifiThread = new HandlerThread("WifiService");
         wifiThread.start();
+        mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
 
         mContext.registerReceiver(
                 new BroadcastReceiver() {
@@ -602,7 +643,12 @@
      */
     public boolean removeNetwork(int netId) {
         enforceChangePermission();
-        return mWifiStateMachine.syncRemoveNetwork(netId);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -843,7 +889,9 @@
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                Slog.d(TAG, "ACTION_SCREEN_ON");
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_SCREEN_ON");
+                }
                 mAlarmManager.cancel(mIdleIntent);
                 mDeviceIdle = false;
                 mScreenOff = false;
@@ -853,7 +901,9 @@
                 mWifiStateMachine.enableRssiPolling(true);
                 updateWifiState();
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                Slog.d(TAG, "ACTION_SCREEN_OFF");
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_SCREEN_OFF");
+                }
                 mScreenOff = true;
                 mWifiStateMachine.enableRssiPolling(false);
                 /*
@@ -870,19 +920,26 @@
                         // but not as long as we would if connected (below)
                         // TODO - fix the race conditions and switch back to the immediate turn-off
                         long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
-                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+                        if (DBG) {
+                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+                        }
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                         //  // do not keep Wifi awake when screen is off if Wifi is not associated
                         //  mDeviceIdle = true;
                         //  updateWifiState();
                     } else {
                         long triggerTime = System.currentTimeMillis() + idleMillis;
-                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                        if (DBG) {
+                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
+                                    + "ms");
+                        }
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                     }
                 }
             } else if (action.equals(ACTION_DEVICE_IDLE)) {
-                Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+                if (DBG) {
+                    Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+                }
                 mDeviceIdle = true;
                 reportStartWorkSource();
                 updateWifiState();
@@ -895,10 +952,15 @@
                  * the already-set timer.
                  */
                 int pluggedType = intent.getIntExtra("plugged", 0);
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
+                }
                 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
                         !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
                     long triggerTime = System.currentTimeMillis() + idleMillis;
-                    Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                    if (DBG) {
+                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                    }
                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                 }
                 mPluggedType = pluggedType;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 0623f5b..d469d67 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -136,7 +136,6 @@
     static final long USB_DISCONNECT_DELAY = 1000;
 
     public Tethering(Context context, Looper looper) {
-        Log.d(TAG, "Tethering starting");
         mContext = context;
         mLooper = looper;
 
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
index bc96980..813d255 100644
--- a/services/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -44,6 +44,7 @@
 public class GpsXtraDownloader {
 
     private static final String TAG = "GpsXtraDownloader";
+    static final boolean DEBUG = false;
     
     private Context mContext;
     private String[] mXtraServers;
@@ -107,7 +108,7 @@
 
     protected static byte[] doDownload(String url, boolean isProxySet, 
             String proxyHost, int proxyPort) {
-        if (Config.LOGD) Log.d(TAG, "Downloading XTRA data from " + url);
+        if (DEBUG) Log.d(TAG, "Downloading XTRA data from " + url);
 
         AndroidHttpClient client = null;
         try {
@@ -130,7 +131,7 @@
             HttpResponse response = client.execute(req);
             StatusLine status = response.getStatusLine();
             if (status.getStatusCode() != 200) { // HTTP 200 is success.
-                if (Config.LOGD) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+                if (DEBUG) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
                 return null;
             }
 
@@ -159,7 +160,7 @@
             }
             return body;
         } catch (Exception e) {
-            if (Config.LOGD) Log.d(TAG, "error " + e);
+            if (DEBUG) Log.d(TAG, "error " + e);
         } finally {
             if (client != null) {
                 client.close();
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 397a84a..d4513e9 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -164,7 +164,7 @@
     
     jboolean value = false;
     if (readFromFile(path, buf, SIZE) > 0) {
-        if (buf[0] == '1') {
+        if (buf[0] != '0') {
             value = true;
         }
     }
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index e4825d0..1c2a2c8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -24,6 +24,10 @@
 ifeq ($(TARGET_BOARD_PLATFORM), omap3)
 	LOCAL_CFLAGS += -DNO_RGBX_8888 -DHAS_PUSH_BUFFERS
 endif
+ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
+	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+endif
+
 
 # need "-lrt" on Linux simulator to pick up clock_gettime
 ifeq ($(TARGET_SIMULATOR),true)
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index bd348bf..8926c03 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -199,8 +199,18 @@
      * Create our OpenGL ES context
      */
     
-    context = eglCreateContext(display, config, NULL, NULL);
-    
+
+    EGLint contextAttributes[] = {
+#ifdef EGL_IMG_context_priority
+#ifdef HAS_CONTEXT_PRIORITY
+#warning "using EGL_IMG_context_priority"
+        EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
+#endif
+#endif
+        EGL_NONE, EGL_NONE
+    };
+    context = eglCreateContext(display, config, NULL, contextAttributes);
+
     mDisplay = display;
     mConfig  = config;
     mSurface = surface;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 3f0ec0a..ab7b601 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -1626,7 +1626,15 @@
     }
 
 
-
+    private boolean hasMoreThanOneRingingCall() {
+        int count = 0;
+        for (Call call : mRingingCalls) {
+            if (call.getState().isRinging()) {
+                if (++count > 1) return true;
+            }
+        }
+        return false;
+    }
 
     private Handler mHandler = new Handler() {
 
@@ -1644,7 +1652,17 @@
                     break;
                 case EVENT_NEW_RINGING_CONNECTION:
                     if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
-                    mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+                    if (getActiveFgCallState().isDialing() || hasMoreThanOneRingingCall()) {
+                        Connection c = (Connection) ((AsyncResult) msg.obj).result;
+                        try {
+                            Log.d(LOG_TAG, "silently drop incoming call: " + c.getCall());
+                            c.getCall().hangup();
+                        } catch (CallStateException e) {
+                            Log.w(LOG_TAG, "new ringing connection", e);
+                        }
+                    } else {
+                        mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+                    }
                     break;
                 case EVENT_UNKNOWN_CONNECTION:
                     if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)");
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index db16dec..82fcb6a 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -267,6 +267,7 @@
         Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
 
         CallerInfo info = getCallerInfo(context, contactUri);
+        info = doSecondaryLookupIfNecessary(context, number, info);
 
         // if no query results were returned with a viable number,
         // fill in the original number value we used to query with.
@@ -278,6 +279,30 @@
     }
 
     /**
+     * Performs another lookup if previous lookup fails and it's a SIP call
+     * and the peer's username is all numeric. Look up the username as it
+     * could be a PSTN number in the contact database.
+     *
+     * @param context the query context
+     * @param number the original phone number, could be a SIP URI
+     * @param previousResult the result of previous lookup
+     * @return previousResult if it's not the case
+     */
+    static CallerInfo doSecondaryLookupIfNecessary(Context context,
+            String number, CallerInfo previousResult) {
+        if (!previousResult.contactExists
+                && PhoneNumberUtils.isUriNumber(number)) {
+            String username = number.substring(0, number.indexOf('@'));
+            if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
+                previousResult = getCallerInfo(context,
+                        Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+                                Uri.encode(username)));
+            }
+        }
+        return previousResult;
+    }
+
+    /**
      * getCallerId: a convenience method to get the caller id for a given
      * number.
      *
@@ -409,6 +434,7 @@
                 .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
                 .append("\nemergency: " + mIsEmergency)
                 .append("\nvoicemail " + mIsVoiceMail)
+                .append("\ncontactExists " + contactExists)
                 .toString();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 8471efa..c47e076 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -38,7 +38,7 @@
  */
 
 public class CallerInfoAsyncQuery {
-    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+    private static final boolean DBG = false;
     private static final String LOG_TAG = "CallerInfoAsyncQuery";
 
     private static final int EVENT_NEW_QUERY = 1;
@@ -129,13 +129,13 @@
                     // However, if there is any code that this Handler calls (such as in
                     // super.handleMessage) that DOES place unexpected messages on the
                     // queue, then we need pass these messages on.
-                    if (DBG) log("Unexpected command (CookieWrapper is null): " + msg.what +
+                    if (DBG) Log.d(LOG_TAG, "Unexpected command (CookieWrapper is null): " + msg.what +
                             " ignored by CallerInfoWorkerHandler, passing onto parent.");
 
                     super.handleMessage(msg);
                 } else {
 
-                    if (DBG) log("Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
+                    if (DBG) Log.d(LOG_TAG, "Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
                         " command: " + msg.what + " query URI: " + sanitizeUriToString(args.uri));
 
                     switch (cw.event) {
@@ -191,7 +191,7 @@
          */
         @Override
         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            if (DBG) log("##### onQueryComplete() #####   query complete for token: " + token);
+            if (DBG) Log.d(LOG_TAG, "##### onQueryComplete() #####   query complete for token: " + token);
 
             //get the cookie and notify the listener.
             CookieWrapper cw = (CookieWrapper) cookie;
@@ -200,7 +200,7 @@
                 // from within this code.
                 // However, if there is any code that calls this method, we should
                 // check the parameters to make sure they're viable.
-                if (DBG) log("Cookie is null, ignoring onQueryComplete() request.");
+                if (DBG) Log.d(LOG_TAG, "Cookie is null, ignoring onQueryComplete() request.");
                 return;
             }
 
@@ -229,7 +229,15 @@
                     mCallerInfo = new CallerInfo().markAsVoiceMail();
                 } else {
                     mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
-                    if (DBG) log("==> Got mCallerInfo: " + mCallerInfo);
+                    if (DBG) Log.d(LOG_TAG, "==> Got mCallerInfo: " + mCallerInfo);
+
+                    CallerInfo newCallerInfo = CallerInfo.doSecondaryLookupIfNecessary(
+                            mQueryContext, cw.number, mCallerInfo);
+                    if (newCallerInfo != mCallerInfo) {
+                        mCallerInfo = newCallerInfo;
+                        if (DBG) Log.d(LOG_TAG, "#####async contact look up with numeric username"
+                                + mCallerInfo);
+                    }
 
                     // Use the number entered by the user for display.
                     if (!TextUtils.isEmpty(cw.number)) {
@@ -241,7 +249,7 @@
                     }
                 }
 
-                if (DBG) log("constructing CallerInfo object for token: " + token);
+                if (DBG) Log.d(LOG_TAG, "constructing CallerInfo object for token: " + token);
 
                 //notify that we can clean up the queue after this.
                 CookieWrapper endMarker = new CookieWrapper();
@@ -251,7 +259,7 @@
 
             //notify the listener that the query is complete.
             if (cw.listener != null) {
-                if (DBG) log("notifying listener: " + cw.listener.getClass().toString() +
+                if (DBG) Log.d(LOG_TAG, "notifying listener: " + cw.listener.getClass().toString() +
                              " for token: " + token + mCallerInfo);
                 cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
             }
@@ -274,7 +282,7 @@
         CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
         c.allocate(context, contactRef);
 
-        if (DBG) log("starting query for URI: " + contactRef + " handler: " + c.toString());
+        if (DBG) Log.d(LOG_TAG, "starting query for URI: " + contactRef + " handler: " + c.toString());
 
         //create cookieWrapper, start query
         CookieWrapper cw = new CookieWrapper();
@@ -301,9 +309,9 @@
     public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
             OnQueryCompleteListener listener, Object cookie) {
         if (DBG) {
-            log("##### CallerInfoAsyncQuery startQuery()... #####");
-            log("- number: " + /*number*/ "xxxxxxx");
-            log("- cookie: " + cookie);
+            Log.d(LOG_TAG, "##### CallerInfoAsyncQuery startQuery()... #####");
+            Log.d(LOG_TAG, "- number: " + /*number*/ "xxxxxxx");
+            Log.d(LOG_TAG, "- cookie: " + cookie);
         }
 
         // Construct the URI object and query params, and start the query.
@@ -314,7 +322,7 @@
 
         if (PhoneNumberUtils.isUriNumber(number)) {
             // "number" is really a SIP address.
-            if (DBG) log("  - Treating number as a SIP address: " + /*number*/ "xxxxxxx");
+            if (DBG) Log.d(LOG_TAG, "  - Treating number as a SIP address: " + /*number*/ "xxxxxxx");
 
             // We look up SIP addresses directly in the Data table:
             contactRef = Data.CONTENT_URI;
@@ -346,11 +354,11 @@
         }
 
         if (DBG) {
-            log("==> contactRef: " + sanitizeUriToString(contactRef));
-            log("==> selection: " + selection);
+            Log.d(LOG_TAG, "==> contactRef: " + sanitizeUriToString(contactRef));
+            Log.d(LOG_TAG, "==> selection: " + selection);
             if (selectionArgs != null) {
                 for (int i = 0; i < selectionArgs.length; i++) {
-                    log("==> selectionArgs[" + i + "]: " + selectionArgs[i]);
+                    Log.d(LOG_TAG, "==> selectionArgs[" + i + "]: " + selectionArgs[i]);
                 }
             }
         }
@@ -388,7 +396,7 @@
      */
     public void addQueryListener(int token, OnQueryCompleteListener listener, Object cookie) {
 
-        if (DBG) log("adding listener to query: " + sanitizeUriToString(mHandler.mQueryUri) +
+        if (DBG) Log.d(LOG_TAG, "adding listener to query: " + sanitizeUriToString(mHandler.mQueryUri) +
                 " handler: " + mHandler.toString());
 
         //create cookieWrapper, add query request to end of queue.
@@ -436,11 +444,4 @@
             return "";
         }
     }
-
-    /**
-     * static logging method
-     */
-    private static void log(String msg) {
-        Log.d(LOG_TAG, msg);
-    }
 }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 09d3c8e..ef31ddd 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -28,6 +28,7 @@
 import android.os.Message;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.telephony.Call;
@@ -483,7 +484,12 @@
 
         void merge(SipCall that) throws CallStateException {
             AudioGroup audioGroup = getAudioGroup();
-            for (Connection c : that.connections) {
+
+            // copy to an array to avoid concurrent modification as connections
+            // in that.connections will be removed in add(SipConnection).
+            Connection[] cc = that.connections.toArray(
+                    new Connection[that.connections.size()]);
+            for (Connection c : cc) {
                 SipConnection conn = (SipConnection) c;
                 add(conn);
                 if (conn.getState() == Call.State.HOLDING) {
@@ -676,6 +682,18 @@
             this(owner, callee, getUriString(callee));
         }
 
+        @Override
+        public String getCnapName() {
+            String displayName = mPeer.getDisplayName();
+            return TextUtils.isEmpty(displayName) ? null
+                                                  : displayName;
+        }
+
+        @Override
+        public int getNumberPresentation() {
+            return Connection.PRESENTATION_ALLOWED;
+        }
+
         void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) {
             setState(newState);
             mSipAudioCall = sipAudioCall;
@@ -704,7 +722,6 @@
             setState(Call.State.DIALING);
             mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
                     TIMEOUT_MAKE_CALL);
-            mSipAudioCall.setRingbackToneEnabled(false);
             mSipAudioCall.setListener(mAdapter);
         }
 
@@ -789,7 +806,9 @@
         @Override
         public void separate() throws CallStateException {
             synchronized (SipPhone.class) {
-                SipCall call = (SipCall) SipPhone.this.getBackgroundCall();
+                SipCall call = (getPhone() == SipPhone.this)
+                        ? (SipCall) SipPhone.this.getBackgroundCall()
+                        : (SipCall) SipPhone.this.getForegroundCall();
                 if (call.getState() != Call.State.IDLE) {
                     throw new CallStateException(
                             "cannot put conn back to a call in non-idle state: "
@@ -799,10 +818,20 @@
                         + mPeer.getUriString() + " from " + mOwner + " back to "
                         + call);
 
+                // separate the AudioGroup and connection from the original call
+                Phone originalPhone = getPhone();
                 AudioGroup audioGroup = call.getAudioGroup(); // may be null
                 call.add(this);
                 mSipAudioCall.setAudioGroup(audioGroup);
-                call.hold();
+
+                // put the original call to bg; and the separated call becomes
+                // fg if it was in bg
+                originalPhone.switchHoldingAndActive();
+
+                // start audio and notify the phone app of the state change
+                call = (SipCall) SipPhone.this.getForegroundCall();
+                mSipAudioCall.startAudio();
+                call.onConnectionStateChanged(this);
             }
         }
 
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index d859c4c..0179748 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -18,10 +18,6 @@
 
 import android.content.Context;
 import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
 import android.net.rtp.AudioCodec;
 import android.net.rtp.AudioGroup;
 import android.net.rtp.AudioStream;
@@ -30,8 +26,6 @@
 import android.net.wifi.WifiManager;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.Vibrator;
-import android.provider.Settings;
 import android.util.Log;
 
 import java.io.IOException;
@@ -47,6 +41,16 @@
  * facilitates instantiating a {@code SipAudioCall} object for making/receiving
  * calls. See {@link SipManager#makeAudioCall} and
  * {@link SipManager#takeAudioCall}.
+ *
+ * <p>Requires permissions to use this class:
+ *   {@link android.Manifest.permission#INTERNET} and
+ *   {@link android.Manifest.permission#USE_SIP}.
+ * <br/>Requires permissions to {@link #startAudio}:
+ *   {@link android.Manifest.permission#RECORD_AUDIO},
+ *   {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
+ *   {@link android.Manifest.permission#WAKE_LOCK}.
+ * <br/>Requires permissions to {@link #setSpeakerMode}:
+ *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
  */
 public class SipAudioCall {
     private static final String TAG = SipAudioCall.class.getSimpleName();
@@ -175,11 +179,6 @@
     private boolean mMuted = false;
     private boolean mHold = false;
 
-    private boolean mRingbackToneEnabled = true;
-    private boolean mRingtoneEnabled = true;
-    private Ringtone mRingtone;
-    private ToneGenerator mRingbackTone;
-
     private SipProfile mPendingCallRequest;
     private WifiManager mWm;
     private WifiManager.WifiLock mWifiHighPerfLock;
@@ -285,8 +284,6 @@
 
     private synchronized void close(boolean closeRtp) {
         if (closeRtp) stopCall(RELEASE_SOCKET);
-        stopRingbackTone();
-        stopRinging();
 
         mInCall = false;
         mHold = false;
@@ -366,7 +363,6 @@
             @Override
             public void onRingingBack(SipSession session) {
                 Log.d(TAG, "sip call ringing back: " + session);
-                if (!mInCall) startRingbackTone();
                 Listener listener = mListener;
                 if (listener != null) {
                     try {
@@ -403,8 +399,6 @@
             @Override
             public void onCallEstablished(SipSession session,
                     String sessionDescription) {
-                stopRingbackTone();
-                stopRinging();
                 mPeerSd = sessionDescription;
                 Log.v(TAG, "onCallEstablished()" + mPeerSd);
 
@@ -533,10 +527,6 @@
             Log.v(TAG, "attachCall()" + mPeerSd);
             try {
                 session.setListener(createListener());
-
-                if (getState() == SipSession.State.INCOMING_CALL) {
-                    startRinging();
-                }
             } catch (Throwable e) {
                 Log.e(TAG, "attachCall()", e);
                 throwSipException(e);
@@ -580,7 +570,6 @@
      */
     public void endCall() throws SipException {
         synchronized (this) {
-            stopRinging();
             stopCall(RELEASE_SOCKET);
             mInCall = false;
 
@@ -625,7 +614,6 @@
      */
     public void answerCall(int timeout) throws SipException {
         synchronized (this) {
-            stopRinging();
             try {
                 mAudioStream = new AudioStream(InetAddress.getByName(
                         getLocalIp()));
@@ -798,7 +786,11 @@
         }
     }
 
-    /** Puts the device to speaker mode. */
+    /**
+     * Puts the device to speaker mode.
+     * <p>Requires permission:
+     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+     */
     public void setSpeakerMode(boolean speakerMode) {
         synchronized (this) {
             ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
@@ -898,6 +890,10 @@
     /**
      * Starts the audio for the established call. This method should be called
      * after {@link Listener#onCallEstablished} is called.
+     * <p>Requires permission:
+     *   {@link android.Manifest.permission#RECORD_AUDIO},
+     *   {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
+     *   {@link android.Manifest.permission#WAKE_LOCK}.
      */
     public void startAudio() {
         try {
@@ -1026,69 +1022,6 @@
         return mSipSession.getLocalIp();
     }
 
-
-    /**
-     * Enables/disables the ring-back tone.
-     *
-     * @param enabled true to enable; false to disable
-     */
-    public void setRingbackToneEnabled(boolean enabled) {
-        synchronized (this) {
-            mRingbackToneEnabled = enabled;
-        }
-    }
-
-    /**
-     * Enables/disables the ring tone.
-     *
-     * @param enabled true to enable; false to disable
-     */
-    public void setRingtoneEnabled(boolean enabled) {
-        synchronized (this) {
-            mRingtoneEnabled = enabled;
-        }
-    }
-
-    private void startRingbackTone() {
-        if (!mRingbackToneEnabled) return;
-        if (mRingbackTone == null) {
-            // The volume relative to other sounds in the stream
-            int toneVolume = 80;
-            mRingbackTone = new ToneGenerator(
-                    AudioManager.STREAM_VOICE_CALL, toneVolume);
-        }
-        mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
-    }
-
-    private void stopRingbackTone() {
-        if (mRingbackTone != null) {
-            mRingbackTone.stopTone();
-            mRingbackTone.release();
-            mRingbackTone = null;
-        }
-    }
-
-    private void startRinging() {
-        if (!mRingtoneEnabled) return;
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .vibrate(new long[] {0, 1000, 1000}, 1);
-        AudioManager am = (AudioManager)
-                mContext.getSystemService(Context.AUDIO_SERVICE);
-        if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
-            String ringtoneUri =
-                    Settings.System.DEFAULT_RINGTONE_URI.toString();
-            mRingtone = RingtoneManager.getRingtone(mContext,
-                    Uri.parse(ringtoneUri));
-            mRingtone.play();
-        }
-    }
-
-    private void stopRinging() {
-        ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
-                .cancel();
-        if (mRingtone != null) mRingtone.stop();
-    }
-
     private void throwSipException(Throwable throwable) throws SipException {
         if (throwable instanceof SipException) {
             throw (SipException) throwable;
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 2f03e34..38d2b0c 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -50,6 +50,9 @@
  * </ul>
  * {@code SipManager} can only be instantiated if SIP API is supported by the
  * device. (See {@link #isApiSupported}).
+ * <p>Requires permissions to use this class:
+ *   {@link android.Manifest.permission#INTERNET} and
+ *   {@link android.Manifest.permission#USE_SIP}.
  */
 public class SipManager {
     /**
@@ -351,17 +354,6 @@
     }
 
     /**
-     * The method calls {@code takeAudioCall(incomingCallIntent,
-     * listener, true}.
-     *
-     * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
-     */
-    public SipAudioCall takeAudioCall(Intent incomingCallIntent,
-            SipAudioCall.Listener listener) throws SipException {
-        return takeAudioCall(incomingCallIntent, listener, true);
-    }
-
-    /**
      * Creates a {@link SipAudioCall} to take an incoming call. Before the call
      * is returned, the listener will receive a
      * {@link SipAudioCall.Listener#onRinging}
@@ -374,8 +366,7 @@
      * @throws SipException if calling the SIP service results in an error
      */
     public SipAudioCall takeAudioCall(Intent incomingCallIntent,
-            SipAudioCall.Listener listener, boolean ringtoneEnabled)
-            throws SipException {
+            SipAudioCall.Listener listener) throws SipException {
         if (incomingCallIntent == null) return null;
 
         String callId = getCallId(incomingCallIntent);
@@ -394,7 +385,6 @@
             if (session == null) return null;
             SipAudioCall call = new SipAudioCall(
                     mContext, session.getLocalProfile());
-            call.setRingtoneEnabled(ringtoneEnabled);
             call.attachCall(new SipSession(session), offerSd);
             call.setListener(listener);
             return call;
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index 2514262..13e6f14 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -365,6 +365,10 @@
             Response response = mMessageFactory.createResponse(
                     Response.BUSY_HERE, request);
 
+            if (inviteTransaction == null) {
+                inviteTransaction = getServerTransaction(event);
+            }
+
             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
                 if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
                 inviteTransaction.sendResponse(response);
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 1df08c0..84e0803 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -68,7 +68,7 @@
 public final class SipService extends ISipService.Stub {
     static final String TAG = "SipService";
     static final boolean DEBUGV = false;
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final boolean DEBUG_TIMER = DEBUG && false;
     private static final int EXPIRY_TIME = 3600;
     private static final int SHORT_EXPIRY_TIME = 10;
@@ -79,6 +79,7 @@
     private String mNetworkType;
     private boolean mConnected;
     private WakeupTimer mTimer;
+    private WifiScanProcess mWifiScanProcess;
     private WifiManager.WifiLock mWifiLock;
     private boolean mWifiOnly;
 
@@ -104,7 +105,7 @@
         if (SipManager.isApiSupported(context)) {
             ServiceManager.addService("sip", new SipService(context));
             context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
-            Log.i(TAG, "SIP service started");
+            if (DEBUG) Log.i(TAG, "SIP service started");
         }
     }
 
@@ -222,7 +223,7 @@
         SipSessionGroupExt group = mSipGroups.get(localProfileUri);
         if (group == null) return;
         if (!isCallerCreatorOrRadio(group)) {
-            Log.d(TAG, "only creator or radio can close this profile");
+            Log.w(TAG, "only creator or radio can close this profile");
             return;
         }
 
@@ -244,7 +245,7 @@
         if (isCallerCreatorOrRadio(group)) {
             return group.isOpened();
         } else {
-            Log.i(TAG, "only creator or radio can query on the profile");
+            Log.w(TAG, "only creator or radio can query on the profile");
             return false;
         }
     }
@@ -257,7 +258,7 @@
         if (isCallerCreatorOrRadio(group)) {
             return group.isRegistered();
         } else {
-            Log.i(TAG, "only creator or radio can query on the profile");
+            Log.w(TAG, "only creator or radio can query on the profile");
             return false;
         }
     }
@@ -271,7 +272,7 @@
         if (isCallerCreator(group)) {
             group.setListener(listener);
         } else {
-            Log.i(TAG, "only creator can set listener on the profile");
+            Log.w(TAG, "only creator can set listener on the profile");
         }
     }
 
@@ -285,7 +286,7 @@
             SipSessionGroupExt group = createGroup(localProfile);
             return group.createSession(listener);
         } catch (SipException e) {
-            Log.w(TAG, "createSession()", e);
+            if (DEBUG) Log.d(TAG, "createSession()", e);
             return null;
         }
     }
@@ -303,7 +304,7 @@
             s.connect(InetAddress.getByName("192.168.1.1"), 80);
             return s.getLocalAddress().getHostAddress();
         } catch (IOException e) {
-            Log.w(TAG, "determineLocalIp()", e);
+            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
             // dont do anything; there should be a connectivity change going
             return null;
         }
@@ -371,6 +372,7 @@
                     mContext.getSystemService(Context.WIFI_SERVICE))
                     .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
             mWifiLock.acquire();
+            if (!mConnected) startWifiScanner();
         }
     }
 
@@ -379,6 +381,20 @@
             if (DEBUG) Log.d(TAG, "~~~~~~~~~~~~~~~~~~~~~ release wifi lock");
             mWifiLock.release();
             mWifiLock = null;
+            stopWifiScanner();
+        }
+    }
+
+    private synchronized void startWifiScanner() {
+        if (mWifiScanProcess == null) {
+            mWifiScanProcess = new WifiScanProcess();
+        }
+        mWifiScanProcess.start();
+    }
+
+    private synchronized void stopWifiScanner() {
+        if (mWifiScanProcess != null) {
+            mWifiScanProcess.stop();
         }
     }
 
@@ -413,8 +429,10 @@
                 for (SipSessionGroupExt group : mSipGroups.values()) {
                     group.onConnectivityChanged(true);
                 }
+                if (isWifi && (mWifiLock != null)) stopWifiScanner();
             } else {
                 mMyWakeLock.reset(); // in case there's a leak
+                if (isWifi && (mWifiLock != null)) startWifiScanner();
             }
         } catch (SipException e) {
             Log.e(TAG, "onConnectivityChanged()", e);
@@ -430,6 +448,21 @@
         }
     }
 
+    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
+            SipSessionGroup.SipSessionImpl ringingSession) {
+        String callId = ringingSession.getCallId();
+        for (SipSessionGroupExt group : mSipGroups.values()) {
+            if ((group != ringingGroup) && group.containsSession(callId)) {
+                if (DEBUG) Log.d(TAG, "call self: "
+                        + ringingSession.getLocalProfile().getUriString()
+                        + " -> " + group.getLocalProfile().getUriString());
+                return true;
+            }
+        }
+        return false;
+    }
+
+
     private class SipSessionGroupExt extends SipSessionAdapter {
         private SipSessionGroup mSipGroup;
         private PendingIntent mIncomingCallPendingIntent;
@@ -452,6 +485,10 @@
             return mSipGroup.getLocalProfile();
         }
 
+        public boolean containsSession(String callId) {
+            return mSipGroup.containsSession(callId);
+        }
+
         // network connectivity is tricky because network can be disconnected
         // at any instant so need to deal with exceptions carefully even when
         // you think you are connected
@@ -467,7 +504,7 @@
                     return createSipSessionGroup(null, localProfile, password);
                 } else {
                     // recursive
-                    Log.wtf(TAG, "impossible!");
+                    Log.wtf(TAG, "impossible! recursive!");
                     throw new RuntimeException("createSipSessionGroup");
                 }
             }
@@ -551,7 +588,7 @@
                     (SipSessionGroup.SipSessionImpl) s;
             synchronized (SipService.this) {
                 try {
-                    if (!isRegistered()) {
+                    if (!isRegistered() || callingSelf(this, session)) {
                         session.endCall();
                         return;
                     }
@@ -592,6 +629,36 @@
         }
     }
 
+    private class WifiScanProcess implements Runnable {
+        private static final String TAG = "\\WIFI_SCAN/";
+        private static final int INTERVAL = 60;
+        private boolean mRunning = false;
+
+        private WifiManager mWifiManager;
+
+        public void start() {
+            if (mRunning) return;
+            mRunning = true;
+            mTimer.set(INTERVAL * 1000, this);
+        }
+
+        WifiScanProcess() {
+            mWifiManager = (WifiManager)
+                    mContext.getSystemService(Context.WIFI_SERVICE);
+        }
+
+        public void run() {
+            // scan and associate now
+            if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning...");
+            mWifiManager.startScanActive();
+        }
+
+        public void stop() {
+            mRunning = false;
+            mTimer.cancel(this);
+        }
+    }
+
     // KeepAliveProcess is controlled by AutoRegistrationProcess.
     // All methods will be invoked in sync with SipService.this.
     private class KeepAliveProcess implements Runnable {
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index bb246a6..50ce7dc 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -231,6 +231,10 @@
         }
     }
 
+    synchronized boolean containsSession(String callId) {
+        return mSessionMap.containsKey(callId);
+    }
+
     private synchronized SipSessionImpl getSipSession(EventObject event) {
         String key = SipHelper.getCallId(event);
         SipSessionImpl session = mSessionMap.get(key);
@@ -582,6 +586,7 @@
         }
 
         private void processCommand(EventObject command) throws SipException {
+            if (isLoggable(command)) Log.d(TAG, "process cmd: " + command);
             if (!process(command)) {
                 onError(SipErrorCode.IN_PROGRESS,
                         "cannot initiate a new transaction to execute: "
@@ -1050,6 +1055,13 @@
                 mSipHelper.sendCancel(mClientTransaction);
                 startSessionTimer(CANCEL_CALL_TIMER);
                 return true;
+            } else if (isRequestEvent(Request.INVITE, evt)) {
+                // Call self? Send BUSY HERE so server may redirect the call to
+                // voice mailbox.
+                RequestEvent event = (RequestEvent) evt;
+                mSipHelper.sendInviteBusyHere(event,
+                        event.getServerTransaction());
+                return true;
             }
             return false;
         }
@@ -1351,6 +1363,10 @@
         return DEBUG;
     }
 
+    private static boolean isLoggable(EventObject evt) {
+        return isLoggable(null, evt);
+    }
+
     private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
         if (!isLoggable(s)) return false;
         if (evt == null) return false;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index f2fdbad..65b910b 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -70,6 +70,7 @@
 import android.content.Intent;
 import android.content.Context;
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
@@ -148,6 +149,9 @@
      * and load configuration afterwards */
     private boolean mWpsStarted = false;
 
+    // Channel for sending replies.
+    private AsyncChannel mReplyChannel = new AsyncChannel();
+
     // Event log tags (must be in sync with event-log-tags)
     private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
     private static final int EVENTLOG_WIFI_EVENT_HANDLED        = 50022;
@@ -301,7 +305,6 @@
     /* Start Wi-Fi protected setup */
     private static final int CMD_START_WPS                        = 93;
 
-
     /**
      * Interval in milliseconds between polling for connection
      * status items that are not sent via asynchronous events.
@@ -699,8 +702,21 @@
      *
      * @param networkId id of the network to be removed
      */
-    public boolean syncRemoveNetwork(int networkId) {
-        return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
+    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
+        boolean result = resultMsg.arg1 != 0;
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * Return the result of a removeNetwork
+     *
+     * @param srcMsg is the original message
+     * @param result is true if successfully removed
+     */
+    private void removeNetworkReply(Message srcMsg, boolean result) {
+        mReplyChannel.replyToMessage(srcMsg, CMD_REMOVE_NETWORK, result ? 1 : 0);
     }
 
     private class EnableNetParams {
@@ -1583,7 +1599,6 @@
                     break;
                     /* Synchronous call returns */
                 case CMD_PING_SUPPLICANT:
-                case CMD_REMOVE_NETWORK:
                 case CMD_ENABLE_NETWORK:
                 case CMD_DISABLE_NETWORK:
                 case CMD_ADD_OR_UPDATE_NETWORK:
@@ -1598,6 +1613,9 @@
                     syncParams.mSyncReturn.stringValue = null;
                     notifyOnMsgObject(message);
                     break;
+                case CMD_REMOVE_NETWORK:
+                    removeNetworkReply(message, false);
+                    break;
                 case CMD_ENABLE_RSSI_POLL:
                     mEnableRssiPolling = (message.arg1 == 1);
                     mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
@@ -2023,10 +2041,8 @@
                     break;
                 case CMD_REMOVE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.boolValue = WifiConfigStore.removeNetwork(
-                                message.arg1);
-                    notifyOnMsgObject(message);
+                    boolean ok = WifiConfigStore.removeNetwork(message.arg1);
+                    removeNetworkReply(message, ok);
                     break;
                 case CMD_ENABLE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);