Merge "Waveform member variables must be private"
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 7629c31..33f37f8 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -151,7 +151,7 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        return inflater.inflate(com.android.internal.R.layout.preference_list_content,
+        return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
                 container, false);
     }
 
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 3010178..dcac243 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -273,6 +273,11 @@
      * @param accept TRUE if accept cookie
      */
     public synchronized void setAcceptCookie(boolean accept) {
+        if (useChromiumHttpStack()) {
+            nativeSetAcceptCookie(accept);
+            return;
+        }
+
         mAcceptCookie = accept;
     }
 
@@ -281,6 +286,10 @@
      * @return TRUE if accept cookie
      */
     public synchronized boolean acceptCookie() {
+        if (useChromiumHttpStack()) {
+            return nativeAcceptCookie();
+        }
+
         return mAcceptCookie;
     }
 
@@ -418,6 +427,10 @@
      * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
      */
     public String getCookie(String url) {
+        if (useChromiumHttpStack()) {
+            return nativeGetCookie(url);
+        }
+
         WebAddress uri;
         try {
             uri = new WebAddress(url);
@@ -1035,5 +1048,8 @@
 
     // Native functions
     private static native boolean nativeUseChromiumHttpStack();
+    private static native boolean nativeAcceptCookie();
+    private static native String nativeGetCookie(String url);
     private static native void nativeRemoveAllCookie();
+    private static native void nativeSetAcceptCookie(boolean accept);
 }
diff --git a/core/res/res/layout-port/preference_header_item.xml b/core/res/res/layout-port/preference_header_item.xml
deleted file mode 100644
index cc76c8e..0000000
--- a/core/res/res/layout-port/preference_header_item.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 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.
--->
-
-<!-- Layout of a header item in PreferenceActivity. -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="96dp"
-    android:background="?android:attr/activatedBackgroundIndicator"
-    android:paddingRight="?android:attr/scrollbarSize">
-    
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:layout_gravity="center"
-        android:paddingLeft="4dip"
-        android:paddingRight="4dip"
-        android:paddingTop="4dip"
-        android:paddingBottom="4dip">
-    
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_marginBottom="2dip" />
-    
-        <TextView android:id="@+android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_marginBottom="2dip"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-    
-        <TextView android:id="@+android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:ellipsize="end"
-            android:maxLines="2" />
-    
-    </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/preference_header_item.xml b/core/res/res/layout/preference_header_item.xml
index aba7b2ad..f00e7aa 100644
--- a/core/res/res/layout/preference_header_item.xml
+++ b/core/res/res/layout/preference_header_item.xml
@@ -18,7 +18,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:minHeight="48dp"
     android:background="?android:attr/activatedBackgroundIndicator"
     android:gravity="center_vertical"
     android:paddingRight="?android:attr/scrollbarSize">
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 7c0b791..5b42407 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -21,7 +21,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_height="match_parent"
-    android:layout_width="match_parent">
+    android:layout_width="match_parent"
+    android:background="@android:color/transparent">
 
     <LinearLayout
         android:orientation="horizontal"
@@ -34,6 +35,10 @@
             android:orientation="vertical"
             android:layout_width="0px"
             android:layout_height="match_parent"
+            android:layout_marginLeft="32dp"
+            android:layout_marginTop="32dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginBottom="32dp"
             android:layout_weight="10">
 
             <ListView android:id="@android:id/list"
@@ -41,6 +46,9 @@
                 android:layout_height="0px"
                 android:layout_weight="1"
                 android:drawSelectorOnTop="false"
+                android:background="#40404040"
+                android:cacheColorHint="@android:color/transparent"
+                android:listPreferredItemHeight="48dp"
                 android:scrollbarAlwaysDrawVerticalTrack="true" />
 
             <FrameLayout android:id="@+id/list_footer"
@@ -53,7 +61,12 @@
         <FrameLayout android:id="@+id/prefs"
                 android:layout_width="0px"
                 android:layout_height="match_parent"
-                android:layout_weight="33"
+                android:layout_weight="25"
+                android:layout_marginLeft="16dp"
+                android:layout_marginRight="16dp"
+                android:layout_marginTop="16dp"
+                android:layout_marginBottom="16dp"
+                android:background="#60202040"
                 android:visibility="gone" />
     </LinearLayout>
 
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
new file mode 100644
index 0000000..c499b8a
--- /dev/null
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+** 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@android:color/transparent">
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+    <RelativeLayout android:id="@+id/button_bar"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_weight="0"
+        android:background="@android:drawable/bottom_bar"
+        android:visibility="gone">
+
+        <Button android:id="@+id/back_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:layout_alignParentLeft="true"
+            android:drawableLeft="@drawable/ic_btn_back"
+            android:drawablePadding="3dip"
+            android:text="@string/back_button_label"
+        />
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true">
+
+            <Button android:id="@+id/skip_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:text="@string/skip_button_label"
+                android:visibility="gone"
+            />
+
+            <Button android:id="@+id/next_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:drawableRight="@drawable/ic_btn_next"
+                android:drawablePadding="3dip"
+                android:text="@string/next_button_label"
+            />
+        </LinearLayout>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 76a3c34..61167b5 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -408,7 +408,7 @@
     <style name="Widget.TextView.ListSeparator">
         <item name="android:background">@android:drawable/dark_header_dither</item>
         <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">25dip</item>
+        <item name="android:layout_height">wrap_content</item>
         <item name="android:textStyle">bold</item>
         <item name="android:textColor">?textColorSecondary</item>
         <item name="android:textSize">14sp</item>
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index d8d9eba..f019599 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -42,6 +42,10 @@
 
     public void testEnable() {
         int iterations = BluetoothTestRunner.sEnableIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
         for (int i = 0; i < iterations; i++) {
@@ -53,6 +57,10 @@
 
     public void testDiscoverable() {
         int iterations = BluetoothTestRunner.sDiscoverableIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         mTestUtils.enable(adapter);
 
@@ -67,6 +75,10 @@
 
     public void testScan() {
         int iterations = BluetoothTestRunner.sScanIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         mTestUtils.enable(adapter);
 
@@ -78,4 +90,67 @@
 
         mTestUtils.disable(adapter);
     }
+
+    public void testPair() {
+        int iterations = BluetoothTestRunner.sPairIterations;
+        if (iterations == 0) {
+            return;
+        }
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sPairAddress);
+        mTestUtils.enable(adapter);
+
+        for (int i = 0; i < iterations; i++) {
+            mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+                    BluetoothTestRunner.sPairPin);
+            mTestUtils.unpair(adapter, device);
+        }
+        mTestUtils.disable(adapter);
+    }
+
+    public void testConnectA2dp() {
+        int iterations = BluetoothTestRunner.sConnectA2dpIterations;
+        if (iterations == 0) {
+            return;
+        }
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sA2dpAddress);
+        mTestUtils.enable(adapter);
+        mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+                BluetoothTestRunner.sPairPin);
+
+        for (int i = 0; i < iterations; i++) {
+            mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP);
+            mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP);
+        }
+
+        // TODO: Unpair from device if device can accept pairing after unpairing
+        mTestUtils.disable(adapter);
+    }
+
+    public void testConnectHeadset() {
+        int iterations = BluetoothTestRunner.sConnectHeadsetIterations;
+        if (iterations == 0) {
+            return;
+        }
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sHeadsetAddress);
+        mTestUtils.enable(adapter);
+        mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+                BluetoothTestRunner.sPairPin);
+
+        for (int i = 0; i < iterations; i++) {
+            mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
+            mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET);
+            mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET);
+        }
+
+        // TODO: Unpair from device if device can accept pairing after unpairing
+        mTestUtils.disable(adapter);
+    }
 }
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
index cf0ff99..3e589fc 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
@@ -21,11 +21,24 @@
 import android.os.Bundle;
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
+import android.util.Log;
 
 public class BluetoothTestRunner extends InstrumentationTestRunner {
+    private static final String TAG = "BluetoothTestRunner";
+
     public static int sEnableIterations = 100;
     public static int sDiscoverableIterations = 1000;
     public static int sScanIterations = 1000;
+    public static int sPairIterations = 100;
+    public static int sConnectHeadsetIterations = 100;
+    public static int sConnectA2dpIterations = 100;
+
+    public static String sPairAddress = "";
+    public static String sHeadsetAddress = "";
+    public static String sA2dpAddress = "";
+
+    public static byte[] sPairPin = {'1', '2', '3', '4'};
+    public static int sPairPasskey = 123456;
 
     @Override
     public TestSuite getAllTests() {
@@ -41,8 +54,6 @@
 
     @Override
     public void onCreate(Bundle arguments) {
-        super.onCreate(arguments);
-
         String val = arguments.getString("enable_iterations");
         if (val != null) {
             try {
@@ -69,5 +80,76 @@
                 // Invalid argument, fall back to default value
             }
         }
+
+        val = arguments.getString("pair_iterations");
+        if (val != null) {
+            try {
+                sPairIterations = Integer.parseInt(val);
+            } catch (NumberFormatException e) {
+                // Invalid argument, fall back to default value
+            }
+        }
+
+        val = arguments.getString("connect_a2dp_iterations");
+        if (val != null) {
+            try {
+                sConnectA2dpIterations = Integer.parseInt(val);
+            } catch (NumberFormatException e) {
+                // Invalid argument, fall back to default value
+            }
+        }
+
+        val = arguments.getString("connect_headset_iterations");
+        if (val != null) {
+            try {
+                sConnectHeadsetIterations = Integer.parseInt(val);
+            } catch (NumberFormatException e) {
+                // Invalid argument, fall back to default value
+            }
+        }
+
+        val = arguments.getString("pair_address");
+        if (val != null) {
+            sPairAddress = val;
+        }
+
+        val = arguments.getString("headset_address");
+        if (val != null) {
+            sHeadsetAddress = val;
+        }
+
+        val = arguments.getString("a2dp_address");
+        if (val != null) {
+            sA2dpAddress = val;
+        }
+
+        val = arguments.getString("pair_pin");
+        if (val != null) {
+            sPairPin = BluetoothDevice.convertPinToBytes(val);
+        }
+
+        val = arguments.getString("pair_passkey");
+        if (val != null) {
+            try {
+                sPairPasskey = Integer.parseInt(val);
+            } catch (NumberFormatException e) {
+                // Invalid argument, fall back to default value
+            }
+        }
+
+        Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations));
+        Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations));
+        Log.i(TAG, String.format("scan_iterations=%d", sScanIterations));
+        Log.i(TAG, String.format("pair_iterations=%d", sPairIterations));
+        Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations));
+        Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations));
+        Log.i(TAG, String.format("pair_address=%s", sPairAddress));
+        Log.i(TAG, String.format("a2dp_address=%s", sA2dpAddress));
+        Log.i(TAG, String.format("headset_address=%s", sHeadsetAddress));
+        Log.i(TAG, String.format("pair_pin=%s", new String(sPairPin)));
+        Log.i(TAG, String.format("pair_passkey=%d", sPairPasskey));
+
+        // Call onCreate last since we want to set the static variables first.
+        super.onCreate(arguments);
     }
 }
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
index cddf63d..328891c 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -29,6 +29,8 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 public class BluetoothTestUtils extends Assert {
 
@@ -57,73 +59,43 @@
      */
     private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
 
-    private static final int DISCOVERY_STARTED_FLAG = 1;
-    private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
-    private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
-    private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
-    private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
-    private static final int STATE_OFF_FLAG = 1 << 5;
-    private static final int STATE_TURNING_ON_FLAG = 1 << 6;
-    private static final int STATE_ON_FLAG = 1 << 7;
-    private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+    /**
+     * Timeout for {@link BluetoothDevice#createBond()} in ms.
+     */
+    private static final int PAIR_TIMEOUT = 20000;
+
+    /**
+     * Timeout for {@link BluetoothDevice#removeBond()} in ms.
+     */
+    private static final int UNPAIR_TIMEOUT = 20000;
+
+    /**
+     * Timeout for {@link BluetoothProfile#connect(BluetoothDevice)} in ms.
+     */
+    private static final int CONNECT_PROFILE_TIMEOUT = 20000;
+
+    /**
+     * Timeout for {@link BluetoothProfile#disconnect(BluetoothDevice)} in ms.
+     */
+    private static final int DISCONNECT_PROFILE_TIMEOUT = 20000;
+
+    /**
+     * Timeout to connect a profile proxy in ms.
+     */
+    private static final int CONNECT_PROXY_TIMEOUT = 5000;
 
     /**
      * Time between polls in ms.
      */
     private static final int POLL_TIME = 100;
 
-    private Context mContext;
-
-    private BufferedWriter mOutputWriter;
-
-    private String mOutputFile;
-    private String mTag;
-
-    private class BluetoothReceiver extends BroadcastReceiver {
+    private abstract class FlagReceiver extends BroadcastReceiver {
+        private int mExpectedFlags = 0;
         private int mFiredFlags = 0;
+        private long mCompletedTime = -1;
 
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_STARTED_FLAG;
-                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_FINISHED_FLAG;
-                } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
-                    int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(mode, BluetoothAdapter.ERROR);
-                    switch (mode) {
-                        case BluetoothAdapter.SCAN_MODE_NONE:
-                            mFiredFlags |= SCAN_MODE_NONE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-                            break;
-                    }
-                } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(state, BluetoothAdapter.ERROR);
-                    switch (state) {
-                        case BluetoothAdapter.STATE_OFF:
-                            mFiredFlags |= STATE_OFF_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_ON:
-                            mFiredFlags |= STATE_TURNING_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_ON:
-                            mFiredFlags |= STATE_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_OFF:
-                            mFiredFlags |= STATE_TURNING_OFF_FLAG;
-                            break;
-                    }
-                }
-            }
+        public FlagReceiver(int expectedFlags) {
+            mExpectedFlags = expectedFlags;
         }
 
         public int getFiredFlags() {
@@ -132,14 +104,232 @@
             }
         }
 
-        public void resetFiredFlags() {
+        public long getCompletedTime() {
             synchronized (this) {
-                mFiredFlags = 0;
+                return mCompletedTime;
+            }
+        }
+
+        protected void setFiredFlag(int flag) {
+            synchronized (this) {
+                mFiredFlags |= flag;
+                if (mFiredFlags == mExpectedFlags) {
+                    mCompletedTime = System.currentTimeMillis();
+                }
             }
         }
     }
 
-    private BluetoothReceiver mReceiver = new BluetoothReceiver();
+    private class BluetoothReceiver extends FlagReceiver {
+        private static final int DISCOVERY_STARTED_FLAG = 1;
+        private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
+        private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
+        private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
+        private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
+        private static final int STATE_OFF_FLAG = 1 << 5;
+        private static final int STATE_TURNING_ON_FLAG = 1 << 6;
+        private static final int STATE_ON_FLAG = 1 << 7;
+        private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+
+        public BluetoothReceiver(int expectedFlags) {
+            super(expectedFlags);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i("BT", intent.toString());
+            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+                setFiredFlag(DISCOVERY_STARTED_FLAG);
+            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+                setFiredFlag(DISCOVERY_FINISHED_FLAG);
+            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+                int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1);
+                assertNotSame(-1, mode);
+                switch (mode) {
+                    case BluetoothAdapter.SCAN_MODE_NONE:
+                        setFiredFlag(SCAN_MODE_NONE_FLAG);
+                        break;
+                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+                        setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG);
+                        break;
+                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+                        setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG);
+                        break;
+                }
+            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothAdapter.STATE_OFF:
+                        setFiredFlag(STATE_OFF_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_TURNING_ON:
+                        setFiredFlag(STATE_TURNING_ON_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_ON:
+                        setFiredFlag(STATE_ON_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_TURNING_OFF:
+                        setFiredFlag(STATE_TURNING_OFF_FLAG);
+                        break;
+                }
+            }
+        }
+    }
+
+    private class PairReceiver extends FlagReceiver {
+        private static final int STATE_BONDED_FLAG = 1;
+        private static final int STATE_BONDING_FLAG = 1 << 1;
+        private static final int STATE_NONE_FLAG = 1 << 2;
+
+        private BluetoothDevice mDevice;
+        private int mPasskey;
+        private byte[] mPin;
+
+        public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) {
+            super(expectedFlags);
+
+            mDevice = device;
+            mPasskey = passkey;
+            mPin = pin;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+                return;
+            }
+
+            if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
+                int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1);
+                assertNotSame(-1, varient);
+                switch(varient) {
+                    case BluetoothDevice.PAIRING_VARIANT_PIN:
+                        mDevice.setPin(mPin);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                        mDevice.setPasskey(mPasskey);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+                    case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+                        mDevice.setPairingConfirmation(true);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+                        mDevice.setRemoteOutOfBandData();
+                        break;
+                }
+            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothDevice.BOND_NONE:
+                        setFiredFlag(STATE_NONE_FLAG);
+                        break;
+                    case BluetoothDevice.BOND_BONDING:
+                        setFiredFlag(STATE_BONDING_FLAG);
+                        break;
+                    case BluetoothDevice.BOND_BONDED:
+                        setFiredFlag(STATE_BONDED_FLAG);
+                        break;
+                }
+            }
+        }
+    }
+
+    private class ConnectProfileReceiver extends FlagReceiver {
+        private static final int STATE_DISCONNECTED_FLAG = 1;
+        private static final int STATE_CONNECTING_FLAG = 1 << 1;
+        private static final int STATE_CONNECTED_FLAG = 1 << 2;
+        private static final int STATE_DISCONNECTING_FLAG = 1 << 3;
+
+        private BluetoothDevice mDevice;
+        private int mProfile;
+        private String mConnectionAction;
+
+        public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) {
+            super(expectedFlags);
+
+            mDevice = device;
+            mProfile = profile;
+
+            switch(mProfile) {
+                case BluetoothProfile.A2DP:
+                    mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED;
+                    break;
+                case BluetoothProfile.HEADSET:
+                    mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
+                    break;
+                default:
+                    mConnectionAction = null;
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) {
+                if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+                    return;
+                }
+
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        setFiredFlag(STATE_DISCONNECTED_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_CONNECTING:
+                        setFiredFlag(STATE_CONNECTING_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        setFiredFlag(STATE_CONNECTED_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        setFiredFlag(STATE_DISCONNECTING_FLAG);
+                        break;
+                }
+            }
+        }
+    }
+
+    private BluetoothProfile.ServiceListener mServiceListener =
+            new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            synchronized (this) {
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = (BluetoothA2dp) proxy;
+                        break;
+                    case BluetoothProfile.HEADSET:
+                        mHeadset = (BluetoothHeadset) proxy;
+                        break;
+                }
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            synchronized (this) {
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = null;
+                        break;
+                    case BluetoothProfile.HEADSET:
+                        mHeadset = null;
+                        break;
+                }
+            }
+        }
+    };
+
+    private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>();
+
+    private BufferedWriter mOutputWriter;
+    private String mTag;
+    private String mOutputFile;
+
+    private Context mContext;
+    private BluetoothA2dp mA2dp;
+    private BluetoothHeadset mHeadset;
 
     public BluetoothTestUtils(Context context, String tag) {
         this(context, tag, null);
@@ -161,17 +351,12 @@
                 mOutputWriter = null;
             }
         }
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
-        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
-        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
     }
 
     public void close() {
-        mContext.unregisterReceiver(mReceiver);
+        while (!mReceivers.isEmpty()) {
+            mContext.unregisterReceiver(mReceivers.remove(0));
+        }
 
         if (mOutputWriter != null) {
             try {
@@ -183,25 +368,30 @@
     }
 
     public void enable(BluetoothAdapter adapter) {
-        int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG
+                | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG);
+        long start = -1;
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
 
         int state = adapter.getState();
         switch (state) {
             case BluetoothAdapter.STATE_ON:
                 assertTrue(adapter.isEnabled());
+                removeReceiver(receiver);
                 return;
-            case BluetoothAdapter.STATE_OFF:
-            case BluetoothAdapter.STATE_TURNING_OFF:
-                assertFalse(adapter.isEnabled());
-                assertTrue(adapter.enable());
-                break;
             case BluetoothAdapter.STATE_TURNING_ON:
                 assertFalse(adapter.isEnabled());
                 mask = 0; // Don't check for received intents since we might have missed them.
                 break;
+            case BluetoothAdapter.STATE_OFF:
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                assertFalse(adapter.isEnabled());
+                start = System.currentTimeMillis();
+                assertTrue(adapter.enable());
+                break;
             default:
-                fail("enable() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("enable() invalid state: state=%d", state));
         }
 
         long s = System.currentTimeMillis();
@@ -209,10 +399,14 @@
             state = adapter.getState();
             if (state == BluetoothAdapter.STATE_ON) {
                 assertTrue(adapter.isEnabled());
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("enable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("enable() completed in %d ms", (finish - start)));
+                    } else {
+                        writeOutput("enable() completed");
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
@@ -222,27 +416,32 @@
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
                 state, BluetoothAdapter.STATE_ON, firedFlags, mask));
     }
 
     public void disable(BluetoothAdapter adapter) {
-        int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG
+                | BluetoothReceiver.SCAN_MODE_NONE_FLAG);
+        long start = -1;
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
 
         int state = adapter.getState();
         switch (state) {
             case BluetoothAdapter.STATE_OFF:
                 assertFalse(adapter.isEnabled());
+                removeReceiver(receiver);
                 return;
-            case BluetoothAdapter.STATE_ON:
-                assertTrue(adapter.isEnabled());
-                assertTrue(adapter.disable());
-                break;
             case BluetoothAdapter.STATE_TURNING_ON:
                 assertFalse(adapter.isEnabled());
+                start = System.currentTimeMillis();
+                assertTrue(adapter.disable());
+                break;
+            case BluetoothAdapter.STATE_ON:
+                assertTrue(adapter.isEnabled());
+                start = System.currentTimeMillis();
                 assertTrue(adapter.disable());
                 break;
             case BluetoothAdapter.STATE_TURNING_OFF:
@@ -250,7 +449,8 @@
                 mask = 0; // Don't check for received intents since we might have missed them.
                 break;
             default:
-                fail("disable() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("disable() invalid state: state=%d", state));
         }
 
         long s = System.currentTimeMillis();
@@ -258,10 +458,15 @@
             state = adapter.getState();
             if (state == BluetoothAdapter.STATE_OFF) {
                 assertFalse(adapter.isEnabled());
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
-                    writeOutput(String.format("disable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("disable() completed in %d ms",
+                                (finish - start)));
+                    } else {
+                        writeOutput("disable() completed");
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
@@ -271,15 +476,14 @@
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
                 state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
     }
 
     public void discoverable(BluetoothAdapter adapter) {
-        int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
 
         if (!adapter.isEnabled()) {
             fail("discoverable() bluetooth not enabled");
@@ -290,35 +494,36 @@
             return;
         }
 
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
+        long start = System.currentTimeMillis();
         assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+        while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
             scanMode = adapter.getScanMode();
             if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
+                if ((receiver.getFiredFlags() & mask) == mask) {
                     writeOutput(String.format("discoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                            (receiver.getCompletedTime() - start)));
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+                assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
                 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
                 firedFlags, mask));
     }
 
     public void undiscoverable(BluetoothAdapter adapter) {
-        int mask = SCAN_MODE_CONNECTABLE_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
 
         if (!adapter.isEnabled()) {
             fail("undiscoverable() bluetooth not enabled");
@@ -329,35 +534,36 @@
             return;
         }
 
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
+        long start = System.currentTimeMillis();
         assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+        while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
             scanMode = adapter.getScanMode();
             if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-                if ((mReceiver.getFiredFlags() & mask) == mask) {
-                    mReceiver.resetFiredFlags();
+                if ((receiver.getFiredFlags() & mask) == mask) {
                     writeOutput(String.format("undiscoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                            (receiver.getCompletedTime() - start)));
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+                assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
                 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
                 mask));
     }
 
     public void startScan(BluetoothAdapter adapter) {
-        int mask = DISCOVERY_STARTED_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG;
 
         if (!adapter.isEnabled()) {
             fail("startScan() bluetooth not enabled");
@@ -367,28 +573,29 @@
             return;
         }
 
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        long start = System.currentTimeMillis();
         assertTrue(adapter.startDiscovery());
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
-            if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
-                mReceiver.resetFiredFlags();
+        while (System.currentTimeMillis() - start < START_DISCOVERY_TIMEOUT) {
+            if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
                 writeOutput(String.format("startScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
+                        (receiver.getCompletedTime() - start)));
+                removeReceiver(receiver);
                 return;
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
                 adapter.isDiscovering(), firedFlags, mask));
     }
 
     public void stopScan(BluetoothAdapter adapter) {
-        int mask = DISCOVERY_FINISHED_FLAG;
-        mReceiver.resetFiredFlags();
+        int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG;
 
         if (!adapter.isEnabled()) {
             fail("stopScan() bluetooth not enabled");
@@ -398,27 +605,277 @@
             return;
         }
 
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        long start = System.currentTimeMillis();
         // TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
         adapter.cancelDiscovery();
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
-            if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
-                mReceiver.resetFiredFlags();
+        while (System.currentTimeMillis() - start < CANCEL_DISCOVERY_TIMEOUT) {
+            if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
                 writeOutput(String.format("stopScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
+                        (receiver.getCompletedTime() - start)));
+                removeReceiver(receiver);
                 return;
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mReceiver.getFiredFlags();
-        mReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
                 adapter.isDiscovering(), firedFlags, mask));
 
     }
 
+    public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) {
+        int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG;
+        long start = -1;
+
+        if (!adapter.isEnabled()) {
+            fail("pair() bluetooth not enabled");
+        }
+
+        PairReceiver receiver = getPairReceiver(device, passkey, pin, mask);
+
+        int state = device.getBondState();
+        switch (state) {
+            case BluetoothDevice.BOND_NONE:
+                assertFalse(adapter.getBondedDevices().contains(device));
+                start = System.currentTimeMillis();
+                assertTrue(device.createBond());
+                break;
+            case BluetoothDevice.BOND_BONDING:
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
+            case BluetoothDevice.BOND_BONDED:
+                assertTrue(adapter.getBondedDevices().contains(device));
+                return;
+            default:
+                removeReceiver(receiver);
+                fail(String.format("pair() invalid state: device=%s, state=%d", device, state));
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < PAIR_TIMEOUT) {
+            state = device.getBondState();
+            if (state == BluetoothDevice.BOND_BONDED) {
+                assertTrue(adapter.getBondedDevices().contains(device));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("pair() completed in %d ms: device=%s",
+                                (finish - start), device));
+                    } else {
+                        writeOutput(String.format("pair() completed: device=%s", device));
+                    }
+                    removeReceiver(receiver);
+                    return;
+                }
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("pair() timeout: device=%s, state=%d (expected %d), "
+                + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+                firedFlags, mask));
+    }
+
+    public void unpair(BluetoothAdapter adapter, BluetoothDevice device) {
+        int mask = PairReceiver.STATE_NONE_FLAG;
+        long start = -1;
+
+        if (!adapter.isEnabled()) {
+            fail("unpair() bluetooth not enabled");
+        }
+
+        PairReceiver receiver = getPairReceiver(device, 0, null, mask);
+
+        int state = device.getBondState();
+        switch (state) {
+            case BluetoothDevice.BOND_NONE:
+                assertFalse(adapter.getBondedDevices().contains(device));
+                removeReceiver(receiver);
+                return;
+            case BluetoothDevice.BOND_BONDING:
+                start = System.currentTimeMillis();
+                assertTrue(device.removeBond());
+                break;
+            case BluetoothDevice.BOND_BONDED:
+                assertTrue(adapter.getBondedDevices().contains(device));
+                start = System.currentTimeMillis();
+                assertTrue(device.removeBond());
+                break;
+            default:
+                removeReceiver(receiver);
+                fail(String.format("unpair() invalid state: device=%s, state=%d", device, state));
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < UNPAIR_TIMEOUT) {
+            if (device.getBondState() == BluetoothDevice.BOND_NONE) {
+                assertFalse(adapter.getBondedDevices().contains(device));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("unpair() completed in %d ms: device=%s",
+                                (finish - start), device));
+                    } else {
+                        writeOutput(String.format("unpair() completed: device=%s", device));
+                    }
+                    removeReceiver(receiver);
+                    return;
+                }
+            }
+        }
+
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("unpair() timeout: device=%s, state=%d (expected %d), "
+                + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+                firedFlags, mask));
+    }
+
+    public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+        int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG
+                | ConnectProfileReceiver.STATE_CONNECTED_FLAG);
+        long start = -1;
+
+        if (!adapter.isEnabled()) {
+            fail(String.format("connectProfile() bluetooth not enabled: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        if (!adapter.getBondedDevices().contains(device)) {
+            fail(String.format("connectProfile() device not paired: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        BluetoothProfile proxy = connectProxy(adapter, profile);
+        if (proxy == null) {
+            fail(String.format("connectProfile() unknown profile: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+        int state = proxy.getConnectionState(device);
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTED:
+                removeReceiver(receiver);
+                return;
+            case BluetoothProfile.STATE_CONNECTING:
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+            case BluetoothProfile.STATE_DISCONNECTING:
+                start = System.currentTimeMillis();
+                assertTrue(proxy.connect(device));
+                break;
+            default:
+                removeReceiver(receiver);
+                fail(String.format("connectProfile() invalid state: device=%s, profile=%d, "
+                        + "state=%d", device, profile, state));
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < CONNECT_PROFILE_TIMEOUT) {
+            state = proxy.getConnectionState(device);
+            if (state == BluetoothProfile.STATE_CONNECTED) {
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("connectProfile() completed in %d ms: "
+                                +"device=%s, profile=%d", (finish - start), device, profile));
+                    } else {
+                        writeOutput(String.format("connectProfile() completed: device=%s, "
+                                + "profile=%d", device, profile));
+                    }
+                    removeReceiver(receiver);
+                    return;
+                }
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("connectProfile() timeout: device=%s, profile=%s, "
+                + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+                BluetoothProfile.STATE_CONNECTED, firedFlags, mask));
+    }
+
+    public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+        int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG
+                | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG);
+        long start = -1;
+
+        if (!adapter.isEnabled()) {
+            fail(String.format("disconnectProfile() bluetooth not enabled: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        if (!adapter.getBondedDevices().contains(device)) {
+            fail(String.format("disconnectProfile() device not paired: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        BluetoothProfile proxy = connectProxy(adapter, profile);
+        if (proxy == null) {
+            fail(String.format("disconnectProfile() unknown profile: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+        int state = proxy.getConnectionState(device);
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTED:
+            case BluetoothProfile.STATE_CONNECTING:
+                start = System.currentTimeMillis();
+                assertTrue(proxy.disconnect(device));
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+                removeReceiver(receiver);
+                return;
+            case BluetoothProfile.STATE_DISCONNECTING:
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
+            default:
+                removeReceiver(receiver);
+                fail(String.format("disconnectProfile() invalid state: device=%s, profile=%d, "
+                        + "state=%d", device, profile, state));
+        }
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < DISCONNECT_PROFILE_TIMEOUT) {
+            state = proxy.getConnectionState(device);
+            if (state == BluetoothProfile.STATE_DISCONNECTED) {
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("disconnectProfile() completed in %d ms: "
+                                +"device=%s, profile=%d", (finish - start), device, profile));
+                    } else {
+                        writeOutput(String.format("disconnectProfile() completed: device=%s, "
+                                + "profile=%d", device, profile));
+                    }
+                    removeReceiver(receiver);
+                    return;
+                }
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("disconnectProfile() timeout: device=%s, profile=%s, "
+                + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+                BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask));
+    }
+
     public void writeOutput(String s) {
         Log.i(mTag, s);
         if (mOutputWriter == null) {
@@ -432,6 +889,67 @@
         }
     }
 
+    private BluetoothReceiver getBluetoothReceiver(int expectedFlags) {
+        BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
+        return receiver;
+    }
+
+    private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin,
+            int expectedFlags) {
+        PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
+        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
+        return receiver;
+    }
+
+    private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile,
+            int expectedFlags) {
+        ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
+                expectedFlags);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
+        return receiver;
+    }
+
+    private void removeReceiver(BroadcastReceiver receiver) {
+        mContext.unregisterReceiver(receiver);
+        mReceivers.remove(receiver);
+    }
+
+    private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) {
+        adapter.getProfileProxy(mContext, mServiceListener, profile);
+        long s = System.currentTimeMillis();
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                while (mA2dp != null
+                        && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+                    sleep(POLL_TIME);
+                }
+                return mA2dp;
+            case BluetoothProfile.HEADSET:
+                while (mHeadset != null
+                        && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+                    sleep(POLL_TIME);
+                }
+                return mHeadset;
+            default:
+                return null;
+        }
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 5505f14..c957dba 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -243,19 +243,22 @@
     // derive capture scaling factor from peak value in current buffer
     // this gives more interesting captures for display.
     int32_t shift = 32;
-    for (size_t i = 0; i < inBuffer->frameCount; i++) {
+    int len = inBuffer->frameCount * 2;
+    for (size_t i = 0; i < len; i++) {
         int32_t smp = inBuffer->s16[i];
-        if (smp < 0) smp = -smp;
+        if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
         int32_t clz = __builtin_clz(smp);
         if (shift > clz) shift = clz;
     }
-    // never scale by less than 8 to avoid returning unaltered PCM signal.
-    // add one to combine the division by 2 needed after summing left and right channels below
-    if (20 > shift) {
-        shift = (31 - 8 + 1) - shift;
-    } else {
-        shift = (3 + 1);
+    // A maximum amplitude signal will have 17 leading zeros, which we want to
+    // translate to a shift of 8 (for converting 16 bit to 8 bit)
+     shift = 25 - shift;
+    // Never scale by less than 8 to avoid returning unaltered PCM signal.
+    if (shift < 3) {
+        shift = 3;
     }
+    // add one to combine the division by 2 needed after summing left and right channels below
+    shift++;
 
     uint32_t captIdx;
     uint32_t inIdx;
@@ -264,7 +267,7 @@
          inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
          inIdx++, captIdx++) {
         int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
-        smp = (smp + (1 << (shift - 1))) >> shift;
+        smp = smp >> shift;
         buf[captIdx] = ((uint8_t)smp)^0x80;
     }
     pContext->mCaptureIdx = captIdx;
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 27faf4f..092c33e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -281,7 +281,7 @@
     mNumSyncSamples = U32_AT(&header[4]);
 
     if (mNumSyncSamples < 2) {
-        LOGW("Table of sync samples is empty or has only a single entry!");
+        LOGV("Table of sync samples is empty or has only a single entry!");
     }
 
     mSyncSamples = new uint32_t[mNumSyncSamples];
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 3908d71..b4ae593 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -300,6 +300,7 @@
             if (duration <= 0){
                 assertTrue("stressRecordAndPlayback", false);
             }
+            mp.release();
         } catch (Exception e) {
             assertTrue("stressRecordAndPlayback", false);
         }
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 0d85404..8f2f974 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -536,7 +536,14 @@
             Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
         }
         if (mDetached && (mRenderer != null)) {
+            int renderMode = RENDERMODE_CONTINUOUSLY;
+            if (mGLThread != null) {
+                renderMode = mGLThread.getRenderMode();
+            }
             mGLThread = new GLThread(mRenderer);
+            if (renderMode != RENDERMODE_CONTINUOUSLY) {
+                mGLThread.setRenderMode(renderMode);
+            }
             mGLThread.start();
         }
         mDetached = false;
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
new file mode 100644
index 0000000..e74c22f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index dda86d2..65990ad 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -36,6 +36,7 @@
      */
     final Object[] SERVICES = new Object[] {
             R.string.config_statusBarComponent,
+            com.android.systemui.power.PowerUI.class,
         };
 
     /**
@@ -96,9 +97,19 @@
             return;
         }
 
-        for (SystemUI ui: mServices) {
-            pw.println("dumping service: " + ui.getClass().getName());
-            ui.dump(fd, pw, args);
+        if (args == null || args.length == 0) {
+            for (SystemUI ui: mServices) {
+                pw.println("dumping service: " + ui.getClass().getName());
+                ui.dump(fd, pw, args);
+            }
+        } else {
+            String svc = args[0];
+            for (SystemUI ui: mServices) {
+                String name = ui.getClass().getName();
+                if (name.endsWith(svc)) {
+                    ui.dump(fd, pw, args);
+                }
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
new file mode 100644
index 0000000..1a55830
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+public class PowerUI extends SystemUI {
+    static final String TAG = "PowerUI";
+
+    Handler mHandler = new Handler();
+
+    AlertDialog mLowBatteryDialog;
+    int mBatteryLevel;
+    TextView mBatteryLevelTextView;
+
+    public void start() {
+        // Register for Intent broadcasts for...
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_BATTERY_LOW);
+        filter.addAction(Intent.ACTION_BATTERY_OKAY);
+        filter.addAction(Intent.ACTION_POWER_CONNECTED);
+        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+    }
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                mBatteryLevel = intent.getIntExtra("level", -1);
+            } else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+                showLowBatteryWarning();
+            } else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+                    || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+                if (mLowBatteryDialog != null) {
+                    mLowBatteryDialog.dismiss();
+                }
+            } else {
+                Slog.w(TAG, "unknown intent: " + intent);
+            }
+        }
+    };
+
+    void showLowBatteryWarning() {
+        CharSequence levelText = mContext.getString(
+                R.string.battery_low_percent_format, mBatteryLevel);
+
+        if (mBatteryLevelTextView != null) {
+            mBatteryLevelTextView.setText(levelText);
+        } else {
+            View v = View.inflate(mContext, R.layout.battery_low, null);
+            mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
+
+            mBatteryLevelTextView.setText(levelText);
+
+            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+                b.setCancelable(true);
+                b.setTitle(R.string.battery_low_title);
+                b.setView(v);
+                b.setIcon(android.R.drawable.ic_dialog_alert);
+                b.setPositiveButton(android.R.string.ok, null);
+
+                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
+                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+                    b.setNegativeButton(R.string.battery_low_why,
+                            new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            mContext.startActivity(intent);
+                            if (mLowBatteryDialog != null) {
+                                mLowBatteryDialog.dismiss();
+                            }
+                        }
+                    });
+                }
+
+            AlertDialog d = b.create();
+            d.setOnDismissListener(mLowBatteryListener);
+            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            d.show();
+            mLowBatteryDialog = d;
+        }
+
+        final ContentResolver cr = mContext.getContentResolver();
+        if (Settings.System.getInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) {
+            final String soundPath = Settings.System.getString(cr,
+                    Settings.System.LOW_BATTERY_SOUND);
+            if (soundPath != null) {
+                final Uri soundUri = Uri.parse("file://" + soundPath);
+                if (soundUri != null) {
+                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                    if (sfx != null) {
+                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                        sfx.play();
+                    }
+                }
+            }
+        }
+    }
+
+    private DialogInterface.OnDismissListener mLowBatteryListener
+            = new DialogInterface.OnDismissListener() {
+        public void onDismiss(DialogInterface dialog) {
+            mLowBatteryDialog = null;
+            mBatteryLevelTextView = null;
+        }
+    };
+
+    
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (false) {
+            pw.println("args=" + Arrays.toString(args));
+        }
+        if (args == null || args.length == 0) {
+            pw.print("mLowBatteryDialog=");
+            pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
+            pw.print("mBatteryLevel=");
+            pw.println(Integer.toString(mBatteryLevel));
+        }
+
+        // DO NOT SUBMIT with this turned on.
+        if (false) {
+            if (args.length == 3 && "level".equals(args[1])) {
+                try {
+                    final int level = Integer.parseInt(args[2]);
+                    Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+                    intent.putExtra("level", level);
+                    mIntentReceiver.onReceive(mContext, intent);
+                } catch (NumberFormatException ex) {
+                    pw.println(ex);
+                }
+            } else if (args.length == 2 && "low".equals(args[1])) {
+                Intent intent = new Intent(Intent.ACTION_BATTERY_LOW);
+                mIntentReceiver.onReceive(mContext, intent);
+            } else if (args.length == 2 && "ok".equals(args[1])) {
+                Intent intent = new Intent(Intent.ACTION_BATTERY_OKAY);
+                mIntentReceiver.onReceive(mContext, intent);
+            }
+        }
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index fb74e70..a03a0ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -17,14 +17,11 @@
 package com.android.systemui.statusbar.policy;
 
 import android.app.StatusBarManager;
-import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothPbap;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
@@ -33,8 +30,6 @@
 import android.graphics.drawable.Drawable;
 import android.location.LocationManager;
 import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -59,11 +54,8 @@
 import android.util.Slog;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.IccCard;
@@ -95,24 +87,12 @@
 
     private final Context mContext;
     private final StatusBarManager mService;
-    private final Handler mHandler = new StatusBarHandler();
+    private final Handler mHandler = new Handler();
     private final IBatteryStats mBatteryStats;
 
     // storage
     private StorageManager mStorageManager;
 
-    // battery
-    private boolean mBatteryFirst = true;
-    private boolean mBatteryPlugged;
-    private int mBatteryLevel;
-    private AlertDialog mLowBatteryDialog;
-    private TextView mBatteryLevelTextView;
-    private View mBatteryView;
-    private int mBatteryViewSequence;
-    private boolean mBatteryShowLowOnEndCall = false;
-    private static final boolean SHOW_LOW_BATTERY_WARNING = true;
-    private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
     // phone
     private TelephonyManager mPhone;
     private int mPhoneSignalIconId;
@@ -357,13 +337,6 @@
             else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
                 updateSyncState(intent);
             }
-            else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
-                onBatteryLow(intent);
-            }
-            else if (action.equals(Intent.ACTION_BATTERY_OKAY)
-                    || action.equals(Intent.ACTION_POWER_CONNECTED)) {
-                onBatteryOkay(intent);
-            }
             else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
                     action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                 updateBluetooth(intent);
@@ -473,9 +446,6 @@
 
         // Register for Intent broadcasts for...
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Intent.ACTION_BATTERY_LOW);
-        filter.addAction(Intent.ACTION_BATTERY_OKAY);
-        filter.addAction(Intent.ACTION_POWER_CONNECTED);
         filter.addAction(Intent.ACTION_ALARM_CHANGED);
         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -520,179 +490,6 @@
         final int id = intent.getIntExtra("icon-small", 0);
         int level = intent.getIntExtra("level", 0);
         mService.setIcon("battery", id, level);
-
-        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
-        level = intent.getIntExtra("level", -1);
-        if (false) {
-            Slog.d(TAG, "updateBattery level=" + level
-                    + " plugged=" + plugged
-                    + " mBatteryPlugged=" + mBatteryPlugged
-                    + " mBatteryLevel=" + mBatteryLevel
-                    + " mBatteryFirst=" + mBatteryFirst);
-        }
-
-        boolean oldPlugged = mBatteryPlugged;
-
-        mBatteryPlugged = plugged;
-        mBatteryLevel = level;
-
-        if (mBatteryFirst) {
-            mBatteryFirst = false;
-        }
-        /*
-         * No longer showing the battery view because it draws attention away
-         * from the USB storage notification. We could still show it when
-         * connected to a brick, but that could lead to the user into thinking
-         * the device does not charge when plugged into USB (since he/she would
-         * not see the same battery screen on USB as he sees on brick).
-         */
-        if (false) {
-            Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
-        }
-    }
-
-    private void onBatteryLow(Intent intent) {
-        if (SHOW_LOW_BATTERY_WARNING) {
-            if (false) {
-                Slog.d(TAG, "mPhoneState=" + mPhoneState
-                      + " mLowBatteryDialog=" + mLowBatteryDialog
-                      + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
-            }
-
-            if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
-                showLowBatteryWarning();
-            } else {
-                mBatteryShowLowOnEndCall = true;
-            }
-        }
-    }
-
-    private void onBatteryOkay(Intent intent) {
-        if (mLowBatteryDialog != null
-                && SHOW_LOW_BATTERY_WARNING) {
-            mLowBatteryDialog.dismiss();
-            mBatteryShowLowOnEndCall = false;
-        }
-    }
-
-    private void setBatteryLevel(View parent, int id, int height, int background, int level) {
-        ImageView v = (ImageView)parent.findViewById(id);
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
-        lp.weight = height;
-        if (background != 0) {
-            v.setBackgroundResource(background);
-            Drawable bkg = v.getBackground();
-            bkg.setLevel(level);
-        }
-    }
-
-    private void showLowBatteryWarning() {
-        closeLastBatteryView();
-
-        // Show exact battery level.
-        CharSequence levelText = mContext.getString(
-                    R.string.battery_low_percent_format, mBatteryLevel);
-
-        if (mBatteryLevelTextView != null) {
-            mBatteryLevelTextView.setText(levelText);
-        } else {
-            View v = View.inflate(mContext, R.layout.battery_low, null);
-            mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
-
-            mBatteryLevelTextView.setText(levelText);
-
-            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
-                b.setCancelable(true);
-                b.setTitle(R.string.battery_low_title);
-                b.setView(v);
-                b.setIcon(android.R.drawable.ic_dialog_alert);
-                b.setPositiveButton(android.R.string.ok, null);
-
-                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
-                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
-                    b.setNegativeButton(R.string.battery_low_why,
-                            new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            mContext.startActivity(intent);
-                            if (mLowBatteryDialog != null) {
-                                mLowBatteryDialog.dismiss();
-                            }
-                        }
-                    });
-                }
-
-            AlertDialog d = b.create();
-            d.setOnDismissListener(mLowBatteryListener);
-            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-            d.show();
-            mLowBatteryDialog = d;
-        }
-
-        final ContentResolver cr = mContext.getContentResolver();
-        if (Settings.System.getInt(cr,
-                Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
-        {
-            final String soundPath = Settings.System.getString(cr,
-                Settings.System.LOW_BATTERY_SOUND);
-            if (soundPath != null) {
-                final Uri soundUri = Uri.parse("file://" + soundPath);
-                if (soundUri != null) {
-                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
-                    if (sfx != null) {
-                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
-                        sfx.play();
-                    }
-                }
-            }
-        }
-    }
-
-    private final void updateCallState(int state) {
-        mPhoneState = state;
-        if (false) {
-            Slog.d(TAG, "mPhoneState=" + mPhoneState
-                    + " mLowBatteryDialog=" + mLowBatteryDialog
-                    + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
-        }
-        if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
-            if (mBatteryShowLowOnEndCall) {
-                if (!mBatteryPlugged) {
-                    showLowBatteryWarning();
-                }
-                mBatteryShowLowOnEndCall = false;
-            }
-        } else {
-            if (mLowBatteryDialog != null) {
-                mLowBatteryDialog.dismiss();
-                mBatteryShowLowOnEndCall = true;
-            }
-        }
-    }
-
-    private DialogInterface.OnDismissListener mLowBatteryListener
-            = new DialogInterface.OnDismissListener() {
-        public void onDismiss(DialogInterface dialog) {
-            mLowBatteryDialog = null;
-            mBatteryLevelTextView = null;
-        }
-    };
-
-    private void scheduleCloseBatteryView() {
-        Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
-        m.arg1 = (++mBatteryViewSequence);
-        mHandler.sendMessageDelayed(m, 3000);
-    }
-
-    private void closeLastBatteryView() {
-        if (mBatteryView != null) {
-            //mBatteryView.debug();
-            WindowManagerImpl.getDefault().removeView(mBatteryView);
-            mBatteryView = null;
-        }
     }
 
     private void updateConnectivity(Intent intent) {
@@ -753,7 +550,6 @@
 
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
-            updateCallState(state);
             // In cdma, if a voice call is made, RSSI should switch to 1x.
             if (isCdma()) {
                 updateSignalStrength();
@@ -1181,18 +977,4 @@
         }
         mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
     }
-
-
-    private class StatusBarHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case EVENT_BATTERY_CLOSE:
-                if (msg.arg1 == mBatteryViewSequence) {
-                    closeLastBatteryView();
-                }
-                break;
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 32dd0d5..23bd5c8 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -120,10 +120,13 @@
     private long mDischargeStartTime;
     private int mDischargeStartLevel;
 
+    private Led mLed;
+
     private boolean mSentLowBatteryBroadcast = false;
 
-    public BatteryService(Context context) {
+    public BatteryService(Context context, LightsService lights) {
         mContext = context;
+        mLed = new Led(context, lights);
         mBatteryStats = BatteryStatsService.getService();
 
         mLowBatteryWarningLevel = mContext.getResources().getInteger(
@@ -311,9 +314,9 @@
              *   (becomes <= mLowBatteryWarningLevel).
              */
             final boolean sendBatteryLow = !plugged
-                && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
-                && mBatteryLevel <= mLowBatteryWarningLevel
-                && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+                    && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                    && mBatteryLevel <= mLowBatteryWarningLevel
+                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
 
             sendIntent();
 
@@ -341,6 +344,9 @@
                 mContext.sendBroadcast(statusIntent);
             }
 
+            // Update the battery LED
+            mLed.updateLightsLocked();
+
             // This needs to be done after sendIntent() so that we get the lastest battery stats.
             if (logOutlier && dischargeDuration != 0) {
                 logOutlier(dischargeDuration);
@@ -495,4 +501,66 @@
             pw.println("  technology: " + mBatteryTechnology);
         }
     }
+
+    class Led {
+        private LightsService mLightsService;
+        private LightsService.Light mBatteryLight;
+
+        private int mBatteryLowARGB;
+        private int mBatteryMediumARGB;
+        private int mBatteryFullARGB;
+        private int mBatteryLedOn;
+        private int mBatteryLedOff;
+
+        private boolean mBatteryCharging;
+        private boolean mBatteryLow;
+        private boolean mBatteryFull;
+
+        Led(Context context, LightsService lights) {
+            mLightsService = lights;
+            mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
+
+            mBatteryLowARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+            mBatteryMediumARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+            mBatteryFullARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+            mBatteryLedOn = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLedOn);
+            mBatteryLedOff = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLedOff);
+        }
+
+        /**
+         * Synchronize on BatteryService.
+         */
+        void updateLightsLocked() {
+            final int level = mBatteryLevel;
+            final int status = mBatteryStatus;
+            if (level < mLowBatteryWarningLevel) {
+                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+                    // Solid red when battery is charging
+                    mBatteryLight.setColor(mBatteryLowARGB);
+                } else {
+                    // Flash red when battery is low and not charging
+                    mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+                            mBatteryLedOn, mBatteryLedOff);
+                }
+            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
+                    || status == BatteryManager.BATTERY_STATUS_FULL) {
+                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
+                    // Solid green when full or charging and nearly full
+                    mBatteryLight.setColor(mBatteryFullARGB);
+                } else {
+                    // Solid orange when charging and halfway full
+                    mBatteryLight.setColor(mBatteryMediumARGB);
+                }
+            } else {
+                // No lights if not charging and not low
+                mBatteryLight.turnOff();
+            }
+        }
+    }
 }
+
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 8066fa7..1081a20 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -93,7 +93,6 @@
     private WorkerHandler mHandler;
     private StatusBarManagerService mStatusBar;
     private LightsService mLightsService;
-    private LightsService.Light mBatteryLight;
     private LightsService.Light mNotificationLight;
     private LightsService.Light mAttentionLight;
 
@@ -128,18 +127,8 @@
     private ArrayList<ToastRecord> mToastQueue;
 
     private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
-
-    private boolean mBatteryCharging;
-    private boolean mBatteryLow;
-    private boolean mBatteryFull;
     private NotificationRecord mLedNotification;
 
-    private static int mBatteryLowARGB;
-    private static int mBatteryMediumARGB;
-    private static int mBatteryFullARGB;
-    private static int mBatteryLedOn;
-    private static int mBatteryLedOff;
-
     private static String idDebugString(Context baseContext, String packageName, int id) {
         Context c = null;
 
@@ -342,22 +331,7 @@
 
             boolean queryRestart = false;
             
-            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
-                int level = intent.getIntExtra("level", -1);
-                boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
-                int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
-                boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
-
-                if (batteryCharging != mBatteryCharging ||
-                        batteryLow != mBatteryLow ||
-                        batteryFull != mBatteryFull) {
-                    mBatteryCharging = batteryCharging;
-                    mBatteryLow = batteryLow;
-                    mBatteryFull = batteryFull;
-                    updateLights();
-                }
-            } else if (action.equals(Usb.ACTION_USB_STATE)) {
+            if (action.equals(Usb.ACTION_USB_STATE)) {
                 Bundle extras = intent.getExtras();
                 boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
                 boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
@@ -435,7 +409,6 @@
     {
         super();
         mContext = context;
-        mLightsService = lights;
         mAm = ActivityManagerNative.getDefault();
         mSound = new NotificationPlayer(TAG);
         mSound.setUsesWakeLock(context);
@@ -445,7 +418,6 @@
         mStatusBar = statusBar;
         statusBar.setNotificationCallbacks(mNotificationCallbacks);
 
-        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
         mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
 
@@ -457,17 +429,6 @@
         mDefaultNotificationLedOff = resources.getInteger(
                 com.android.internal.R.integer.config_defaultNotificationLedOff);
 
-        mBatteryLowARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLowARGB);
-        mBatteryMediumARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
-        mBatteryFullARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryFullARGB);
-        mBatteryLedOn = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLedOn);
-        mBatteryLedOff = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLedOff);
-
         // Don't start allowing notifications until the setup wizard has run once.
         // After that, including subsequent boots, init with notifications turned on.
         // This works on the first boot because the setup wizard will toggle this
@@ -479,7 +440,6 @@
 
         // register for battery changed notifications
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(Usb.ACTION_USB_STATE);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -1076,25 +1036,6 @@
     // lock on mNotificationList
     private void updateLightsLocked()
     {
-        // Battery low always shows, other states only show if charging.
-        if (mBatteryLow) {
-            if (mBatteryCharging) {
-                mBatteryLight.setColor(mBatteryLowARGB);
-            } else {
-                // Flash when battery is low and not charging
-                mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
-                        mBatteryLedOn, mBatteryLedOff);
-            }
-        } else if (mBatteryCharging) {
-            if (mBatteryFull) {
-                mBatteryLight.setColor(mBatteryFullARGB);
-            } else {
-                mBatteryLight.setColor(mBatteryMediumARGB);
-            }
-        } else {
-            mBatteryLight.turnOff();
-        }
-
         // clear pending pulse notification if screen is on
         if (mScreenOn || mLedNotification == null) {
             mPendingPulseNotification = false;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 46797c5..237ab80 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -170,13 +170,13 @@
             Slog.i(TAG, "System Content Providers");
             ActivityManagerService.installSystemProviders();
 
-            Slog.i(TAG, "Battery Service");
-            battery = new BatteryService(context);
-            ServiceManager.addService("battery", battery);
-
             Slog.i(TAG, "Lights Service");
             lights = new LightsService(context);
 
+            Slog.i(TAG, "Battery Service");
+            battery = new BatteryService(context, lights);
+            ServiceManager.addService("battery", battery);
+
             Slog.i(TAG, "Vibrator Service");
             ServiceManager.addService("vibrator", new VibratorService(context));
 
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 70140d8..d3bcb6c 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -6,7 +6,7 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
-	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
 	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 1e1aba9..0dccc0d6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -18,13 +18,6 @@
 
 import com.android.layoutlib.api.ILayoutLog;
 
-import android.graphics.DrawFilter;
-import android.graphics.Picture;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Xfermode;
 import android.graphics.Paint.Align;
 import android.graphics.Paint.FontInfo;
 import android.graphics.Paint.Style;
@@ -42,8 +35,6 @@
 import java.util.List;
 import java.util.Stack;
 
-import javax.microedition.khronos.opengles.GL;
-
 /**
  * Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
  */
@@ -509,7 +500,7 @@
             // get the Graphics2D current matrix
             AffineTransform currentTx = g.getTransform();
             // get the AffineTransform from the matrix
-            AffineTransform matrixTx = matrix.getTransform();
+            AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
 
             // combine them so that the matrix is applied after.
             currentTx.preConcatenate(matrixTx);
@@ -969,9 +960,9 @@
         Graphics2D g = getGraphics2d();
 
         // and apply the matrix
-        g.setTransform(matrix.getTransform());
+        g.setTransform(Matrix_Delegate.getAffineTransform(matrix));
 
-        if (mLogger != null && matrix.hasPerspective()) {
+        if (mLogger != null && Matrix_Delegate.hasPerspective(matrix)) {
             mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
         }
     }
@@ -987,7 +978,7 @@
         // get its current matrix
         AffineTransform currentTx = g.getTransform();
         // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = matrix.getTransform();
+        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
 
         // combine them so that the given matrix is applied after.
         currentTx.preConcatenate(matrixTx);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
deleted file mode 100644
index 9e30671..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-
-
-/**
- * A matrix implementation overridden by the LayoutLib bridge.
- */
-public class Matrix extends _Original_Matrix {
-
-    float mValues[] = new float[9];
-
-    /**
-     * Create an identity matrix
-     */
-    public Matrix() {
-        reset();
-    }
-
-    /**
-     * Create a matrix that is a (deep) copy of src
-     * @param src The matrix to copy into this matrix
-     */
-    public Matrix(Matrix src) {
-        set(src);
-    }
-
-    /**
-     * Creates a Matrix object from the float array. The array becomes the internal storage
-     * of the object.
-     * @param data
-     */
-    private Matrix(float[] data) {
-        assert data.length != 9;
-        mValues = data;
-    }
-
-    //---------- Custom Methods
-
-    /**
-     * Adds the given transformation to the current Matrix
-     * <p/>This in effect does this = this*matrix
-     * @param matrix
-     */
-    private void addTransform(float[] matrix) {
-        float[] tmp = new float[9];
-
-        // first row
-        tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
-        tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
-        tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
-
-        // 2nd row
-        tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
-        tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
-        tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
-
-        // 3rd row
-        tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
-        tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
-        tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
-
-        // copy the result over to mValues
-        mValues = tmp;
-    }
-
-    public AffineTransform getTransform() {
-        // the AffineTransform constructor takes the value in a different order
-        // for a matrix [ 0 1 2 ]
-        //              [ 3 4 5 ]
-        // the order is 0, 3, 1, 4, 2, 5...
-        return new AffineTransform(mValues[0], mValues[3], mValues[1],
-                mValues[4], mValues[2], mValues[5]);
-    }
-
-    public boolean hasPerspective() {
-        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
-    }
-
-    //----------
-
-    /**
-     * Returns true if the matrix is identity.
-     * This maybe faster than testing if (getType() == 0)
-     */
-    @Override
-    public boolean isIdentity() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                if (mValues[k] != ((i==j) ? 1 : 0)) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns true if will map a rectangle to another rectangle. This can be
-     * true if the matrix is identity, scale-only, or rotates a multiple of 90
-     * degrees.
-     */
-    @Override
-    public boolean rectStaysRect() {
-        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
-    }
-
-    /**
-     * (deep) copy the src matrix into this matrix. If src is null, reset this
-     * matrix to the identity matrix.
-     */
-    public void set(Matrix src) {
-        if (src == null) {
-            reset();
-        } else {
-            System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
-        }
-    }
-
-    @Override
-    public void set(_Original_Matrix src) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Returns true if obj is a Matrix and its values equal our values.
-    */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj != null && obj instanceof Matrix) {
-            Matrix matrix = (Matrix)obj;
-            for (int i = 0 ; i < 9 ; i++) {
-                if (mValues[i] != matrix.mValues[i]) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /** Set the matrix to identity */
-    @Override
-    public void reset() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                mValues[k] = ((i==j) ? 1 : 0);
-            }
-        }
-    }
-
-    /** Set the matrix to translate by (dx, dy). */
-    @Override
-    public void setTranslate(float dx, float dy) {
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = dx;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = dy;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to scale by sx and sy. */
-    @Override
-    public void setScale(float sx, float sy) {
-        mValues[0] = sx;
-        mValues[1] = 0;
-        mValues[2] = 0;
-        mValues[3] = 0;
-        mValues[4] = sy;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified number of degrees, with a pivot
-     * point at (px, py). The pivot point is the coordinate that should remain
-     * unchanged by the specified transformation.
-     */
-    @Override
-    public void setRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /**
-     * Set the matrix to rotate about (0,0) by the specified number of degrees.
-     */
-    @Override
-    public void setRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-
-        mValues[0] = cos;
-        mValues[1] = -sin;
-        mValues[2] = 0;
-        mValues[3] = sin;
-        mValues[4] = cos;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified sine and cosine values, with a
-     * pivot point at (px, py). The pivot point is the coordinate that should
-     * remain unchanged by the specified transformation.
-     */
-    @Override
-    public void setSinCos(float sinValue, float cosValue, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to rotate by the specified sine and cosine values. */
-    @Override
-    public void setSinCos(float sinValue, float cosValue) {
-        mValues[0] = cosValue;
-        mValues[1] = -sinValue;
-        mValues[2] = 0;
-        mValues[3] = sinValue;
-        mValues[4] = cosValue;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to skew by sx and sy. */
-    @Override
-    public void setSkew(float kx, float ky) {
-        mValues[0] = 1;
-        mValues[1] = kx;
-        mValues[2] = -0;
-        mValues[3] = ky;
-        mValues[4] = 1;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to the concatenation of the two specified matrices,
-     * returning true if the the result can be represented. Either of the two
-     * matrices may also be the target matrix. this = a * b
-     */
-    public boolean setConcat(Matrix a, Matrix b) {
-        if (a == this) {
-            preConcat(b);
-        } else if (b == this) {
-            postConcat(b);
-        } else {
-            Matrix tmp = new Matrix(b);
-            tmp.addTransform(a.mValues);
-            set(tmp);
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Preconcats the matrix with the specified translation.
-     * M' = M * T(dx, dy)
-     */
-    @Override
-    public boolean preTranslate(float dx, float dy) {
-        // create a matrix that will be multiply by this
-        Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        m.addTransform(this.mValues);
-
-        System.arraycopy(m.mValues, 0, mValues, 0, 9);
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy, px, py)
-     */
-    @Override
-    public boolean preScale(float sx, float sy, float px, float py) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy)
-     */
-    @Override
-    public boolean preScale(float sx, float sy) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees, px, py)
-     */
-    @Override
-    public boolean preRotate(float degrees, float px, float py) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees)
-     */
-    @Override
-    public boolean preRotate(float degrees) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky, px, py)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky, float px, float py) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified matrix.
-     * M' = M * other
-     */
-    public boolean preConcat(Matrix other) {
-        Matrix m = new Matrix(other);
-        other.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    @Override
-    public boolean preConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Postconcats the matrix with the specified translation.
-     * M' = T(dx, dy) * M
-     */
-    @Override
-    public boolean postTranslate(float dx, float dy) {
-        addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy, px, py) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy) {
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees, px, py) * M
-     */
-    @Override
-    public boolean postRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees) * M
-     */
-    @Override
-    public boolean postRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky, px, py) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky) {
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified matrix.
-     * M' = other * M
-     */
-    public boolean postConcat(Matrix other) {
-        addTransform(other.mValues);
-
-        return true;
-    }
-
-    @Override
-    public boolean postConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Controlls how the src rect should align into the dst rect for
-        setRectToRect().
-    */
-    public enum ScaleToFit {
-        /**
-         * Scale in X and Y independently, so that src matches dst exactly.
-         * This may change the aspect ratio of the src.
-         */
-        FILL    (0),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. START aligns the result to the
-         * left and top edges of dst.
-         */
-        START   (1),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. The result is centered inside dst.
-         */
-        CENTER  (2),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. END aligns the result to the
-         * right and bottom edges of dst.
-         */
-        END     (3);
-
-        // the native values must match those in SkMatrix.h
-        ScaleToFit(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * Set the matrix to the scale and translate values that map the source
-     * rectangle to the destination rectangle, returning true if the result
-     * can be represented.
-     *
-     * @param src the source rectangle to map from.
-     * @param dst the destination rectangle to map to.
-     * @param stf the ScaleToFit option
-     * @return true if the matrix can be represented by the rectangle mapping.
-     */
-    public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        if (src.isEmpty()) {
-            reset();
-            return false;
-        }
-
-        if (dst.isEmpty()) {
-            mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
-               = mValues[6] = mValues[7] = 0;
-            mValues[8] = 1;
-        } else {
-            float    tx, sx = dst.width() / src.width();
-            float    ty, sy = dst.height() / src.height();
-            boolean  xLarger = false;
-
-            if (stf != ScaleToFit.FILL) {
-                if (sx > sy) {
-                    xLarger = true;
-                    sx = sy;
-                } else {
-                    sy = sx;
-                }
-            }
-
-            tx = dst.left - src.left * sx;
-            ty = dst.top - src.top * sy;
-            if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
-                float diff;
-
-                if (xLarger) {
-                    diff = dst.width() - src.width() * sy;
-                } else {
-                    diff = dst.height() - src.height() * sy;
-                }
-
-                if (stf == ScaleToFit.CENTER) {
-                    diff = diff / 2;
-                }
-
-                if (xLarger) {
-                    tx += diff;
-                } else {
-                    ty += diff;
-                }
-            }
-
-            mValues[0] = sx;
-            mValues[4] = sy;
-            mValues[2] = tx;
-            mValues[5] = ty;
-            mValues[1]  = mValues[3] = mValues[6] = mValues[7] = 0;
-
-        }
-        // shared cleanup
-        mValues[8] = 1;
-        return true;
-    }
-
-    @Override
-    public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Set the matrix such that the specified src points would map to the
-     * specified dst points. The "points" are represented as an array of floats,
-     * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
-     *
-     * @param src   The array of src [x,y] pairs (points)
-     * @param srcIndex Index of the first pair of src values
-     * @param dst   The array of dst [x,y] pairs (points)
-     * @param dstIndex Index of the first pair of dst values
-     * @param pointCount The number of pairs/points to be used. Must be [0..4]
-     * @return true if the matrix was set to the specified transformation
-     */
-    @Override
-    public boolean setPolyToPoly(float[] src, int srcIndex,
-                                 float[] dst, int dstIndex,
-                                 int pointCount) {
-        if (pointCount > 4) {
-            throw new IllegalArgumentException();
-        }
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * If this matrix can be inverted, return true and if inverse is not null,
-     * set inverse to be the inverse of this matrix. If this matrix cannot be
-     * inverted, ignore inverse and return false.
-     */
-    public boolean invert(Matrix inverse) {
-        if (inverse == null) {
-            return false;
-        }
-
-        try {
-            AffineTransform affineTransform = getTransform();
-            AffineTransform inverseTransform = affineTransform.createInverse();
-            inverse.mValues[0] = (float)inverseTransform.getScaleX();
-            inverse.mValues[1] = (float)inverseTransform.getShearX();
-            inverse.mValues[2] = (float)inverseTransform.getTranslateX();
-            inverse.mValues[3] = (float)inverseTransform.getScaleX();
-            inverse.mValues[4] = (float)inverseTransform.getShearY();
-            inverse.mValues[5] = (float)inverseTransform.getTranslateY();
-
-            return true;
-        } catch (NoninvertibleTransformException e) {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean invert(_Original_Matrix inverse) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-    * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src points (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param pointCount The number of points (x,y pairs) to transform
-     */
-    @Override
-    public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int pointCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-
-        for (int i = 0 ; i < pointCount ; i++) {
-            // just in case we are doing in place, we better put this in temp vars
-            float x = mValues[0] * src[i + srcIndex] +
-                      mValues[1] * src[i + srcIndex + 1] +
-                      mValues[2];
-            float y = mValues[3] * src[i + srcIndex] +
-                      mValues[4] * src[i + srcIndex + 1] +
-                      mValues[5];
-
-            dst[i + dstIndex]     = x;
-            dst[i + dstIndex + 1] = y;
-        }
-    }
-
-    /**
-    * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src vectors (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param vectorCount The number of vectors (x,y pairs) to transform
-     */
-    @Override
-    public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int vectorCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param src   The array of src points (x,y pairs)
-     */
-    @Override
-    public void mapPoints(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapPoints(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param src   The array of src vectors (x,y pairs)
-     */
-    @Override
-    public void mapVectors(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapVectors(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points, and write the transformed
-     * points back into the array
-     *
-     * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
-     */
-    @Override
-    public void mapPoints(float[] pts) {
-        mapPoints(pts, 0, pts, 0, pts.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors, and write the transformed
-     * vectors back into the array.
-     * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
-     */
-    @Override
-    public void mapVectors(float[] vecs) {
-        mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the src rectangle, and write the transformed
-     * rectangle into dst. This is accomplished by transforming the 4 corners of
-     * src, and then setting dst to the bounds of those points.
-     *
-     * @param dst Where the transformed rectangle is written.
-     * @param src The original rectangle to be transformed.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF dst, RectF src) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        // array with 4 corners
-        float[] corners = new float[] {
-                src.left, src.top,
-                src.right, src.top,
-                src.right, src.bottom,
-                src.left, src.bottom,
-        };
-
-        // apply the transform to them.
-        mapPoints(corners);
-
-        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
-        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
-        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
-        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
-        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-        return rectStaysRect();
-    }
-
-    /**
-     * Apply this matrix to the rectangle, and write the transformed rectangle
-     * back into it. This is accomplished by transforming the 4 corners of rect,
-     * and then setting it to the bounds of those points
-     *
-     * @param rect The rectangle to transform.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF rect) {
-        return mapRect(rect, rect);
-    }
-
-    /**
-     * Return the mean radius of a circle after it has been mapped by
-     * this matrix. NOTE: in perspective this value assumes the circle
-     * has its center at the origin.
-     */
-    @Override
-    public float mapRadius(float radius) {
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /** Copy 9 values from the matrix into the array.
-    */
-    @Override
-    public void getValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(mValues, 0, values, 0, mValues.length);
-    }
-
-    /** Copy 9 values from the array into the matrix.
-        Depending on the implementation of Matrix, these may be
-        transformed into 16.16 integers in the Matrix, such that
-        a subsequent call to getValues() will not yield exactly
-        the same values.
-    */
-    @Override
-    public void setValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(values, 0, mValues, 0, mValues.length);
-    }
-
-    @SuppressWarnings("unused")
-    private final static int kIdentity_Mask      = 0;
-    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
-    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
-    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
-    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
-    private final static int kRectStaysRect_Mask = 0x10;
-    @SuppressWarnings("unused")
-    private final static int kUnknown_Mask       = 0x80;
-
-    @SuppressWarnings("unused")
-    private final static int kAllMasks           = kTranslate_Mask |
-                                                     kScale_Mask |
-                                                     kAffine_Mask |
-                                                     kPerspective_Mask |
-                                                     kRectStaysRect_Mask;
-
-    // these guys align with the masks, so we can compute a mask from a variable 0/1
-    @SuppressWarnings("unused")
-    private final static int kTranslate_Shift = 0;
-    @SuppressWarnings("unused")
-    private final static int kScale_Shift = 1;
-    @SuppressWarnings("unused")
-    private final static int kAffine_Shift = 2;
-    @SuppressWarnings("unused")
-    private final static int kPerspective_Shift = 3;
-    private final static int kRectStaysRect_Shift = 4;
-
-    private int computeTypeMask() {
-        int mask = 0;
-
-        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
-            mask |= kPerspective_Mask;
-        }
-
-        if (mValues[2] != 0. || mValues[5] != 0.) {
-            mask |= kTranslate_Mask;
-        }
-
-        float m00 = mValues[0];
-        float m01 = mValues[1];
-        float m10 = mValues[3];
-        float m11 = mValues[4];
-
-        if (m01 != 0. || m10 != 0.) {
-            mask |= kAffine_Mask;
-        }
-
-        if (m00 != 1. || m11 != 1.) {
-            mask |= kScale_Mask;
-        }
-
-        if ((mask & kPerspective_Mask) == 0) {
-            // map non-zero to 1
-            int im00 = m00 != 0 ? 1 : 0;
-            int im01 = m01 != 0 ? 1 : 0;
-            int im10 = m10 != 0 ? 1 : 0;
-            int im11 = m11 != 0 ? 1 : 0;
-
-            // record if the (p)rimary and (s)econdary diagonals are all 0 or
-            // all non-zero (answer is 0 or 1)
-            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
-            int dp1 = im00 & im11;        // true if both are 1
-            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
-            int ds1 = im01 & im10;        // true if both are 1
-
-            // return 1 if primary is 1 and secondary is 0 or
-            // primary is 0 and secondary is 1
-            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
-        }
-
-        return mask;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
new file mode 100644
index 0000000..ed2eff2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -0,0 +1,1011 @@
+/*
+ * 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.graphics;
+
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Matrix.ScaleToFit;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Matrix
+ *
+ * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Matrix class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Matrix_Delegate {
+
+    private final static int MATRIX_SIZE = 9;
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Matrix_Delegate> sManager =
+            new DelegateManager<Matrix_Delegate>();
+
+    // ---- delegate data ----
+    private float mValues[] = new float[MATRIX_SIZE];
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns an {@link AffineTransform} matching the given Matrix.
+     */
+    public static AffineTransform getAffineTransform(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            assert false;
+            return null;
+        }
+
+        return getAffineTransform(delegate);
+    }
+
+    public static boolean hasPerspective(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            assert false;
+            return false;
+        }
+
+        return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1);
+    }
+
+
+    // ---- native methods ----
+
+    public static int native_create(int native_src_or_zero) {
+        // create the delegate
+        Matrix_Delegate newDelegate = new Matrix_Delegate();
+
+        // copy from values if needed.
+        if (native_src_or_zero > 0) {
+            Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
+            if (oldDelegate != null) {
+                System.arraycopy(
+                        oldDelegate.mValues, 0,
+                        newDelegate.mValues, 0,
+                        MATRIX_SIZE);
+            }
+        }
+
+        return sManager.addDelegate(newDelegate);
+    }
+
+    public static boolean native_isIdentity(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                if (d.mValues[k] != ((i==j) ? 1 : 0)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static boolean native_rectStaysRect(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return true;
+        }
+
+        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+    public static void native_reset(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        reset(d.mValues);
+    }
+
+    public static void native_set(int native_object, int other) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        Matrix_Delegate src = sManager.getDelegate(other);
+        if (src == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    public static void native_setTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setTranslate(d.mValues, dx, dy);
+    }
+
+    public static void native_setScale(int native_object, float sx, float sy, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getScale(sx, sy, px, py);
+    }
+
+    public static void native_setScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues[0] = sx;
+        d.mValues[1] = 0;
+        d.mValues[2] = 0;
+        d.mValues[3] = 0;
+        d.mValues[4] = sy;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    public static void native_setRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getRotate(degrees, px, py);
+    }
+
+    public static void native_setRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setRotate(d.mValues, degrees);
+    }
+
+    public static void native_setSinCos(int native_object, float sinValue, float cosValue,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(d.mValues, -px, -py);
+
+        // scale
+        d.postTransform(getRotate(sinValue, cosValue));
+        // translate back the pivot
+        d.postTransform(getTranslate(px, py));
+    }
+
+    public static void native_setSinCos(int native_object, float sinValue, float cosValue) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setRotate(d.mValues, sinValue, cosValue);
+    }
+
+    public static void native_setSkew(int native_object, float kx, float ky, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getSkew(kx, ky, px, py);
+    }
+
+    public static void native_setSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues[0] = 1;
+        d.mValues[1] = kx;
+        d.mValues[2] = -0;
+        d.mValues[3] = ky;
+        d.mValues[4] = 1;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    public static boolean native_setConcat(int native_object, int a, int b) {
+        if (a == native_object) {
+            return native_preConcat(native_object, b);
+        } else if (b == native_object) {
+            return native_postConcat(native_object, a);
+        }
+
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate a_mtx = sManager.getDelegate(a);
+        if (a_mtx == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate b_mtx = sManager.getDelegate(b);
+        if (b_mtx == null) {
+            assert false;
+            return false;
+        }
+
+        multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
+
+        return true;
+    }
+
+    public static boolean native_preTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    public static boolean native_preScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    public static boolean native_preScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy));
+        return true;
+    }
+
+    public static boolean native_preRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    public static boolean native_preRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        d.preTransform(getRotate(sin, cos));
+        return true;
+    }
+
+    public static boolean native_preSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    public static boolean native_preSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    public static boolean native_preConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(other.mValues);
+        return true;
+    }
+
+    public static boolean native_postTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    public static boolean native_postScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    public static boolean native_postScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy));
+        return true;
+    }
+
+    public static boolean native_postRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    public static boolean native_postRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getRotate(degrees));
+        return true;
+    }
+
+    public static boolean native_postSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    public static boolean native_postSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    public static boolean native_postConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(other.mValues);
+        return true;
+    }
+
+    public static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        if (src.isEmpty()) {
+            reset(d.mValues);
+            return false;
+        }
+
+        if (dst.isEmpty()) {
+            d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
+               = d.mValues[6] = d.mValues[7] = 0;
+            d.mValues[8] = 1;
+        } else {
+            float    tx, sx = dst.width() / src.width();
+            float    ty, sy = dst.height() / src.height();
+            boolean  xLarger = false;
+
+            if (stf != ScaleToFit.FILL.nativeInt) {
+                if (sx > sy) {
+                    xLarger = true;
+                    sx = sy;
+                } else {
+                    sy = sx;
+                }
+            }
+
+            tx = dst.left - src.left * sx;
+            ty = dst.top - src.top * sy;
+            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
+                float diff;
+
+                if (xLarger) {
+                    diff = dst.width() - src.width() * sy;
+                } else {
+                    diff = dst.height() - src.height() * sy;
+                }
+
+                if (stf == ScaleToFit.CENTER.nativeInt) {
+                    diff = diff / 2;
+                }
+
+                if (xLarger) {
+                    tx += diff;
+                } else {
+                    ty += diff;
+                }
+            }
+
+            d.mValues[0] = sx;
+            d.mValues[4] = sy;
+            d.mValues[2] = tx;
+            d.mValues[5] = ty;
+            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
+
+        }
+        // shared cleanup
+        d.mValues[8] = 1;
+        return true;
+    }
+
+    public static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
+            float[] dst, int dstIndex, int pointCount) {
+        // FIXME
+        throw new UnsupportedOperationException("NATIVE DELEGATE NEEDED");
+    }
+
+    public static boolean native_invert(int native_object, int inverse) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
+        if (inv_mtx == null) {
+            assert false;
+            return false;
+        }
+
+
+        try {
+            AffineTransform affineTransform = getAffineTransform(d);
+            AffineTransform inverseTransform = affineTransform.createInverse();
+            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
+            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
+            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
+            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+
+            return true;
+        } catch (NoninvertibleTransformException e) {
+            return false;
+        }
+    }
+
+    public static void native_mapPoints(int native_object, float[] dst, int dstIndex,
+            float[] src, int srcIndex, int ptCount, boolean isPts) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        if (isPts) {
+            d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+        } else {
+            // src is vectors
+            // FIXME
+            throw new UnsupportedOperationException("NATIVE DELEGATE NEEDED");
+        }
+    }
+
+    public static boolean native_mapRect(int native_object, RectF dst, RectF src) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        // array with 4 corners
+        float[] corners = new float[] {
+                src.left, src.top,
+                src.right, src.top,
+                src.right, src.bottom,
+                src.left, src.bottom,
+        };
+
+        // apply the transform to them.
+        d.mapPoints(corners);
+
+        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+
+
+        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+    public static float native_mapRadius(int native_object, float radius) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    public static void native_getValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    public static void native_setValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    public static boolean native_equals(int native_a, int native_b) {
+        Matrix_Delegate a = sManager.getDelegate(native_a);
+        if (a == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate b = sManager.getDelegate(native_b);
+        if (b == null) {
+            assert false;
+            return false;
+        }
+
+        for (int i = 0 ; i < MATRIX_SIZE ; i++) {
+            if (a.mValues[i] != b.mValues[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static void finalizer(int native_instance) {
+        sManager.removeDelegate(native_instance);
+    }
+
+    // ---- Private helper methods ----
+
+    private static AffineTransform getAffineTransform(Matrix_Delegate d) {
+        // the AffineTransform constructor takes the value in a different order
+        // for a matrix [ 0 1 2 ]
+        //              [ 3 4 5 ]
+        // the order is 0, 3, 1, 4, 2, 5...
+        return new AffineTransform(
+                d.mValues[0], d.mValues[3], d.mValues[1],
+                d.mValues[4], d.mValues[2], d.mValues[5]);
+    }
+
+
+    /**
+     * Reset a matrix to the identity
+     */
+    private static void reset(float[] mtx) {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                mtx[k] = ((i==j) ? 1 : 0);
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private final static int kIdentity_Mask      = 0;
+    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
+    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
+    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
+    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
+    private final static int kRectStaysRect_Mask = 0x10;
+    @SuppressWarnings("unused")
+    private final static int kUnknown_Mask       = 0x80;
+
+    @SuppressWarnings("unused")
+    private final static int kAllMasks           = kTranslate_Mask |
+                                                   kScale_Mask |
+                                                   kAffine_Mask |
+                                                   kPerspective_Mask |
+                                                   kRectStaysRect_Mask;
+
+    // these guys align with the masks, so we can compute a mask from a variable 0/1
+    @SuppressWarnings("unused")
+    private final static int kTranslate_Shift = 0;
+    @SuppressWarnings("unused")
+    private final static int kScale_Shift = 1;
+    @SuppressWarnings("unused")
+    private final static int kAffine_Shift = 2;
+    @SuppressWarnings("unused")
+    private final static int kPerspective_Shift = 3;
+    private final static int kRectStaysRect_Shift = 4;
+
+    private int computeTypeMask() {
+        int mask = 0;
+
+        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
+            mask |= kPerspective_Mask;
+        }
+
+        if (mValues[2] != 0. || mValues[5] != 0.) {
+            mask |= kTranslate_Mask;
+        }
+
+        float m00 = mValues[0];
+        float m01 = mValues[1];
+        float m10 = mValues[3];
+        float m11 = mValues[4];
+
+        if (m01 != 0. || m10 != 0.) {
+            mask |= kAffine_Mask;
+        }
+
+        if (m00 != 1. || m11 != 1.) {
+            mask |= kScale_Mask;
+        }
+
+        if ((mask & kPerspective_Mask) == 0) {
+            // map non-zero to 1
+            int im00 = m00 != 0 ? 1 : 0;
+            int im01 = m01 != 0 ? 1 : 0;
+            int im10 = m10 != 0 ? 1 : 0;
+            int im11 = m11 != 0 ? 1 : 0;
+
+            // record if the (p)rimary and (s)econdary diagonals are all 0 or
+            // all non-zero (answer is 0 or 1)
+            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
+            int dp1 = im00 & im11;        // true if both are 1
+            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
+            int ds1 = im01 & im10;        // true if both are 1
+
+            // return 1 if primary is 1 and secondary is 0 or
+            // primary is 0 and secondary is 1
+            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+        }
+
+        return mask;
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = this*matrix
+     * @param matrix
+     */
+    private void postTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, mValues, matrix);
+        mValues = tmp;
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = matrix*this
+     * @param matrix
+     */
+    private void preTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, matrix, mValues);
+        mValues = tmp;
+    }
+
+    /**
+     * Apply this matrix to the array of 2D points specified by src, and write
+      * the transformed points into the array of points specified by dst. The
+      * two arrays represent their "points" as pairs of floats [x, y].
+      *
+      * @param dst   The array of dst points (x,y pairs)
+      * @param dstIndex The index of the first [x,y] pair of dst floats
+      * @param src   The array of src points (x,y pairs)
+      * @param srcIndex The index of the first [x,y] pair of src floats
+      * @param pointCount The number of points (x,y pairs) to transform
+      */
+
+     private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
+                           int pointCount) {
+         //checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
+
+         float[] tmpDest = dst;
+         boolean inPlace = dst == src;
+         if (inPlace) {
+             tmpDest = new float[dstIndex + pointCount * 2];
+         }
+
+         for (int i = 0 ; i < pointCount ; i++) {
+             // just in case we are doing in place, we better put this in temp vars
+             float x = mValues[0] * src[i + srcIndex] +
+                       mValues[1] * src[i + srcIndex + 1] +
+                       mValues[2];
+             float y = mValues[3] * src[i + srcIndex] +
+                       mValues[4] * src[i + srcIndex + 1] +
+                       mValues[5];
+
+             tmpDest[i + dstIndex]     = x;
+             tmpDest[i + dstIndex + 1] = y;
+         }
+
+         if (inPlace) {
+             System.arraycopy(tmpDest, dstIndex, dst, dstIndex, pointCount * 2);
+         }
+     }
+
+     /**
+      * Apply this matrix to the array of 2D points, and write the transformed
+      * points back into the array
+      *
+      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
+      */
+
+     private void mapPoints(float[] pts) {
+         mapPoints(pts, 0, pts, 0, pts.length >> 1);
+     }
+
+    /**
+     * multiply two matrices and store them in a 3rd.
+     * <p/>This in effect does dest = a*b
+     * dest cannot be the same as a or b.
+     */
+    private static void multiply(float dest[], float[] a, float[] b) {
+        // first row
+        dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
+        dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
+        dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
+
+        // 2nd row
+        dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
+        dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
+        dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
+
+        // 3rd row
+        dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
+        dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
+        dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
+    }
+
+    /**
+     * Returns a matrix that represents a given translate
+     * @param dx
+     * @param dy
+     * @return
+     */
+    private static float[] getTranslate(float dx, float dy) {
+        return setTranslate(new float[9], dx, dy);
+    }
+
+    private static float[] setTranslate(float[] dest, float dx, float dy) {
+        dest[0] = 1;
+        dest[1] = 0;
+        dest[2] = dx;
+        dest[3] = 0;
+        dest[4] = 1;
+        dest[5] = dy;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    private static float[] getScale(float sx, float sy) {
+        return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+    }
+
+    /**
+     * Returns a matrix that represents the given scale info.
+     * @param sx
+     * @param sy
+     * @param px
+     * @param py
+     */
+    private static float[] getScale(float sx, float sy, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate tmp so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // scale into tmp2
+        multiply(tmp2, tmp, getScale(sx, sy));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+
+    private static float[] getRotate(float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return getRotate(sin, cos);
+    }
+
+    private static float[] getRotate(float sin, float cos) {
+        return setRotate(new float[9], sin, cos);
+    }
+
+    private static float[] setRotate(float[] dest, float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return setRotate(dest, sin, cos);
+    }
+
+    private static float[] setRotate(float[] dest, float sin, float cos) {
+        dest[0] = cos;
+        dest[1] = -sin;
+        dest[2] = 0;
+        dest[3] = sin;
+        dest[4] = cos;
+        dest[5] = 0;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    private static float[] getRotate(float degrees, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // rotate into tmp2
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        multiply(tmp2, tmp, getRotate(sin, cos));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+    private static float[] getSkew(float kx, float ky) {
+        return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
+    }
+
+    private static float[] getSkew(float kx, float ky, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // skew into tmp2
+        multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
index 12d2cde..c0bc005 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path.java
@@ -31,13 +31,13 @@
  * text on a path.
  */
 public class Path {
-    
+
     private FillType mFillType = FillType.WINDING;
     private GeneralPath mPath = new GeneralPath();
-    
+
     private float mLastX = 0;
     private float mLastY = 0;
-    
+
     //---------- Custom methods ----------
 
     public Shape getAwtShape() {
@@ -60,7 +60,7 @@
     public Path(Path src) {
         mPath.append(src.mPath, false /* connect */);
     }
-    
+
     /**
      * Clear any lines and curves from the path, making it empty.
      * This does NOT change the fill-type setting.
@@ -92,7 +92,7 @@
         EVEN_ODD        (GeneralPath.WIND_EVEN_ODD, false),
         INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
         INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
-        
+
         FillType(int rule, boolean inverse) {
             this.rule = rule;
             this.inverse = inverse;
@@ -101,7 +101,7 @@
         final int rule;
         final boolean inverse;
     }
-    
+
     /**
      * Return the path's fill type. This defines how "inside" is
      * computed. The default value is WINDING.
@@ -121,7 +121,7 @@
         mFillType = ft;
         mPath.setWindingRule(ft.rule);
     }
-    
+
     /**
      * Returns true if the filltype is one of the INVERSE variants
      *
@@ -130,7 +130,7 @@
     public boolean isInverseFillType() {
         return mFillType.inverse;
     }
-    
+
     /**
      * Toggles the INVERSE state of the filltype
      */
@@ -150,7 +150,7 @@
                 break;
         }
     }
-    
+
     /**
      * Returns true if the path is empty (contains no lines or curves)
      *
@@ -350,7 +350,7 @@
                       boolean forceMoveTo) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Append the specified arc to the path as a new contour. If the start of
      * the path is different from the path's current last point, then an
@@ -365,7 +365,7 @@
     public void arcTo(RectF oval, float startAngle, float sweepAngle) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Close the current contour. If the current point is not equal to the
      * first point of the contour, a line segment is automatically added.
@@ -383,13 +383,13 @@
         CW  (0),    // must match enum in SkPath.h
         /** counter-clockwise */
         CCW (1);    // must match enum in SkPath.h
-        
+
         Direction(int ni) {
             nativeInt = ni;
         }
         final int nativeInt;
     }
-    
+
     /**
      * Add a closed rectangle contour to the path
      *
@@ -400,7 +400,7 @@
         if (rect == null) {
             throw new NullPointerException("need rect parameter");
         }
-        
+
         addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
     }
 
@@ -446,7 +446,7 @@
 
         // FIXME Need to support direction
         Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
-        
+
         mPath.append(ovalShape, false /* connect */);
     }
 
@@ -493,7 +493,7 @@
         // FIXME
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Add a closed round-rectangle contour to the path. Each corner receives
      * two radius values [X, Y]. The corners are ordered top-left, top-right,
@@ -513,7 +513,7 @@
         // FIXME
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Add a copy of src to the path, offset by (dx,dy)
      *
@@ -554,11 +554,11 @@
      */
     public void offset(float dx, float dy, Path dst) {
         GeneralPath newPath = new GeneralPath();
-        
+
         PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-        
+
         newPath.append(iterator, false /* connect */);
-        
+
         if (dst != null) {
             dst.mPath = newPath;
         } else {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index 106d7ed..8592731 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -267,7 +267,7 @@
             customStyle = parser.getAttributeValue(null /* namespace*/, "style");
         }
         if (customStyle != null) {
-            IResourceValue item = findResValue(customStyle);
+            IResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/);
 
             if (item instanceof IStyleResourceValue) {
                 defStyleValues = (IStyleResourceValue)item;
@@ -284,7 +284,7 @@
 
                 if (item != null) {
                     // item is a reference to a style entry. Search for it.
-                    item = findResValue(item.getValue());
+                    item = findResValue(item.getValue(), false /*forceFrameworkOnly*/);
 
                     if (item instanceof IStyleResourceValue) {
                         defStyleValues = (IStyleResourceValue)item;
@@ -414,7 +414,7 @@
         }
 
         // get the IResourceValue referenced by this value
-        IResourceValue resValue = findResValue(value);
+        IResourceValue resValue = findResValue(value, false /*forceFrameworkOnly*/);
 
         // if resValue is null, but value is not null, this means it was not a reference.
         // we return the name/value wrapper in a IResourceValue
@@ -450,7 +450,7 @@
         }
 
         // else attempt to find another IResourceValue referenced by this one.
-        IResourceValue resolvedValue = findResValue(value.getValue());
+        IResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
 
         // if the value did not reference anything, then we simply return the input value
         if (resolvedValue == null) {
@@ -477,9 +477,11 @@
      * only support the android namespace.
      *
      * @param reference the resource reference to search for.
+     * @param forceFrameworkOnly if true all references are considered to be toward framework
+     *      resource even if the reference does not include the android: prefix.
      * @return a {@link IResourceValue} or <code>null</code>.
      */
-    IResourceValue findResValue(String reference) {
+    IResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
         if (reference == null) {
             return null;
         }
@@ -561,7 +563,8 @@
                 segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
             }
 
-            return findResValue(segments[0], segments[1], frameworkOnly);
+            return findResValue(segments[0], segments[1],
+                    forceFrameworkOnly ? true :frameworkOnly);
         }
 
         // Looks like the value didn't reference anything. Return null.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
index 4be6eabc..d145ff6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
@@ -41,7 +41,7 @@
     /*
      * (non-Javadoc)
      * @see android.util.XmlPullAttributes#getAttributeNameResource(int)
-     * 
+     *
      * This methods must return com.android.internal.R.attr.<name> matching
      * the name of the attribute.
      * It returns 0 if it doesn't find anything.
@@ -50,19 +50,19 @@
     public int getAttributeNameResource(int index) {
         // get the attribute name.
         String name = getAttributeName(index);
-        
+
         // get the attribute namespace
         String ns = mParser.getAttributeNamespace(index);
-        
+
         if (BridgeConstants.NS_RESOURCES.equals(ns)) {
             Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
             if (v != null) {
                 return v.intValue();
             }
-            
+
             return 0;
         }
-        
+
         // this is not an attribute in the android namespace, we query the customviewloader, if
         // the namespaces match.
         if (mContext.getProjectCallback().getNamespace().equals(ns)) {
@@ -75,7 +75,7 @@
 
         return 0;
     }
-    
+
     /*
      * (non-Javadoc)
      * @see android.util.XmlPullAttributes#getAttributeResourceValue(int, int)
@@ -83,7 +83,7 @@
     @Override
     public int getAttributeResourceValue(int index, int defaultValue) {
         String value = getAttributeValue(index);
-        
+
         return resolveResourceValue(value, defaultValue);
     }
 
@@ -94,14 +94,15 @@
     @Override
     public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
         String value = getAttributeValue(namespace, attribute);
-        
+
         return resolveResourceValue(value, defaultValue);
     }
 
     private int resolveResourceValue(String value, int defaultValue) {
         // now look for this particular value
-        IResourceValue resource = mContext.resolveResValue(mContext.findResValue(value));
-        
+        IResourceValue resource = mContext.resolveResValue(
+                mContext.findResValue(value, mPlatformFile));
+
         if (resource != null) {
             Integer id = null;
             if (mPlatformFile || resource.isFramework()) {
@@ -115,7 +116,7 @@
                 return id;
             }
         }
-        
+
         return defaultValue;
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
new file mode 100644
index 0000000..3d9f960
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
@@ -0,0 +1,89 @@
+/*
+ * 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.layoutlib.bridge;
+
+import android.util.SparseArray;
+
+/**
+ * Manages native delegates.
+ *
+ * This is used in conjunction with layoublib_create: certain Android java classes are mere
+ * wrappers around a heavily native based implementation, and we need a way to run these classes
+ * in our Eclipse rendering framework without bringing all the native code from the Android
+ * platform.
+ *
+ * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
+ * native methods by "delegate calls".
+ *
+ * For example, a native method android.graphics.Matrix.init(...) will actually become
+ * a call to android.graphics.Matrix_Delegate.init(...).
+ *
+ * The Android java classes that use native code uses an int (Java side) to reference native
+ * objects. This int is generally directly the pointer to the C structure counterpart.
+ * Typically a creation method will return such an int, and then this int will be passed later
+ * to a Java method to identify the C object to manipulate.
+ *
+ * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
+ * int -> Delegate class link.
+ *
+ * Native methods usually always have the int as parameters. The first thing the delegate method
+ * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
+ *
+ * Typical native init methods are returning a new int back to the Java class, so
+ * {@link #addDelegate(Object)} does the same.
+ *
+ * @param <T> the delegate class to manage
+ */
+public final class DelegateManager<T> {
+
+    private final SparseArray<T> mDelegates = new SparseArray<T>();
+    private int mDelegateCounter = 0;
+
+    /**
+     * Returns the delegate from the given native int.
+     * @param native_object the native int.
+     * @return the delegate or null if not found.
+     */
+    public T getDelegate(int native_object) {
+        synchronized (mDelegates) {
+            return mDelegates.get(native_object);
+        }
+    }
+
+    /**
+     * Adds a delegate to the manager and returns the native int used to identify it.
+     * @param newDelegate the delegate to add
+     * @return a unique native int to identify the delegate
+     */
+    public int addDelegate(T newDelegate) {
+        synchronized (mDelegates) {
+            int native_object = ++mDelegateCounter;
+            mDelegates.put(native_object, newDelegate);
+            return native_object;
+        }
+    }
+
+    /**
+     * Removes the delegate matching the given native int.
+     * @param native_object the native int.
+     */
+    public void removeDelegate(int native_object) {
+        synchronized (mDelegates) {
+            mDelegates.remove(native_object);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
index 3d0dd73..f624753 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
@@ -148,8 +148,7 @@
                     parser.setInput(new FileReader(f));
 
                     d = Drawable.createFromXml(context.getResources(),
-                            // FIXME: we need to know if this resource is platform or not
-                            new BridgeXmlBlockParser(parser, context, false));
+                            new BridgeXmlBlockParser(parser, context, isFramework));
                     return d;
                 } catch (XmlPullParserException e) {
                     context.getLogger().error(e);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
new file mode 100644
index 0000000..6eed8ba
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
@@ -0,0 +1,115 @@
+/*
+ * 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.layoutlib.bridge;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.create.CreateInfo;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that native delegate classes implement all the required methods.
+ *
+ * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * have their native methods reimplemented through a delegate.
+ *
+ * Since the reimplemented methods are not native anymore, we look for the annotation
+ * {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
+ * as the modified class with _Delegate added as a suffix).
+ * If the original native method is not static, then we make sure the delegate method also
+ * include the original class as first parameter (to access "this").
+ *
+ */
+public class TestNativeDelegate extends TestCase {
+
+    public void  testNativeDelegates() {
+
+        final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+        final int count = classes.length;
+        for (int i = 0 ; i < count ; i++) {
+            loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+        }
+    }
+
+    private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
+        // load the classes
+        try {
+            ClassLoader classLoader = TestNativeDelegate.class.getClassLoader();
+            Class<?> originalClass = classLoader.loadClass(originalClassName);
+            Class<?> delegateClass = classLoader.loadClass(delegateClassName);
+
+            compare(originalClass, delegateClass);
+        } catch (ClassNotFoundException e) {
+           fail("Failed to load class: " + e.getMessage());
+        } catch (SecurityException e) {
+            fail("Failed to load class: " + e.getMessage());
+        }
+    }
+
+    private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
+        Method[] originalMethods = originalClass.getDeclaredMethods();
+
+        for (Method originalMethod : originalMethods) {
+            // look for methods that were native: they have the LayoutlibDelegate annotation
+            if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                continue;
+            }
+
+            // get the signature.
+            Class<?>[] parameters = originalMethod.getParameterTypes();
+
+            // if the method is not static, then the class is added as the first parameter
+            // (for "this")
+            if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
+
+                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
+                newParameters[0] = originalClass;
+                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+                parameters = newParameters;
+            }
+
+            try {
+                // try to load the method with the given parameter types.
+                delegateClass.getMethod(originalMethod.getName(), parameters);
+            } catch (NoSuchMethodException e) {
+                // compute a full class name that's long but not too long.
+                StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
+                for (int j = 0; j < parameters.length; j++) {
+                    Class<?> theClass = parameters[j];
+                    sb.append(theClass.getName());
+                    int dimensions = 0;
+                    while (theClass.isArray()) {
+                        dimensions++;
+                        theClass = theClass.getComponentType();
+                    }
+                    for (int i = 0; i < dimensions; i++) {
+                        sb.append("[]");
+                    }
+                    if (j < (parameters.length - 1)) {
+                        sb.append(",");
+                    }
+                }
+                sb.append(")");
+
+                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString()));
+            }
+        }
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 92892784..2d0ee6d 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 /**
  * Describes the work to be done by {@link AsmGenerator}.
  */
@@ -83,7 +85,9 @@
             OverrideMethod.class,
             MethodListener.class,
             MethodAdapter.class,
-            CreateInfo.class
+            ICreateInfo.class,
+            CreateInfo.class,
+            LayoutlibDelegate.class
         };
 
     /**
@@ -99,8 +103,7 @@
      * The list of classes on which to delegate all native methods.
      */
     private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
-        // TODO: comment out once DelegateClass is working
-        // "android.graphics.Paint"
+        "android.graphics.Matrix",
     };
 
     /**
@@ -126,7 +129,6 @@
             "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
             "android.graphics.DashPathEffect",       "android.graphics._Original_DashPathEffect",
             "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
-            "android.graphics.Matrix",              "android.graphics._Original_Matrix",
             "android.graphics.Paint",               "android.graphics._Original_Paint",
             "android.graphics.Path",                "android.graphics._Original_Path",
             "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",