Merge "Added missing label and char limits"
diff --git a/api/current.xml b/api/current.xml
index 35fb3ef..54aae7f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26658,6 +26658,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;extra_click_download_ids&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PAUSED_QUEUED_FOR_WIFI"
  type="int"
  transient="false"
@@ -27617,6 +27628,17 @@
  visibility="public"
 >
 </method>
+<method name="onDestroyOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="onDestroyView"
  return="void"
  abstract="false"
@@ -50109,6 +50131,17 @@
  visibility="public"
 >
 </field>
+<field name="CATEGORY_HE_DESK_DOCK"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.category.HE_DESK_DOCK&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CATEGORY_HOME"
  type="java.lang.String"
  transient="false"
@@ -50142,6 +50175,17 @@
  visibility="public"
 >
 </field>
+<field name="CATEGORY_LE_DESK_DOCK"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.category.LE_DESK_DOCK&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CATEGORY_MONKEY"
  type="java.lang.String"
  transient="false"
@@ -50361,6 +50405,28 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_DOCK_STATE_HE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DOCK_STATE_LE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_DOCK_STATE_UNDOCKED"
  type="int"
  transient="false"
@@ -83600,6 +83666,17 @@
  visibility="public"
 >
 </method>
+<method name="getColor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getOpacity"
  return="int"
  abstract="false"
@@ -83624,6 +83701,19 @@
 <parameter name="alpha" type="int">
 </parameter>
 </method>
+<method name="setColor"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="color" type="int">
+</parameter>
+</method>
 <method name="setColorFilter"
  return="void"
  abstract="false"
@@ -145048,6 +145138,23 @@
 <parameter name="key" type="java.lang.CharSequence">
 </parameter>
 </method>
+<method name="finishPreferencePanel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="caller" type="android.app.Fragment">
+</parameter>
+<parameter name="resultCode" type="int">
+</parameter>
+<parameter name="resultData" type="android.content.Intent">
+</parameter>
+</method>
 <method name="getPreferenceManager"
  return="android.preference.PreferenceManager"
  abstract="false"
@@ -145276,7 +145383,7 @@
 <parameter name="push" type="boolean">
 </parameter>
 </method>
-<method name="startPreferenceFragment"
+<method name="startPreferencePanel"
  return="void"
  abstract="false"
  native="false"
@@ -145286,9 +145393,17 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fragment" type="android.app.Fragment">
+<parameter name="fragmentClass" type="java.lang.String">
 </parameter>
-<parameter name="ft" type="android.app.FragmentTransaction">
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="titleRes" type="int">
+</parameter>
+<parameter name="titleText" type="java.lang.CharSequence">
+</parameter>
+<parameter name="resultTo" type="android.app.Fragment">
+</parameter>
+<parameter name="resultRequestCode" type="int">
 </parameter>
 </method>
 <method name="startWithFragment"
@@ -145305,6 +145420,10 @@
 </parameter>
 <parameter name="args" type="android.os.Bundle">
 </parameter>
+<parameter name="resultTo" type="android.app.Fragment">
+</parameter>
+<parameter name="resultRequestCode" type="int">
+</parameter>
 </method>
 <method name="switchToHeader"
  return="void"
@@ -245494,7 +245613,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
@@ -347567,7 +347686,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="toCopyIn" type="E[]">
+<parameter name="array" type="E[]">
 </parameter>
 </constructor>
 <method name="add"
@@ -347595,7 +347714,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="element" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="addAll"
@@ -347723,7 +347842,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="indexOf"
@@ -347736,9 +347857,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="isEmpty"
@@ -347773,7 +347892,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="lastIndexOf"
@@ -347786,9 +347907,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="listIterator"
@@ -347879,7 +347998,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="element" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="size"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 37c8ad0..b5fddfa 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -389,11 +389,13 @@
             if (err == 0) {
                 observer.waitForCompletion();
                 sets = observer.sets;
-                for (RestoreSet s : sets) {
-                    if (s.token == token) {
-                        System.out.println("Scheduling restore: " + s.name);
-                        didRestore = (mRestore.restoreAll(token, observer) == 0);
-                        break;
+                if (sets != null) {
+                    for (RestoreSet s : sets) {
+                        if (s.token == token) {
+                            System.out.println("Scheduling restore: " + s.name);
+                            didRestore = (mRestore.restoreAll(token, observer) == 0);
+                            break;
+                        }
                     }
                 }
             }
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index 1741e60..2242462 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -28,12 +28,18 @@
 
     private int mNumKeyframes;
 
-    ArrayList<Keyframe> mKeyframes;
+    Keyframe mFirstKeyframe;
+    Keyframe mLastKeyframe;
+    TimeInterpolator mInterpolator; // only used in the 2-keyframe case
+    ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
 
     public KeyframeSet(Keyframe... keyframes) {
+        mNumKeyframes = keyframes.length;
         mKeyframes = new ArrayList<Keyframe>();
         mKeyframes.addAll(Arrays.asList(keyframes));
-        mNumKeyframes = mKeyframes.size();
+        mFirstKeyframe = mKeyframes.get(0);
+        mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
+        mInterpolator = mLastKeyframe.getInterpolator();
     }
 
     public static KeyframeSet ofInt(int... values) {
@@ -125,32 +131,40 @@
      * @return The animated value.
      */
     public Object getValue(float fraction, TypeEvaluator evaluator) {
-        // TODO: special-case 2-keyframe common case
+
+        // Special-case optimization for the common case of only two keyframes
+        if (mNumKeyframes == 2) {
+            if (mInterpolator != null) {
+                fraction = mInterpolator.getInterpolation(fraction);
+            }
+            return evaluator.evaluate(fraction, mFirstKeyframe.getValue(),
+                    mLastKeyframe.getValue());
+        }
 
         if (fraction <= 0f) {
-            final Keyframe prevKeyframe = mKeyframes.get(0);
             final Keyframe nextKeyframe = mKeyframes.get(1);
             final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
             if (interpolator != null) {
                 fraction = interpolator.getInterpolation(fraction);
             }
-            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
-                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
-            return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+            final float prevFraction = mFirstKeyframe.getFraction();
+            float intervalFraction = (fraction - prevFraction) /
+                (nextKeyframe.getFraction() - prevFraction);
+            return evaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
                     nextKeyframe.getValue());
         } else if (fraction >= 1f) {
             final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
-            final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1);
-            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
             if (interpolator != null) {
                 fraction = interpolator.getInterpolation(fraction);
             }
-            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
-                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+            final float prevFraction = prevKeyframe.getFraction();
+            float intervalFraction = (fraction - prevFraction) /
+                (mLastKeyframe.getFraction() - prevFraction);
             return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
-                    nextKeyframe.getValue());
+                    mLastKeyframe.getValue());
         }
-        Keyframe prevKeyframe = mKeyframes.get(0);
+        Keyframe prevKeyframe = mFirstKeyframe;
         for (int i = 1; i < mNumKeyframes; ++i) {
             Keyframe nextKeyframe = mKeyframes.get(i);
             if (fraction < nextKeyframe.getFraction()) {
@@ -158,14 +172,15 @@
                 if (interpolator != null) {
                     fraction = interpolator.getInterpolation(fraction);
                 }
-                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
-                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+                final float prevFraction = prevKeyframe.getFraction();
+                float intervalFraction = (fraction - prevFraction) /
+                    (nextKeyframe.getFraction() - prevFraction);
                 return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                         nextKeyframe.getValue());
             }
             prevKeyframe = nextKeyframe;
         }
-        // shouldn't get here
-        return mKeyframes.get(mNumKeyframes - 1).getValue();
+        // shouldn't reach here
+        return mLastKeyframe.getValue();
     }
 }
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 7427651..5b8c91d 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -376,9 +376,14 @@
      */
     @Override
     public void setTarget(Object target) {
-        mTarget = target;
-        // New property/values/target should cause re-initialization prior to starting
-        mInitialized = false;
+        if (mTarget != target) {
+            mTarget = target;
+            if (mTarget  != null && target != null && mTarget.getClass() == target.getClass()) {
+                return;
+            }
+            // New target type should cause re-initialization prior to starting
+            mInitialized = false;
+        }
     }
 
     @Override
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ad8c971..a0b70b5 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1142,7 +1142,7 @@
         switch (mPlayingState) {
         case RUNNING:
         case SEEKED:
-            float fraction = (float)(currentTime - mStartTime) / mDuration;
+            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
             if (fraction >= 1f) {
                 if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                     // Time to repeat
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 66038d8..b359ce6 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -299,7 +299,7 @@
      * 
      * <p>Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the
      * {@link #DISPLAY_SHOW_HOME} option.
-     * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_HIDE_HOME | DISPLAY_USE_LOGO)
+     * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO)
      * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}.
      * 
      * @param options A combination of the bits defined by the DISPLAY_ constants
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 45ce860..b9ac848 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -27,6 +27,14 @@
  * AndroidManifest.xml's &lt;application&gt; tag, which will cause that class
  * to be instantiated for you when the process for your application/package is
  * created.
+ * 
+ * <p class="note">There is normally no need to subclass Application.  In
+ * most situation, static singletons can provide the same functionality in a
+ * more modular way.  If your singleton needs a global context (for example
+ * to register broadcast receivers), the function to retrieve it can be
+ * given a {@link android.content.Context} which internally uses
+ * {@link android.content.Context#getApplicationContext() Context.getApplicationContext()}
+ * when first constructing the singleton.</p>
  */
 public class Application extends ContextWrapper implements ComponentCallbacks {
     
@@ -46,12 +54,10 @@
     }
 
     /**
-     * Called when the application is stopping.  There are no more application
-     * objects running and the process will exit.  <em>Note: never depend on
-     * this method being called; in many cases an unneeded application process
-     * will simply be killed by the kernel without executing any application
-     * code.</em>
-     * If you override this method, be sure to call super.onTerminate().
+     * This method is for use in emulated process environments.  It will
+     * never be called on a production Android device, where processes are
+     * removed by simply killing them; no user code (including this callback)
+     * is executed when doing so.
      */
     public void onTerminate() {
     }
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 570e494..592a63b 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -272,6 +272,14 @@
      */
     public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
 
+    /**
+     * When clicks on multiple notifications are received, the following
+     * provides an array of download ids corresponding to the download notification that was
+     * clicked. It can be retrieved by the receiver of this
+     * Intent using {@link android.content.Intent#getLongArrayExtra(String)}.
+     */
+    public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids";
+
     // this array must contain all public columns
     private static final String[] COLUMNS = new String[] {
         COLUMN_ID,
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f27a15e..eaf1aee 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1027,6 +1027,16 @@
     }
 
     /**
+     * Called when this fragment's option menu items are no longer being
+     * included in the overall options menu.  Receiving this call means that
+     * the menu needed to be rebuilt, but this fragment's items were not
+     * included in the newly built menu (its {@link #onCreateOptionsMenu(Menu, MenuInflater)}
+     * was not called).
+     */
+    public void onDestroyOptionsMenu() {
+    }
+    
+    /**
      * This hook is called whenever an item in your options menu is selected.
      * The default implementation simply returns false to have the normal
      * processing happen (calling the item's Runnable or sending a message to
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index 22e0747..e924c1c 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.animation.LayoutTransition;
 import android.app.FragmentManager.BackStackEntry;
 import android.content.Context;
 import android.util.AttributeSet;
@@ -69,6 +70,7 @@
         addView(mContainer);
         a.getFragmentManager().addOnBackStackChangedListener(this);
         updateCrumbs();
+        setLayoutTransition(new LayoutTransition());
     }
 
     /**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 45f9325..512ca16 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -268,6 +268,7 @@
     ArrayList<Fragment> mAdded;
     ArrayList<Integer> mAvailIndices;
     ArrayList<BackStackRecord> mBackStack;
+    ArrayList<Fragment> mCreatedMenus;
     
     // Must be accessed while locked.
     ArrayList<BackStackRecord> mBackStackIndices;
@@ -1325,15 +1326,32 @@
     
     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         boolean show = false;
+        ArrayList<Fragment> newMenus = null;
         if (mActive != null) {
             for (int i=0; i<mAdded.size(); i++) {
                 Fragment f = mAdded.get(i);
                 if (f != null && !f.mHidden && f.mHasMenu) {
                     show = true;
                     f.onCreateOptionsMenu(menu, inflater);
+                    if (newMenus == null) {
+                        newMenus = new ArrayList<Fragment>();
+                    }
+                    newMenus.add(f);
                 }
             }
         }
+        
+        if (mCreatedMenus != null) {
+            for (int i=0; i<mCreatedMenus.size(); i++) {
+                Fragment f = mCreatedMenus.get(i);
+                if (newMenus == null || !newMenus.contains(f)) {
+                    f.onDestroyOptionsMenu();
+                }
+            }
+        }
+        
+        mCreatedMenus = newMenus;
+        
         return show;
     }
     
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 0c8e4d9..138e7f2 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -773,6 +773,7 @@
 
                 case CONNECT_HFP_INCOMING:
                     transitionTo(mIncomingHandsfree);
+                    break;
                 case CONNECT_A2DP_INCOMING:
                     transitionTo(mIncomingA2dp);
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8c36aa6..e14282c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -533,6 +533,8 @@
  *     <li> {@link #CATEGORY_TEST}
  *     <li> {@link #CATEGORY_CAR_DOCK}
  *     <li> {@link #CATEGORY_DESK_DOCK}
+ *     <li> {@link #CATEGORY_LE_DESK_DOCK}
+ *     <li> {@link #CATEGORY_HE_DESK_DOCK}
  *     <li> {@link #CATEGORY_CAR_MODE}
  *     <li> {@link #CATEGORY_APP_MARKET}
  * </ul>
@@ -549,6 +551,8 @@
  *     <li> {@link #EXTRA_CHANGED_COMPONENT_NAME}
  *     <li> {@link #EXTRA_DATA_REMOVED}
  *     <li> {@link #EXTRA_DOCK_STATE}
+ *     <li> {@link #EXTRA_DOCK_STATE_HE_DESK}
+ *     <li> {@link #EXTRA_DOCK_STATE_LE_DESK}
  *     <li> {@link #EXTRA_DOCK_STATE_CAR}
  *     <li> {@link #EXTRA_DOCK_STATE_DESK}
  *     <li> {@link #EXTRA_DOCK_STATE_UNDOCKED}
@@ -1822,6 +1826,36 @@
             "android.intent.action.HEADSET_PLUG";
 
     /**
+     * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
+     *   <li><em>name</em> - Headset type, human readable string </li>
+     * </ul>
+     * </ul>
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USB_ANLG_HEADSET_PLUG =
+            "android.intent.action.DOCK_HEADSET_PLUG";
+
+    /**
+     * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
+     *   <li><em>name</em> - Headset type, human readable string </li>
+     * </ul>
+     * </ul>
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USB_DGTL_HEADSET_PLUG =
+            "android.intent.action.DOCK_HEADSET_PLUG";
+
+    /**
      * Broadcast Action: An outgoing call is about to be placed.
      *
      * <p>The Intent will have the following extra value:
@@ -2060,6 +2094,21 @@
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
+    /**
+     * An activity to run when device is inserted into a analog (low end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
+
+    /**
+     * An activity to run when device is inserted into a digital (high end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK";
 
     /**
      * Used to indicate that the activity can be used in a car environment.
@@ -2208,7 +2257,9 @@
      * intents to request the dock state.  Possible values are
      * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
      * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
-     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_LE_DESK}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_HE_DESK}.
      */
     public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
 
@@ -2231,6 +2282,18 @@
     public static final int EXTRA_DOCK_STATE_CAR = 2;
 
     /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a analog (low end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_LE_DESK = 3;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a digital (high end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_HE_DESK = 4;
+
+    /**
      * Boolean that can be supplied as meta-data with a dock activity, to
      * indicate that the dock should take over the home key when it is active.
      */
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 0c6a237..97c957d 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -825,7 +825,7 @@
 
     /**
      * Called when the user selects an item in the header list.  The default
-     * implementation will call either {@link #startWithFragment(String, Bundle)}
+     * implementation will call either {@link #startWithFragment(String, Bundle, Fragment, int)}
      * or {@link #switchToHeader(Header)} as appropriate.
      *
      * @param header The header that was selected.
@@ -834,7 +834,7 @@
     public void onHeaderClick(Header header, int position) {
         if (header.fragment != null) {
             if (mSinglePane) {
-                startWithFragment(header.fragment, header.fragmentArguments);
+                startWithFragment(header.fragment, header.fragmentArguments, null, 0);
             } else {
                 switchToHeader(header);
             }
@@ -852,13 +852,18 @@
      * @param fragmentName The name of the fragment to display.
      * @param args Optional arguments to supply to the fragment.
      */
-    public void startWithFragment(String fragmentName, Bundle args) {
+    public void startWithFragment(String fragmentName, Bundle args,
+            Fragment resultTo, int resultRequestCode) {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClass(this, getClass());
         intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
         intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
         intent.putExtra(EXTRA_NO_HEADERS, true);
-        startActivity(intent);
+        if (resultTo == null) {
+            startActivity(intent);
+        } else {
+            resultTo.startActivityForResult(intent, resultRequestCode);
+        }
     }
 
     /**
@@ -923,9 +928,15 @@
      * @param header The new header to display.
      */
     public void switchToHeader(Header header) {
-        int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
-        switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
-        setSelectedHeader(header);
+        if (mCurHeader == header) {
+            // This is the header we are currently displaying.  Just make sure
+            // to pop the stack up to its root state.
+            getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+        } else {
+            int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
+            switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
+            setSelectedHeader(header);
+        }
     }
 
     Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
@@ -982,7 +993,7 @@
      */
     public void startPreferenceFragment(Fragment fragment, boolean push) {
         FragmentTransaction transaction = getFragmentManager().openTransaction();
-        startPreferenceFragment(fragment, transaction);
+        transaction.replace(com.android.internal.R.id.prefs, fragment);
         if (push) {
             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
             transaction.addToBackStack(BACK_STACK_PREFS);
@@ -993,25 +1004,74 @@
     }
 
     /**
-     * Start a new fragment.
-     *
-     * @param fragment The fragment to start
-     * @param ft The FragmentTransaction in which to perform this operation.
-     * Will not be added to the back stack or committed for you; you use do that.
+     * Start a new fragment containing a preference panel.  If the prefences
+     * are being displayed in multi-pane mode, the given fragment class will
+     * be instantiated and placed in the appropriate pane.  If running in
+     * single-pane mode, a new activity will be launched in which to show the
+     * fragment.
+     * 
+     * @param fragmentClass Full name of the class implementing the fragment.
+     * @param args Any desired arguments to supply to the fragment.
+     * @param titleRes Optional resource identifier of the title of this
+     * fragment.
+     * @param titleText Optional text of the title of this fragment.
+     * @param resultTo Optional fragment that result data should be sent to.
+     * If non-null, resultTo.onActivityResult() will be called when this
+     * preference panel is done.  The launched panel must use
+     * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
+     * @param resultRequestCode If resultTo is non-null, this is the caller's
+     * request code to be received with the resut.
      */
-    public void startPreferenceFragment(Fragment fragment, FragmentTransaction ft) {
-        ft.replace(com.android.internal.R.id.prefs, fragment);
+    public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
+            CharSequence titleText, Fragment resultTo, int resultRequestCode) {
+        if (mSinglePane) {
+            startWithFragment(fragmentClass, args, resultTo, resultRequestCode);
+        } else {
+            Fragment f = Fragment.instantiate(this, fragmentClass, args);
+            if (resultTo != null) {
+                f.setTargetFragment(resultTo, resultRequestCode);
+            }
+            FragmentTransaction transaction = getFragmentManager().openTransaction();
+            transaction.replace(com.android.internal.R.id.prefs, f);
+            if (titleRes != 0) {
+                transaction.setBreadCrumbTitle(titleRes);
+            } else if (titleText != null) {
+                transaction.setBreadCrumbTitle(titleText);
+            }
+            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+            transaction.addToBackStack(BACK_STACK_PREFS);
+            transaction.commit();
+        }
     }
-
+    
+    /**
+     * Called by a preference panel fragment to finish itself.
+     * 
+     * @param caller The fragment that is asking to be finished.
+     * @param resultCode Optional result code to send back to the original
+     * launching fragment.
+     * @param resultData Optional result data to send back to the original
+     * launching fragment.
+     */
+    public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
+        if (mSinglePane) {
+            setResult(resultCode, resultData);
+            finish();
+        } else {
+            if (caller != null) {
+                if (caller.getTargetFragment() != null) {
+                    caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
+                            resultCode, resultData);
+                }
+            }
+            // XXX be smarter about popping the stack.
+            onBackPressed();
+        }
+    }
+    
     @Override
     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
-        Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
-        FragmentTransaction transaction = getFragmentManager().openTransaction();
-        startPreferenceFragment(f, transaction);
-        transaction.setBreadCrumbTitle(pref.getTitle());
-        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
-        transaction.addToBackStack(BACK_STACK_PREFS);
-        transaction.commit();
+        startPreferencePanel(pref.getFragment(), pref.getExtras(), 0, pref.getTitle(), null, 0);
         return true;
     }
 
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 7289012..ec8f031 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -385,11 +385,23 @@
         // Allow 3 seconds for profiles to gracefully disconnect
         // TODO: Introduce a callback mechanism so that each profile can notify
         // BluetoothService when it is done shutting down
+        disconnectDevices();
+
         mHandler.sendMessageDelayed(
                 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
         return true;
     }
 
+    private synchronized void disconnectDevices() {
+        // Disconnect devices handled by BluetoothService.
+        for (BluetoothDevice device: getConnectedInputDevices()) {
+            disconnectInputDevice(device);
+        }
+
+        for (BluetoothDevice device: getConnectedPanDevices()) {
+            disconnectPanDevice(device);
+        }
+    }
 
     private synchronized void finishDisable(boolean saveSetting) {
         if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 87e03cf..a9f0780 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6296,8 +6296,14 @@
             if (invalidateCache) {
                 mPrivateFlags &= ~DRAWING_CACHE_VALID;
             }
-            final ViewParent p = mParent;
             final AttachInfo ai = mAttachInfo;
+            final ViewParent p = mParent;
+            if (p != null && ai != null && ai.mHardwareAccelerated) {
+                // fast-track for GL-enabled applications; just invalidate the whole hierarchy
+                // with a null dirty rect, which tells the ViewRoot to redraw everything
+                p.invalidateChild(this, null);
+                return;
+            }
             if (p != null && ai != null) {
                 final Rect r = ai.mTmpInvalRect;
                 r.set(0, 0, mRight - mLeft, mBottom - mTop);
@@ -6321,7 +6327,8 @@
      */
     @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isOpaque() {
-        return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
+        return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK &&
+                (mAlpha >= 1.0f - ViewConfiguration.ALPHA_THRESHOLD);
     }
 
     private void computeOpaqueFlags() {
@@ -8618,7 +8625,11 @@
      */
     @RemotableViewMethod
     public void setBackgroundColor(int color) {
-        setBackgroundDrawable(new ColorDrawable(color));
+        if (mBGDrawable instanceof ColorDrawable) {
+            ((ColorDrawable) mBGDrawable).setColor(color);
+        } else {
+            setBackgroundDrawable(new ColorDrawable(color));
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b881bdd..aef13ad 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2101,6 +2101,8 @@
         final int cr = child.mRight;
         final int cb = child.mBottom;
 
+        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
+
         final int flags = mGroupFlags;
 
         if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
@@ -2182,7 +2184,7 @@
             }
         }
 
-        concatMatrix |= !child.hasIdentityMatrix();
+        concatMatrix |= !childHasIdentityMatrix;
 
         // Sets the flag as early as possible to allow draw() implementations
         // to call invalidate() successfully when doing animations
@@ -2231,40 +2233,41 @@
         }
 
         if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
-            int transX = 0;
-            int transY = 0;
+            if (transformToApply != null || !childHasIdentityMatrix) {
+                int transX = 0;
+                int transY = 0;
 
-            if (hasNoCache) {
-                transX = -sx;
-                transY = -sy;
-            }
+                if (hasNoCache) {
+                    transX = -sx;
+                    transY = -sy;
+                }
 
-            if (transformToApply != null) {
-                if (concatMatrix) {
-                    // Undo the scroll translation, apply the transformation matrix,
-                    // then redo the scroll translate to get the correct result.
+                if (transformToApply != null) {
+                    if (concatMatrix) {
+                        // Undo the scroll translation, apply the transformation matrix,
+                        // then redo the scroll translate to get the correct result.
+                        canvas.translate(-transX, -transY);
+                        canvas.concat(transformToApply.getMatrix());
+                        canvas.translate(transX, transY);
+                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+                    }
+
+                    float transformAlpha = transformToApply.getAlpha();
+                    if (transformAlpha < 1.0f) {
+                        alpha *= transformToApply.getAlpha();
+                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+                    }
+                }
+
+                if (!childHasIdentityMatrix) {
                     canvas.translate(-transX, -transY);
-                    canvas.concat(transformToApply.getMatrix());
+                    canvas.concat(child.getMatrix());
                     canvas.translate(transX, transY);
-                    mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
                 }
-
-                float transformAlpha = transformToApply.getAlpha();
-                if (transformAlpha < 1.0f) {
-                    alpha *= transformToApply.getAlpha();
-                    mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-                }
-            }
-
-            if (!child.hasIdentityMatrix()) {
-                canvas.translate(-transX, -transY);
-                canvas.concat(child.getMatrix());
-                canvas.translate(transX, transY);
             }
 
             if (alpha < 1.0f) {
                 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-
                 if (hasNoCache) {
                     final int multipliedAlpha = (int) (255 * alpha);
                     if (!child.onSetAlpha(multipliedAlpha)) {
@@ -3209,19 +3212,6 @@
 
         final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            final int[] location = attachInfo.mInvalidateChildLocation;
-            location[CHILD_LEFT_INDEX] = child.mLeft;
-            location[CHILD_TOP_INDEX] = child.mTop;
-            Matrix childMatrix = child.getMatrix();
-            if (!childMatrix.isIdentity()) {
-                RectF boundingRect = attachInfo.mTmpTransformRect;
-                boundingRect.set(dirty);
-                childMatrix.mapRect(boundingRect);
-                dirty.set((int) boundingRect.left, (int) boundingRect.top,
-                        (int) (boundingRect.right + 0.5f),
-                        (int) (boundingRect.bottom + 0.5f));
-            }
-
             // If the child is drawing an animation, we want to copy this flag onto
             // ourselves and the parent to make sure the invalidate request goes
             // through
@@ -3229,45 +3219,95 @@
 
             // Check whether the child that requests the invalidate is fully opaque
             final boolean isOpaque = child.isOpaque() && !drawAnimation &&
-                    child.getAnimation() != null;
+                    child.getAnimation() == null;
             // Mark the child as dirty, using the appropriate flag
             // Make sure we do not set both flags at the same time
             final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
 
-            do {
-                View view = null;
-                if (parent instanceof View) {
-                    view = (View) parent;
+            if (dirty == null) {
+                do {
+                    View view = null;
+                    if (parent instanceof View) {
+                        view = (View) parent;
+                        if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
+                            // already marked dirty - we're done
+                            break;
+                        }
+                    }
+
+                    if (drawAnimation) {
+                        if (view != null) {
+                            view.mPrivateFlags |= DRAW_ANIMATION;
+                        } else if (parent instanceof ViewRoot) {
+                            ((ViewRoot) parent).mIsAnimating = true;
+                        }
+                    }
+
+                    if (parent instanceof ViewRoot) {
+                        ((ViewRoot) parent).invalidate();
+                        parent = null;
+                    } else if (view != null) {
+                        if ((mPrivateFlags & DRAWN) == DRAWN) {
+                            view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                            if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
+                                view.mPrivateFlags =
+                                        (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
+                            }
+                            parent = view.mParent;
+                        } else {
+                            parent = null;
+                        }
+                    }
+                } while (parent != null);
+            } else {
+                final int[] location = attachInfo.mInvalidateChildLocation;
+                location[CHILD_LEFT_INDEX] = child.mLeft;
+                location[CHILD_TOP_INDEX] = child.mTop;
+                Matrix childMatrix = child.getMatrix();
+                if (!childMatrix.isIdentity()) {
+                    RectF boundingRect = attachInfo.mTmpTransformRect;
+                    boundingRect.set(dirty);
+                    childMatrix.mapRect(boundingRect);
+                    dirty.set((int) boundingRect.left, (int) boundingRect.top,
+                            (int) (boundingRect.right + 0.5f),
+                            (int) (boundingRect.bottom + 0.5f));
                 }
 
-                if (drawAnimation) {
+                do {
+                    View view = null;
+                    if (parent instanceof View) {
+                        view = (View) parent;
+                    }
+
+                    if (drawAnimation) {
+                        if (view != null) {
+                            view.mPrivateFlags |= DRAW_ANIMATION;
+                        } else if (parent instanceof ViewRoot) {
+                            ((ViewRoot) parent).mIsAnimating = true;
+                        }
+                    }
+
+                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
+                    // flag coming from the child that initiated the invalidate
+                    if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
+                        view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
+                    }
+
+                    parent = parent.invalidateChildInParent(location, dirty);
                     if (view != null) {
-                        view.mPrivateFlags |= DRAW_ANIMATION;
-                    } else if (parent instanceof ViewRoot) {
-                        ((ViewRoot) parent).mIsAnimating = true;
+                        // Account for transform on current parent
+                        Matrix m = view.getMatrix();
+                        if (!m.isIdentity()) {
+                            RectF boundingRect = attachInfo.mTmpTransformRect;
+                            boundingRect.set(dirty);
+                            m.mapRect(boundingRect);
+                            dirty.set((int) boundingRect.left, (int) boundingRect.top,
+                                    (int) (boundingRect.right + 0.5f),
+                                    (int) (boundingRect.bottom + 0.5f));
+                        }
                     }
-                }
-
-                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
-                // flag coming from the child that initiated the invalidate
-                if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
-                    view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
-                }
-
-                parent = parent.invalidateChildInParent(location, dirty);
-                if (view != null) {
-                    // Account for transform on current parent
-                    Matrix m = view.getMatrix();
-                    if (!m.isIdentity()) {
-                        RectF boundingRect = attachInfo.mTmpTransformRect;
-                        boundingRect.set(dirty);
-                        m.mapRect(boundingRect);
-                        dirty.set((int) boundingRect.left, (int) boundingRect.top,
-                                (int) (boundingRect.right + 0.5f),
-                                (int) (boundingRect.bottom + 0.5f));
-                    }
-                }
-            } while (parent != null);
+                } while (parent != null);
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 06261bb..22a7773 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -543,6 +543,11 @@
     public void invalidateChild(View child, Rect dirty) {
         checkThread();
         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+        if (dirty == null) {
+            // Fast invalidation for GL-enabled applications; GL must redraw everything
+            invalidate();
+            return;
+        }
         if (mCurScrollY != 0 || mTranslator != null) {
             mTempRect.set(dirty);
             dirty = mTempRect;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e5f4b08..c657a1c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -794,7 +794,17 @@
         public int softInputMode;
         
         /**
-         * Placement of window within the screen as per {@link Gravity}
+         * Placement of window within the screen as per {@link Gravity}.  Both
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} and
+         * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect)
+         * Gravity.applyDisplay} are used during window layout, with this value
+         * given as the desired gravity.  For example you can specify
+         * {@link Gravity#DISPLAY_CLIP_HORIZONTAL Gravity.DISPLAY_CLIP_HORIZONTAL} and
+         * {@link Gravity#DISPLAY_CLIP_VERTICAL Gravity.DISPLAY_CLIP_VERTICAL} here
+         * to control the behavior of
+         * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect)
+         * Gravity.applyDisplay}.
          *
          * @see Gravity
          */
@@ -802,13 +812,19 @@
     
         /**
          * The horizontal margin, as a percentage of the container's width,
-         * between the container and the widget.
+         * between the container and the widget.  See
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} for how this is used.  This
+         * field is added with {@link #x} to supply the <var>xAdj</var> parameter.
          */
         public float horizontalMargin;
     
         /**
          * The vertical margin, as a percentage of the container's height,
-         * between the container and the widget.
+         * between the container and the widget.  See
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} for how this is used.  This
+         * field is added with {@link #y} to supply the <var>yAdj</var> parameter.
          */
         public float verticalMargin;
     
@@ -1168,14 +1184,22 @@
             sb.append('x');
             sb.append((height== MATCH_PARENT ?"fill":(height==WRAP_CONTENT?"wrap":height)));
             sb.append(")");
-            if (softInputMode != 0) {
-                sb.append(" sim=#");
-                sb.append(Integer.toHexString(softInputMode));
+            if (horizontalMargin != 0) {
+                sb.append(" hm=");
+                sb.append(horizontalMargin);
+            }
+            if (verticalMargin != 0) {
+                sb.append(" vm=");
+                sb.append(verticalMargin);
             }
             if (gravity != 0) {
                 sb.append(" gr=#");
                 sb.append(Integer.toHexString(gravity));
             }
+            if (softInputMode != 0) {
+                sb.append(" sim=#");
+                sb.append(Integer.toHexString(softInputMode));
+            }
             sb.append(" ty=");
             sb.append(type);
             sb.append(" fl=#");
@@ -1190,6 +1214,18 @@
                 sb.append(" or=");
                 sb.append(screenOrientation);
             }
+            if (alpha != 1.0f) {
+                sb.append(" alpha=");
+                sb.append(alpha);
+            }
+            if (screenBrightness != BRIGHTNESS_OVERRIDE_NONE) {
+                sb.append(" sbrt=");
+                sb.append(screenBrightness);
+            }
+            if (buttonBrightness != BRIGHTNESS_OVERRIDE_NONE) {
+                sb.append(" bbrt=");
+                sb.append(buttonBrightness);
+            }
             if ((flags & FLAG_COMPATIBLE_WINDOW) != 0) {
                 sb.append(" compatible=true");
             }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1fd31a3..1a341e1 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -544,16 +544,18 @@
      * Generally, it's best to keep as little as possible in the queue thread
      * because it's the most fragile.
      * @param whenNanos The event time in uptime nanoseconds.
+     * @param action The key event action.
+     * @param flags The key event flags.
      * @param keyCode The key code.
-     * @param down True if the key is down.
+     * @param scanCode The key's scan code.
      * @param policyFlags The policy flags associated with the key.
      * @param isScreenOn True if the screen is already on
      *
      * @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
      *          {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
      */
-    public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags,
-            boolean isScreenOn);
+    public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+            int keyCode, int scanCode, int policyFlags, boolean isScreenOn);
     
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
@@ -571,6 +573,7 @@
      * @param action The key event action.
      * @param flags The key event flags.
      * @param keyCode The key code.
+     * @param scanCode The key's scan code.
      * @param metaState bit mask of meta keys that are held.
      * @param repeatCount Number of times a key down has repeated.
      * @param policyFlags The policy flags associated with the key.
@@ -578,7 +581,7 @@
      * not be further dispatched.
      */
     public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
-            int keyCode, int metaState, int repeatCount, int policyFlags);
+            int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
 
     /**
      * Called when layout of the windows is about to start.
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 3bcd483a4..14dc61d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -370,6 +370,7 @@
             int candEnd = EditableInputConnection.getComposingSpanEnd(sp);
             imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
         }
+        updateCursorControllerPositions();
     }
 
     @Override
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 72b4e36..f5affe5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3477,7 +3477,9 @@
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
             invalidate();
         }
-        if (inEditingMode()) mWebTextView.onDrawSubstitute();
+        if (inEditingMode()) {
+            mWebTextView.onDrawSubstitute();
+        }
         mWebViewCore.signalRepaintDone();
 
         // paint the highlight in the end
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 9a873b6..9edb267 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -472,6 +472,11 @@
      */
     private native int nativeRecordContent(Region invalRegion, Point wh);
 
+    /**
+     * Update the layers' content
+     */
+    private native int nativeUpdateLayers();
+
     private native boolean nativeFocusBoundsChanged();
 
     /**
@@ -798,6 +803,7 @@
             "FREE_MEMORY",  // = 145
             "VALID_NODE_BOUNDS", // = 146
             "SAVE_WEBARCHIVE", // = 147
+            "WEBKIT_DRAW_LAYERS", // = 148;
         };
 
     class EventHub {
@@ -868,6 +874,9 @@
         // Load and save web archives
         static final int SAVE_WEBARCHIVE = 147;
 
+        // Update layers
+        static final int WEBKIT_DRAW_LAYERS = 148;
+
         // Network-based messaging
         static final int CLEAR_SSL_PREF_TABLE = 150;
 
@@ -953,6 +962,10 @@
                             webkitDraw();
                             break;
 
+                        case WEBKIT_DRAW_LAYERS:
+                            webkitDrawLayers();
+                            break;
+
                         case DESTROY:
                             // Time to take down the world. Cancel all pending
                             // loads and destroy the native view and frame.
@@ -1800,6 +1813,7 @@
 
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
+    private boolean mDrawLayersIsScheduled;
 
     // Used to avoid posting more than one split picture message.
     private boolean mSplitPictureIsScheduled;
@@ -1839,6 +1853,20 @@
         boolean mFocusSizeChanged;
     }
 
+    // Only update the layers' content, not the base surface
+    // PictureSet.
+    private void webkitDrawLayers() {
+        mDrawLayersIsScheduled = false;
+        if (mDrawIsScheduled) {
+            removeMessages(EventHub.WEBKIT_DRAW);
+            webkitDraw();
+            return;
+        }
+        DrawData draw = new DrawData();
+        draw.mBaseLayer = nativeUpdateLayers();
+        webkitDraw(draw);
+    }
+
     private void webkitDraw() {
         mDrawIsScheduled = false;
         DrawData draw = new DrawData();
@@ -1848,6 +1876,10 @@
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
             return;
         }
+        webkitDraw(draw);
+    }
+
+    private void webkitDraw(DrawData draw) {
         if (mWebView != null) {
             draw.mFocusSizeChanged = nativeFocusBoundsChanged();
             draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
@@ -1957,6 +1989,15 @@
         }
     }
 
+    // called from JNI
+    void layersDraw() {
+        synchronized (this) {
+            if (mDrawLayersIsScheduled) return;
+            mDrawLayersIsScheduled = true;
+            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS));
+        }
+    }
+
     // called by JNI
     private void contentScrollBy(int dx, int dy, boolean animate) {
         if (!mBrowserFrame.firstLayoutDone()) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 39b1377..be1234d 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -413,33 +413,46 @@
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         int keyCode = event.getKeyCode();
-        if (event.getRepeatCount() == 0 && event.isDown() && (
-                keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK ||
-                keyCode ==  KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                keyCode ==  KeyEvent.KEYCODE_SPACE)) {
-            doPauseResume();
-            show(sDefaultTimeout);
-            if (mPauseButton != null) {
-                mPauseButton.requestFocus();
+        final boolean uniqueDown = event.getRepeatCount() == 0
+                && event.getAction() == KeyEvent.ACTION_DOWN;
+        if (keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                || keyCode == KeyEvent.KEYCODE_SPACE) {
+            if (uniqueDown) {
+                doPauseResume();
+                show(sDefaultTimeout);
+                if (mPauseButton != null) {
+                    mPauseButton.requestFocus();
+                }
             }
             return true;
-        } else if (keyCode ==  KeyEvent.KEYCODE_MEDIA_STOP) {
-            if (mPlayer.isPlaying()) {
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+            if (uniqueDown && !mPlayer.isPlaying()) {
+                mPlayer.start();
+                updatePausePlay();
+                show(sDefaultTimeout);
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            if (uniqueDown && mPlayer.isPlaying()) {
                 mPlayer.pause();
                 updatePausePlay();
+                show(sDefaultTimeout);
             }
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
-                keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
             // don't show the controls for volume adjustment
             return super.dispatchKeyEvent(event);
         } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
-            hide();
-
+            if (uniqueDown) {
+                hide();
+            }
             return true;
-        } else {
-            show(sDefaultTimeout);
         }
+
+        show(sDefaultTimeout);
         return super.dispatchKeyEvent(event);
     }
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 35c50fd..6ba7b44 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -132,6 +132,25 @@
             // here
             return;
         }
+
+        protected boolean startIntentSafely(Context context, PendingIntent pendingIntent,
+                Intent fillInIntent) {
+            try {
+                // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+                context.startIntentSender(
+                        pendingIntent.getIntentSender(), fillInIntent,
+                        Intent.FLAG_ACTIVITY_NEW_TASK,
+                        Intent.FLAG_ACTIVITY_NEW_TASK, 0);
+            } catch (IntentSender.SendIntentException e) {
+                android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
+                return false;
+            } catch (Exception e) {
+                android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
+                        "unknown exception: ", e);
+                return false;
+            }
+            return true;
+        }
     }
 
     private class SetEmptyView extends Action {
@@ -236,15 +255,7 @@
                         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
 
                         fillInIntent.setSourceBounds(rect);
-                        try {
-                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
-                            v.getContext().startIntentSender(
-                                    pendingIntent.getIntentSender(), fillInIntent,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0);
-                        } catch (IntentSender.SendIntentException e) {
-                            android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
-                        }
+                        startIntentSafely(v.getContext(), pendingIntent, fillInIntent);
                     }
 
                 };
@@ -326,16 +337,7 @@
                         final Intent intent = new Intent();
                         intent.setSourceBounds(rect);
                         intent.putExtras(extras);
-
-                        try {
-                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
-                            v.getContext().startIntentSender(
-                                    pendingIntent.getIntentSender(), intent,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0);
-                        } catch (IntentSender.SendIntentException e) {
-                            android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
-                        }
+                        startIntentSafely(v.getContext(), pendingIntent, intent);
                     }
 
                 };
@@ -441,15 +443,7 @@
 
                         final Intent intent = new Intent();
                         intent.setSourceBounds(rect);
-                        try {
-                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
-                            v.getContext().startIntentSender(
-                                    pendingIntent.getIntentSender(), intent,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0);
-                        } catch (IntentSender.SendIntentException e) {
-                            android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
-                        }
+                        startIntentSafely(v.getContext(), pendingIntent, intent);
                     }
                 };
                 target.setOnClickListener(listener);
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 36adacd..ce4e8e5 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -115,7 +115,6 @@
     }
 
     private void initTabWidget() {
-        setOrientation(LinearLayout.HORIZONTAL);
         mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER;
 
         final Context context = mContext;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9855d6e..2fcae1c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4277,6 +4277,15 @@
 
         canvas.restore();
 
+        updateCursorControllerPositions();
+    }
+
+    /**
+     * Update the positions of the CursorControllers.  Needed by WebTextView,
+     * which does not draw.
+     * @hide
+     */
+    protected void updateCursorControllerPositions() {
         if (mInsertionPointCursorController != null &&
                 mInsertionPointCursorController.isShowing()) {
             mInsertionPointCursorController.updatePosition();
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 3d9cde4..2be7bca 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -529,10 +529,19 @@
                     mMediaController.hide();
                 }
                 return true;
+            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+                if (mMediaPlayer.isPlaying()) {
+                    start();
+                    mMediaController.hide();
+                }
+                return true;
             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                    && mMediaPlayer.isPlaying()) {
-                pause();
-                mMediaController.show();
+                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+                if (!mMediaPlayer.isPlaying()) {
+                    pause();
+                    mMediaController.show();
+                }
+                return true;
             } else {
                 toggleMediaControlsVisiblity();
             }
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
index 7a9a3b9..edf1948 100644
--- a/core/res/res/anim/fragment_close_enter.xml
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -26,7 +26,7 @@
         android:duration="@android:integer/config_mediumAnimTime"/>
     <objectAnimator
         android:interpolator="@anim/decelerate_interpolator"
-        android:valueFrom="-400"
+        android:valueFrom="-100"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="translationX"
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
index 07435776..fbba476 100644
--- a/core/res/res/anim/fragment_close_exit.xml
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -27,7 +27,7 @@
     <objectAnimator
         android:interpolator="@anim/accelerate_interpolator"
         android:valueFrom="0"
-        android:valueTo="400"
+        android:valueTo="100"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
index ac60494..334f4ef 100644
--- a/core/res/res/anim/fragment_open_enter.xml
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -24,7 +24,7 @@
         android:propertyName="alpha"
         android:duration="@android:integer/config_mediumAnimTime"/>
     <objectAnimator
-        android:valueFrom="400"
+        android:valueFrom="100"
         android:valueTo="0"
         android:valueType="floatType"
         android:propertyName="translationX"
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
index 3bf1ad4..764fc39 100644
--- a/core/res/res/anim/fragment_open_exit.xml
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -25,7 +25,7 @@
         android:duration="@android:integer/config_mediumAnimTime"/>
     <objectAnimator
         android:valueFrom="0"
-        android:valueTo="-400"
+        android:valueTo="-100"
         android:valueType="floatType"
         android:propertyName="translationX"
         android:duration="@android:integer/config_mediumAnimTime"/>
diff --git a/core/res/res/layout/tab_content.xml b/core/res/res/layout/tab_content.xml
index 0ee87ce..79147fb 100644
--- a/core/res/res/layout/tab_content.xml
+++ b/core/res/res/layout/tab_content.xml
@@ -22,8 +22,9 @@
 	android:layout_width="match_parent" android:layout_height="match_parent">
 	<LinearLayout android:orientation="vertical"
     	android:layout_width="match_parent" android:layout_height="match_parent">
-        <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent"
-        	android:layout_height="wrap_content" android:layout_weight="0" />
+    <TabWidget android:id="@android:id/tabs"
+        android:orientation="horizontal" android:layout_width="match_parent"
+        android:layout_height="wrap_content" android:layout_weight="0" />
         <FrameLayout android:id="@android:id/tabcontent"
         	android:layout_width="match_parent" android:layout_height="0dip"
             android:layout_weight="1"/>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 077ce26..80072f4 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -572,7 +572,7 @@
     <style name="Theme.IconMenu">
         <!-- Menu/item attributes -->
         <item name="android:itemTextAppearance">@android:style/TextAppearance.Widget.IconMenu.Item</item>
-        <item name="android:selectableItemBackground">@android:drawable/menu_selector</item>
+        <item name="android:itemBackground">@android:drawable/menu_selector</item>
         <item name="android:itemIconDisabledAlpha">?android:attr/disabledAlpha</item>
         <item name="android:horizontalDivider">@android:drawable/divider_horizontal_dark</item>
         <item name="android:verticalDivider">@android:drawable/divider_vertical_dark</item>
diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb
new file mode 100644
index 0000000..373b8e4
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_enc_file100_orig1.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb b/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb
new file mode 100644
index 0000000..aa531d8
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_enc_file100_orig3.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_file1.obb b/core/tests/coretests/res/raw/obb_file1.obb
new file mode 100644
index 0000000..e71a680
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_file1.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_file2.obb b/core/tests/coretests/res/raw/obb_file2.obb
new file mode 100644
index 0000000..1c397df
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_file2.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_file2_nosign.obb b/core/tests/coretests/res/raw/obb_file2_nosign.obb
new file mode 100644
index 0000000..8292361
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_file2_nosign.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_file3.obb b/core/tests/coretests/res/raw/obb_file3.obb
new file mode 100644
index 0000000..7f97a88
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_file3.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb b/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb
new file mode 100644
index 0000000..baa714a
--- /dev/null
+++ b/core/tests/coretests/res/raw/obb_file3_bad_packagename.obb
Binary files differ
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 340137c..8df37ad 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -83,6 +83,7 @@
 
     protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
     protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
+    protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
 
     // Just a few popular file types used to return from a download
     protected enum DownloadFileType {
@@ -180,7 +181,7 @@
          * @return A Set<Long> with the ids of the completed downloads.
          */
         public Set<Long> getDownloadIds() {
-            synchronized(downloadIds) {
+            synchronized(this) {
                 Set<Long> returnIds = new HashSet<Long>(downloadIds);
                 return returnIds;
             }
@@ -224,6 +225,7 @@
             ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
                     Context.CONNECTIVITY_SERVICE);
             NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+            Log.i(LOG_TAG, "WiFi Connection state is currently: " + info.isConnected());
             return info.isConnected();
         }
     }
@@ -511,6 +513,7 @@
      * @param enable true if it should be enabled, false if it should be disabled
      */
     protected void setWiFiStateOn(boolean enable) throws Exception {
+        Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
         WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
 
         manager.setWifiEnabled(enable);
@@ -528,7 +531,7 @@
 
             while (receiver.getWiFiIsOn() != enable && !timedOut) {
                 try {
-                    receiver.wait(DEFAULT_MAX_WAIT_TIME);
+                    receiver.wait(DEFAULT_WAIT_POLL_TIME);
 
                     if (SystemClock.elapsedRealtime() > timeoutTime) {
                         timedOut = true;
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index ddf138f..e0b28d0 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -24,7 +24,6 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
 
@@ -38,6 +37,7 @@
     public void setUp() throws Exception {
         super.setUp();
         mServer.play(0);
+        setWiFiStateOn(true);
         removeAllCurrentDownloads();
     }
 
@@ -71,7 +71,8 @@
             }
 
             // wait for the download to complete or timeout
-            waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
+            waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME,
+                    MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME);
             cursor = mDownloadManager.query(new Query());
             assertEquals(NUM_FILES, cursor.getCount());
             Log.i(LOG_TAG, "Verified number of downloads in download manager is what we expect.");
@@ -130,10 +131,11 @@
     }
 
     /**
-     * Tests trying to download a large file (~300M bytes) when there's not enough space in cache
+     * Tests trying to download a large file (~600M bytes) when there's not enough space in cache
      */
     public void testInsufficientSpace() throws Exception {
-        long fileSize = 300000000L;
+        // @TODO: Rework this to fill up cache partition with a dynamically calculated size
+        long fileSize = 600000000L;
         File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
 
         Cursor cursor = null;
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
new file mode 100644
index 0000000..90cb9a5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.storage.OnObbStateChangeListener;
+import android.os.storage.StorageManager;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.StringReader;
+
+public class StorageManagerBaseTest extends InstrumentationTestCase {
+
+    protected Context mContext = null;
+    protected StorageManager mSm = null;
+    private static String LOG_TAG = "StorageManagerBaseTest";
+    protected static final long MAX_WAIT_TIME = 120*1000;
+    protected static final long WAIT_TIME_INCR = 5*1000;
+    protected static String OBB_FILE_1 = "obb_file1.obb";
+    protected static String OBB_FILE_1_CONTENTS_1 = "OneToOneThousandInts.bin";
+    protected static String OBB_FILE_2 = "obb_file2.obb";
+    protected static String OBB_FILE_3 = "obb_file3.obb";
+    protected static String OBB_FILE_1_PASSWORD = "password1";
+    protected static String OBB_FILE_1_ENCRYPTED = "obb_enc_file100_orig1.obb";
+    protected static String OBB_FILE_2_UNSIGNED = "obb_file2_nosign.obb";
+    protected static String OBB_FILE_3_PASSWORD = "password3";
+    protected static String OBB_FILE_3_ENCRYPTED = "obb_enc_file100_orig3.obb";
+    protected static String OBB_FILE_3_BAD_PACKAGENAME = "obb_file3_bad_packagename.obb";
+
+    protected static boolean FORCE = true;
+    protected static boolean DONT_FORCE = false;
+
+    private static final String SAMPLE1_TEXT = "This is sample text.\n\nTesting 1 2 3.";
+
+    private static final String SAMPLE2_TEXT =
+        "We the people of the United States, in order to form a more perfect union,\n"
+        + "establish justice, insure domestic tranquility, provide for the common\n"
+        + "defense, promote the general welfare, and secure the blessings of liberty\n"
+        + "to ourselves and our posterity, do ordain and establish this Constitution\n"
+        + "for the United States of America.\n\n";
+
+    class MountingObbThread extends Thread {
+        boolean mStop = false;
+        volatile boolean mFileOpenOnObb = false;
+        private String mObbFilePath = null;
+        private String mPathToContentsFile = null;
+        private String mOfficialObbFilePath = null;
+
+        /**
+         * Constructor
+         *
+         * @param obbFilePath path to the OBB image file
+         * @param pathToContentsFile path to a file on the mounted OBB volume to open after the OBB
+         *      has been mounted
+         */
+        public MountingObbThread (String obbFilePath, String pathToContentsFile) {
+            assertTrue("obbFilePath cannot be null!", obbFilePath != null);
+            mObbFilePath = obbFilePath;
+            assertTrue("path to contents file cannot be null!", pathToContentsFile != null);
+            mPathToContentsFile = pathToContentsFile;
+        }
+
+        /**
+         * Runs the thread
+         *
+         * Mounts OBB_FILE_1, and tries to open a file on the mounted OBB (specified in the
+         * constructor). Once it's open, it waits until someone calls its doStop(), after which it
+         * closes the opened file.
+         */
+        public void run() {
+            // the official OBB file path and the mount-request file path should be the same, but
+            // let's distinguish the two as they may make for some interesting tests later
+            mOfficialObbFilePath = mountObb(mObbFilePath);
+            assertEquals("Expected and actual OBB file paths differ!", mObbFilePath,
+                    mOfficialObbFilePath);
+
+            // open a file on OBB 1...
+            DataInputStream inputFile = openFileOnMountedObb(mOfficialObbFilePath,
+                    mPathToContentsFile);
+            assertTrue("Failed to open file!", inputFile != null);
+
+            synchronized (this) {
+                mFileOpenOnObb = true;
+                notifyAll();
+            }
+
+            while (!mStop) {
+                try {
+                    Thread.sleep(WAIT_TIME_INCR);
+                } catch (InterruptedException e) {
+                    // nothing special to be done for interruptions
+                }
+            }
+            try {
+                inputFile.close();
+            } catch (IOException e) {
+                fail("Failed to close file on OBB due to error: " + e.toString());
+            }
+        }
+
+        /**
+         * Tells whether a file has yet been successfully opened on the OBB or not
+         *
+         * @return true if the specified file on the OBB was opened; false otherwise
+         */
+        public boolean isFileOpenOnObb() {
+            return mFileOpenOnObb;
+        }
+
+        /**
+         * Returns the official path of the OBB file that was mounted
+         *
+         * This is not the mount path, but the normalized path to the actual OBB file
+         *
+         * @return a {@link String} representation of the path to the OBB file that was mounted
+         */
+        public String officialObbFilePath() {
+            return mOfficialObbFilePath;
+        }
+
+        /**
+         * Requests the thread to stop running
+         *
+         * Closes the opened file and returns
+         */
+        public void doStop() {
+            mStop = true;
+        }
+    }
+
+    public class ObbListener extends OnObbStateChangeListener {
+        private String LOG_TAG = "StorageManagerBaseTest.ObbListener";
+
+        String mOfficialPath = null;
+        boolean mDone = false;
+        int mState = -1;
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void onObbStateChange(String path, int state) {
+            Log.i(LOG_TAG, "Storage state changing to: " + state);
+
+            synchronized (this) {
+                Log.i(LOG_TAG, "OfficialPath is now: " + path);
+                mState = state;
+                mOfficialPath = path;
+                mDone = true;
+                notifyAll();
+            }
+        }
+
+        /**
+         * Tells whether we are done or not (system told us the OBB has changed state)
+         *
+         * @return true if the system has told us this OBB's state has changed, false otherwise
+         */
+        public boolean isDone() {
+            return mDone;
+        }
+
+        /**
+         * The last state of the OBB, according to the system
+         *
+         * @return A {@link String} representation of the state of the OBB
+         */
+        public int state() {
+            return mState;
+        }
+
+        /**
+         * The normalized, official path to the OBB file (according to the system)
+         *
+         * @return A {@link String} representation of the official path to the OBB file
+         */
+        public String officialPath() {
+            return mOfficialPath;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE);
+
+    }
+
+    /**
+     * Helper to copy a raw resource file to an actual specified file
+     *
+     * @param rawResId The raw resource ID of the OBB resource file
+     * @param outFile A File representing the file we want to copy the OBB to
+     * @throws NotFoundException If the resource file could not be found
+     */
+    private void copyRawToFile(int rawResId, File outFile) throws NotFoundException {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(rawResId);
+        } catch (NotFoundException e) {
+            Log.i(LOG_TAG, "Failed to load resource with id: " + rawResId);
+            throw e;
+        }
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO, -1, -1);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO, -1, -1);
+    }
+
+    /**
+     * Creates an OBB file (with the given name), into the app's standard files directory
+     *
+     * @param name The name of the OBB file we want to create/write to
+     * @param rawResId The raw resource ID of the OBB file in the package
+     * @return A {@link File} representing the file to write to
+     */
+    protected File createObbFile(String name, int rawResId) {
+        File outFile = null;
+        try {
+            final File filesDir = mContext.getFilesDir();
+            outFile = new File(filesDir, name);
+            copyRawToFile(rawResId, outFile);
+        } catch (NotFoundException e) {
+            if (outFile != null) {
+                outFile.delete();
+            }
+        }
+        return outFile;
+    }
+
+    /**
+     * Mounts an OBB file and opens a file located on it
+     *
+     * @param obbPath Path to OBB image
+     * @param fileName The full name and path to the file on the OBB to open once the OBB is mounted
+     * @return The {@link DataInputStream} representing the opened file, if successful in opening
+     *      the file, or null of unsuccessful.
+     */
+    protected DataInputStream openFileOnMountedObb(String obbPath, String fileName) {
+
+        // get mSm obb mount path
+        assertTrue("Cannot open file when OBB is not mounted!", mSm.isObbMounted(obbPath));
+
+        String path = mSm.getMountedObbPath(obbPath);
+        assertTrue("Path should not be null!", path != null);
+
+        File inFile = new File(path, fileName);
+        DataInputStream inStream = null;
+        try {
+            inStream = new DataInputStream(new FileInputStream(inFile));
+            Log.i(LOG_TAG, "Opened file: " + fileName + " for read at path: " + path);
+        } catch (FileNotFoundException e) {
+            Log.e(LOG_TAG, e.toString());
+            return null;
+        } catch (SecurityException e) {
+            Log.e(LOG_TAG, e.toString());
+            return null;
+        }
+        return inStream;
+    }
+
+    /**
+     * Mounts an OBB file
+     *
+     * @param obbFilePath The full path to the OBB file to mount
+     * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
+     * @param expectedState The expected state resulting from trying to mount the OBB
+     * @return A {@link String} representing the normalized path to OBB file that was mounted
+     */
+    protected String mountObb(String obbFilePath, String key, int expectedState) {
+        return doMountObb(obbFilePath, key, expectedState);
+    }
+
+    /**
+     * Mounts an OBB file with default options (no encryption, mounting succeeds)
+     *
+     * @param obbFilePath The full path to the OBB file to mount
+     * @return A {@link String} representing the normalized path to OBB file that was mounted
+     */
+    protected String mountObb(String obbFilePath) {
+        return doMountObb(obbFilePath, null, OnObbStateChangeListener.MOUNTED);
+    }
+
+    /**
+     * Synchronously waits for an OBB listener to be signaled of a state change, but does not throw
+     *
+     * @param obbListener The listener for the OBB file
+     * @return true if the listener was signaled of a state change by the system, else returns
+     *      false if we time out.
+     */
+    protected boolean doWaitForObbStateChange(ObbListener obbListener) {
+        synchronized(obbListener) {
+            long waitTimeMillis = 0;
+            while (!obbListener.isDone()) {
+                try {
+                    Log.i(LOG_TAG, "Waiting for listener...");
+                    obbListener.wait(WAIT_TIME_INCR);
+                    Log.i(LOG_TAG, "Awoke from waiting for listener...");
+                    waitTimeMillis += WAIT_TIME_INCR;
+                    if (waitTimeMillis > MAX_WAIT_TIME) {
+                        fail("Timed out waiting for OBB state to change!");
+                    }
+                } catch (InterruptedException e) {
+                    Log.i(LOG_TAG, e.toString());
+                }
+            }
+            return obbListener.isDone();
+            }
+    }
+
+    /**
+     * Synchronously waits for an OBB listener to be signaled of a state change
+     *
+     * @param obbListener The listener for the OBB file
+     * @return true if the listener was signaled of a state change by the system; else a fail()
+     *      is triggered if we timed out
+     */
+    protected String doMountObb_noThrow(String obbFilePath, String key, int expectedState) {
+        Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
+        assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
+        assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
+
+        ObbListener obbListener = new ObbListener();
+        boolean success = mSm.mountObb(obbFilePath, key, obbListener);
+        success &= obbFilePath.equals(doWaitForObbStateChange(obbListener));
+        success &= (expectedState == obbListener.state());
+
+        if (OnObbStateChangeListener.MOUNTED == expectedState) {
+            success &= obbFilePath.equals(obbListener.officialPath());
+            success &= mSm.isObbMounted(obbListener.officialPath());
+        } else {
+            success &= !mSm.isObbMounted(obbListener.officialPath());
+        }
+
+        if (success) {
+            return obbListener.officialPath();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Mounts an OBB file without throwing and synchronously waits for it to finish mounting
+     *
+     * @param obbFilePath The full path to the OBB file to mount
+     * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
+     * @param expectedState The expected state resulting from trying to mount the OBB
+     * @return A {@link String} representing the actual normalized path to OBB file that was
+     *      mounted, or null if the mounting failed
+     */
+    protected String doMountObb(String obbFilePath, String key, int expectedState) {
+        Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
+        assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
+
+        ObbListener obbListener = new ObbListener();
+        assertTrue("mountObb call failed", mSm.mountObb(obbFilePath, key, obbListener));
+        assertTrue("Failed to get OBB mount status change for file: " + obbFilePath,
+                doWaitForObbStateChange(obbListener));
+        assertEquals("OBB mount state not what was expected!", expectedState, obbListener.state());
+
+        if (OnObbStateChangeListener.MOUNTED == expectedState) {
+            assertEquals(obbFilePath, obbListener.officialPath());
+            assertTrue("Obb should be mounted, but SM reports it is not!",
+                    mSm.isObbMounted(obbListener.officialPath()));
+        } else if (OnObbStateChangeListener.UNMOUNTED == expectedState) {
+            assertFalse("Obb should not be mounted, but SM reports it is!",
+                    mSm.isObbMounted(obbListener.officialPath()));
+        }
+
+        assertEquals("Mount state is not what was expected!", expectedState, obbListener.state());
+        return obbListener.officialPath();
+    }
+
+    /**
+     * Unmounts an OBB file without throwing, and synchronously waits for it to finish unmounting
+     *
+     * @param obbFilePath The full path to the OBB file to mount
+     * @param force true if we shuold force the unmount, false otherwise
+     * @return true if the unmount was successful, false otherwise
+     */
+    protected boolean unmountObb_noThrow(String obbFilePath, boolean force) {
+        Log.i(LOG_TAG, "doUnmountObb_noThrow() on " + obbFilePath);
+        assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
+        boolean success = true;
+
+        ObbListener obbListener = new ObbListener();
+        assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener));
+
+        boolean stateChanged = doWaitForObbStateChange(obbListener);
+        if (force) {
+            success &= stateChanged;
+            success &= (OnObbStateChangeListener.UNMOUNTED == obbListener.state());
+            success &= !mSm.isObbMounted(obbFilePath);
+        }
+        return success;
+    }
+
+    /**
+     * Unmounts an OBB file and synchronously waits for it to finish unmounting
+     *
+     * @param obbFilePath The full path to the OBB file to mount
+     * @param force true if we shuold force the unmount, false otherwise
+     */
+    protected void unmountObb(String obbFilePath, boolean force) {
+        Log.i(LOG_TAG, "doUnmountObb() on " + obbFilePath);
+        assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
+
+        ObbListener obbListener = new ObbListener();
+        assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener));
+
+        boolean stateChanged = doWaitForObbStateChange(obbListener);
+        if (force) {
+            assertTrue("Timed out waiting to unmount OBB file " + obbFilePath, stateChanged);
+            assertEquals("OBB failed to unmount", OnObbStateChangeListener.UNMOUNTED,
+                    obbListener.state());
+            assertFalse("Obb should NOT be mounted, but SM reports it is!", mSm.isObbMounted(
+                    obbFilePath));
+        }
+    }
+
+    /**
+     * Helper to validate the contents of an "int" file in an OBB.
+     *
+     * The format of the files are sequential int's, in the range of: [start..end)
+     *
+     * @param path The full path to the file (path to OBB)
+     * @param filename The filename containing the ints to validate
+     * @param start The first int expected to be found in the file
+     * @param end The last int + 1 expected to be found in the file
+     */
+    protected void doValidateIntContents(String path, String filename, int start, int end) {
+        File inFile = new File(path, filename);
+        DataInputStream inStream = null;
+        Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
+        try {
+            inStream = new DataInputStream(new FileInputStream(inFile));
+
+            for (int i = start; i < end; ++i) {
+                if (inStream.readInt() != i) {
+                    fail("Unexpected value read in OBB file");
+                }
+            }
+            if (inStream != null) {
+                inStream.close();
+            }
+            Log.i(LOG_TAG, "Successfully validated file " + filename);
+        } catch (FileNotFoundException e) {
+            fail("File " + inFile + " not found: " + e.toString());
+        } catch (IOException e) {
+            fail("IOError with file " + inFile + ":" + e.toString());
+        }
+    }
+
+    /**
+     * Helper to validate the contents of a text file in an OBB
+     *
+     * @param path The full path to the file (path to OBB)
+     * @param filename The filename containing the ints to validate
+     * @param contents A {@link String} containing the expected contents of the file
+     */
+    protected void doValidateTextContents(String path, String filename, String contents) {
+        File inFile = new File(path, filename);
+        BufferedReader fileReader = null;
+        BufferedReader textReader = null;
+        Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
+        try {
+            fileReader = new BufferedReader(new FileReader(inFile));
+            textReader = new BufferedReader(new StringReader(contents));
+            String actual = null;
+            String expected = null;
+            while ((actual = fileReader.readLine()) != null) {
+                expected = textReader.readLine();
+                if (!actual.equals(expected)) {
+                    fail("File " + filename + " in OBB " + path + " does not match expected value");
+                }
+            }
+            fileReader.close();
+            textReader.close();
+            Log.i(LOG_TAG, "File " + filename + " successfully verified.");
+        } catch (IOException e) {
+            fail("IOError with file " + inFile + ":" + e.toString());
+        }
+    }
+
+    /**
+     * Helper to validate the contents of a "long" file on our OBBs
+     *
+     * The format of the files are sequential 0's of type long
+     *
+     * @param path The full path to the file (path to OBB)
+     * @param filename The filename containing the ints to validate
+     * @param size The number of zero's expected in the file
+     * @param checkContents If true, the contents of the file are actually verified; if false,
+     *      we simply verify that the file can be opened
+     */
+    protected void doValidateZeroLongFile(String path, String filename, long size,
+            boolean checkContents) {
+        File inFile = new File(path, filename);
+        DataInputStream inStream = null;
+        Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
+        try {
+            inStream = new DataInputStream(new FileInputStream(inFile));
+
+            if (checkContents) {
+                for (long i = 0; i < size; ++i) {
+                    if (inStream.readLong() != 0) {
+                        fail("Unexpected value read in OBB file" + filename);
+                    }
+                }
+            }
+
+            if (inStream != null) {
+                inStream.close();
+            }
+            Log.i(LOG_TAG, "File " + filename + " successfully verified for " + size + " zeros");
+        } catch (IOException e) {
+            fail("IOError with file " + inFile + ":" + e.toString());
+        }
+    }
+
+    /**
+     * Helper to synchronously wait until we can get a path for a given OBB file
+     *
+     * @param filePath The full normalized path to the OBB file
+     * @return The mounted path of the OBB, used to access contents in it
+     */
+    protected String doWaitForPath(String filePath) {
+        String path = null;
+
+        long waitTimeMillis = 0;
+        assertTrue("OBB " + filePath + " is not currently mounted!", mSm.isObbMounted(filePath));
+        while (path == null) {
+            try {
+                Thread.sleep(WAIT_TIME_INCR);
+                waitTimeMillis += WAIT_TIME_INCR;
+                if (waitTimeMillis > MAX_WAIT_TIME) {
+                    fail("Timed out waiting to get path of OBB file " + filePath);
+                }
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+            path = mSm.getMountedObbPath(filePath);
+        }
+        Log.i(LOG_TAG, "Got OBB path: " + path);
+        return path;
+    }
+
+    /**
+     * Verifies the pre-defined contents of our first OBB (OBB_FILE_1)
+     *
+     * The OBB contains 4 files and no subdirectories
+     *
+     * @param filePath The normalized path to the already-mounted OBB file
+     */
+    protected void verifyObb1Contents(String filePath) {
+        String path = null;
+        path = doWaitForPath(filePath);
+
+        // Validate contents of 2 files in this obb
+        doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000);
+        doValidateIntContents(path, "SevenHundredInts.bin", 0, 700);
+        doValidateZeroLongFile(path, "FiveLongs.bin", 5, true);
+    }
+
+    /**
+     * Verifies the pre-defined contents of our second OBB (OBB_FILE_2)
+     *
+     * The OBB contains 2 files and no subdirectories
+     *
+     * @param filePath The normalized path to the already-mounted OBB file
+     */
+    protected void verifyObb2Contents(String filename) {
+        String path = null;
+        path = doWaitForPath(filename);
+
+        // Validate contents of file
+        doValidateTextContents(path, "sample.txt", SAMPLE1_TEXT);
+        doValidateTextContents(path, "sample2.txt", SAMPLE2_TEXT);
+    }
+
+    /**
+     * Verifies the pre-defined contents of our third OBB (OBB_FILE_3)
+     *
+     * The OBB contains nested files and subdirectories
+     *
+     * @param filePath The normalized path to the already-mounted OBB file
+     */
+    protected void verifyObb3Contents(String filename) {
+        String path = null;
+        path = doWaitForPath(filename);
+
+        // Validate contents of file
+        doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000);
+        doValidateZeroLongFile(path, "TwoHundredLongs", 200, true);
+
+        // validate subdirectory 1
+        doValidateZeroLongFile(path + File.separator + "subdir1", "FiftyLongs", 50, true);
+
+        // validate subdirectory subdir2/
+        doValidateIntContents(path + File.separator + "subdir2", "OneToOneThousandInts", 0, 1000);
+
+        // validate subdirectory subdir2/subdir2a/
+        doValidateZeroLongFile(path + File.separator + "subdir2" + File.separator + "subdir2a",
+                "TwoHundredLongs", 200, true);
+
+        // validate subdirectory subdir2/subdir2a/subdir2a1/
+        doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a"
+                + File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000);
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
new file mode 100644
index 0000000..71772d9
--- /dev/null
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.content.Context;
+import android.os.Environment;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+
+import junit.framework.AssertionFailedError;
+
+public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
+
+    private static String LOG_TAG = "StorageManagerBaseTest.StorageManagerIntegrationTest";
+    protected File mFile = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        mFile = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        if (mFile != null) {
+            mFile.delete();
+            mFile = null;
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Tests mounting a single OBB file and verifies its contents.
+     */
+    @LargeTest
+    public void testMountSingleObb() {
+        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath);
+        verifyObb1Contents(filePath);
+        unmountObb(filePath, DONT_FORCE);
+    }
+
+    /**
+     * Tests mounting several OBB files and verifies its contents.
+     */
+    @LargeTest
+    public void testMountMultipleObb() {
+        File file1 = null;
+        File file2 = null;
+        File file3 = null;
+        try {
+            file1 = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+            String filePath1 = file1.getAbsolutePath();
+            mountObb(filePath1);
+            verifyObb1Contents(filePath1);
+
+            file2 = createObbFile(OBB_FILE_2, R.raw.obb_file2);
+            String filePath2 = file2.getAbsolutePath();
+            mountObb(filePath2);
+            verifyObb2Contents(filePath2);
+
+            file3 = createObbFile(OBB_FILE_3, R.raw.obb_file3);
+            String filePath3 = file3.getAbsolutePath();
+            mountObb(filePath3);
+            verifyObb3Contents(filePath3);
+
+            unmountObb(filePath1, DONT_FORCE);
+            unmountObb(filePath2, DONT_FORCE);
+            unmountObb(filePath3, DONT_FORCE);
+        } finally {
+            if (file1 != null) {
+                file1.delete();
+            }
+            if (file2 != null) {
+                file2.delete();
+            }
+            if (file3 != null) {
+                file3.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests mounting a single encrypted OBB file and verifies its contents.
+     */
+    @LargeTest
+    public void testMountSingleEncryptedObb() {
+        mFile = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
+        verifyObb3Contents(filePath);
+        unmountObb(filePath, DONT_FORCE);
+    }
+
+    /**
+     * Tests mounting a single encrypted OBB file using an invalid password.
+     */
+    @LargeTest
+    public void testMountSingleEncryptedObbInvalidPassword() {
+        mFile = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
+        unmountObb(filePath, DONT_FORCE);
+    }
+
+    /**
+     * Tests simultaneously mounting 2 encrypted OBBs with different keys and verifies contents.
+     */
+    @LargeTest
+    public void testMountTwoEncryptedObb() {
+        File file3 = null;
+        File file1 = null;
+        try {
+            file3 = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
+            String filePath3 = file3.getAbsolutePath();
+            mountObb(filePath3, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
+            verifyObb3Contents(filePath3);
+
+            file1 = createObbFile(OBB_FILE_1_ENCRYPTED, R.raw.obb_enc_file100_orig1);
+            String filePath1 = file1.getAbsolutePath();
+            mountObb(filePath1, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.MOUNTED);
+            verifyObb1Contents(filePath1);
+
+            unmountObb(filePath3, DONT_FORCE);
+            unmountObb(filePath1, DONT_FORCE);
+        } finally {
+            if (file3 != null) {
+                file3.delete();
+            }
+            if (file1 != null) {
+                file1.delete();
+            }
+        }
+    }
+
+    /**
+     * Tests that we can not force unmount when a file is currently open on the OBB.
+     */
+    @LargeTest
+    public void testUnmount_DontForce() {
+        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+        String obbFilePath = mFile.getAbsolutePath();
+
+        MountingObbThread mountingThread = new MountingObbThread(obbFilePath,
+                OBB_FILE_1_CONTENTS_1);
+
+        try {
+            mountingThread.start();
+
+            long waitTime = 0;
+            while (!mountingThread.isFileOpenOnObb()) {
+                synchronized (mountingThread) {
+                    Log.i(LOG_TAG, "Waiting for file to be opened on OBB...");
+                    mountingThread.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                    if (waitTime > MAX_WAIT_TIME) {
+                        fail("Timed out waiting for file file to be opened on OBB!");
+                    }
+                }
+            }
+
+            unmountObb(obbFilePath, DONT_FORCE);
+
+            // verify still mounted
+            assertTrue("mounted path should not be null!", obbFilePath != null);
+            assertTrue("mounted path should still be mounted!", mSm.isObbMounted(obbFilePath));
+
+            // close the opened file
+            mountingThread.doStop();
+
+            // try unmounting again (should succeed this time)
+            unmountObb(obbFilePath, DONT_FORCE);
+            assertFalse("mounted path should no longer be mounted!",
+                    mSm.isObbMounted(obbFilePath));
+        } catch (InterruptedException e) {
+            fail("Timed out waiting for file on OBB to be opened...");
+        }
+    }
+
+    /**
+     * Tests mounting a single OBB that isn't signed.
+     */
+    @LargeTest
+    public void testMountUnsignedObb() {
+        mFile = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath, OBB_FILE_2_UNSIGNED, OnObbStateChangeListener.ERROR_INTERNAL);
+    }
+
+    /**
+     * Tests mounting a single OBB that is signed with a different package.
+     */
+    @LargeTest
+    public void testMountBadPackageNameObb() {
+        mFile = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath, OBB_FILE_3_BAD_PACKAGENAME,
+                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+    }
+
+    /**
+     * Tests remounting a single OBB that has already been mounted.
+     */
+    @LargeTest
+    public void testRemountObb() {
+        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+        String filePath = mFile.getAbsolutePath();
+        mountObb(filePath);
+        verifyObb1Contents(filePath);
+        mountObb(filePath, null, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+        verifyObb1Contents(filePath);
+        unmountObb(filePath, DONT_FORCE);
+    }
+}
\ No newline at end of file
diff --git a/docs/html/guide/developing/tools/MonkeyDevice.jd b/docs/html/guide/developing/tools/MonkeyDevice.jd
new file mode 100644
index 0000000..34bbba9
--- /dev/null
+++ b/docs/html/guide/developing/tools/MonkeyDevice.jd
@@ -0,0 +1,1353 @@
+page.title=MonkeyDevice
+@jd:body
+<style>
+    h4.jd-details-title {background-color: #DEE8F1;}
+</style>
+<p>
+    A monkeyrunner class that represents a device or emulator accessible by the workstation running
+<code><a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a></code>.
+</p>
+<p>
+    This class is used to control an Android device or emulator. The methods send UI events,
+    retrieve information, install and remove applications, and run applications.
+</p>
+<p>
+    You normally do not have to create an instance of <code>MonkeyDevice</code>. Instead, you
+    use
+<code><a href="{@docRoot}guide/developing/tools/MonkeyRunner.html#waitForConnection">
+MonkeyRunner.waitForConnection()</a></code> to create a new object from a connection to a device or
+emulator. For example, instead of
+using:</p>
+<pre>
+newdevice = MonkeyDevice()
+</pre>
+<p>
+    you would use:
+</p>
+<pre>
+newdevice = MonkeyRunner.waitForConnection()
+</pre>
+<h2>Summary</h2>
+    <table id="constants" class="jd-sumtable" style="background-color: white;">
+       <tr>
+            <th colspan="12" style="background-color: #E2E2E2">Constants</th>
+       </tr>
+        <tr class="api" style="background-color: white;">
+            <td class="jd-typecol"><em>string</em></td>
+            <td class="jd-linkcol"><a href="#ACTION_DOWN">DOWN</a></td>
+            <td class="jd-descrcol" width="100%">
+                Use this with the <code>type</code> argument of
+                <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a>
+                </code>
+                to send a DOWN event.
+            </td>
+        </tr>
+        <tr class="api" style="background-color: white;">
+            <td class="jd-typecol"><em>string</em></td>
+            <td class="jd-linkcol"><a href="#ACTION_UP">UP</a></td>
+            <td class="jd-descrcol" width="100%">
+                Use this with the <code>type</code> argument of
+                <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a>
+                </code>
+                to send an UP event.
+            </td>
+        </tr>
+        <tr class="api" style="background-color: white;">
+            <td class="jd-typecol"><em>string</em></td>
+            <td class="jd-linkcol"><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></td>
+            <td class="jd-descrcol" width="100%">
+                Use this with the <code>type</code> argument of
+                <code><a href="#press">press()</a></code> or <code><a href="#touch">touch()</a>
+                </code>
+                to send a DOWN event immediately followed by an UP event.
+            </td>
+        </tr>
+    </table>
+<table id="pubmethods" class="jd-sumtable">
+    <tr>
+        <th colspan="12" style="background-color: #E2E2E2">Methods</th>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#broadcastIntent">broadcastIntent</a>
+                </span>
+                (<em>string</em> uri,
+                <em>string</em> action,
+                <em>string</em> data,
+                <em>string</em> mimetype,
+                <em>iterable</em> categories
+                <em>dictionary</em> extras,
+                <em>component</em> component,
+                <em>iterable</em> flags)
+            </nobr>
+            <div class="jd-descrdiv">
+                Broadcasts an Intent to this device, as if the Intent were coming from an
+                application.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#drag">drag</a>
+                </span>
+                (<em>tuple</em> start,
+                <em>tuple</em> end,
+                <em>float</em> duration,
+                <em>integer</em> steps)
+            </nobr>
+            <div class="jd-descrdiv">
+                Simulates a drag gesture (touch, hold, and move) on this device's screen.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>object</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#getProperty">getProperty</a>
+                </span>
+                (<em>string</em> key)
+            </nobr>
+            <div class="jd-descrdiv">
+                Given the name of a system environment variable, returns its value for this device.
+                The available variable names are listed in the <a href="#getProperty">
+                detailed description</a> of this method.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>object</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#getSystemProperty">getSystemProperty</a>
+                </span>
+                (<em>string</em> key)
+            </nobr>
+            <div class="jd-descrdiv">
+.               The API equivalent of <code>adb shell getprop &lt;key&gt;. This is provided for use
+                by platform developers.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#installPackage">installPackage</a>
+                </span>
+                (<em>string</em> path)
+            </nobr>
+            <div class="jd-descrdiv">
+                Installs the Android application or test package contained in packageFile onto this
+                device. If the application or test package is already installed, it is replaced.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>dictionary</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#instrument">instrument</a>
+                </span>
+                (<em>string</em> className,
+                <em>dictionary</em> args)
+            </nobr>
+            <div class="jd-descrdiv">
+                Runs the specified component under Android instrumentation, and returns the results
+                in a dictionary whose exact format is dictated by the component being run. The
+                component must already be present on this device.
+            </div>
+        </td>
+    </tr>
+    <tr class="api">
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#press">press</a>
+                </span>
+                (<em>string</em> name,
+                <em>dictionary</em> type)
+            </nobr>
+            <div class="jd-descrdiv">
+                Sends the key event specified by type to the key specified by
+                keycode.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#reboot">reboot</a>
+                </span>
+                (<em>string</em> into)
+            </nobr>
+            <div class="jd-descrdiv">
+                Reboots this device into the bootloader specified by bootloadType.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#removePackage">removePackage</a>
+                </span>
+                (<em>string</em> package)
+            </nobr>
+            <div class="jd-descrdiv">
+                Deletes the specified package from this device, including its data and cache.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>object</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#shell">shell</a>
+                </span>
+                (<em>string</em> cmd)
+            </nobr>
+            <div class="jd-descrdiv">
+                Executes an <code>adb</code> shell command and returns the result, if any.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#startActivity">startActivity</a>
+                </span>
+                (<em>string</em> uri,
+                <em>string</em> action,
+                <em>string</em> data,
+                <em>string</em> mimetype,
+                <em>iterable</em> categories
+                <em>dictionary</em> extras,
+                <em>component</em> component,
+                <em>flags</em>)
+            </nobr>
+            <div class="jd-descrdiv">
+                Starts an Activity on this device by sending an Intent constructed from the
+                supplied arguments.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <code>
+                <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">
+                        MonkeyImage
+                    </a>
+                </code>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#takeSnapshot">takeSnapshot</a>()
+                </span>
+            </nobr>
+            <div class="jd-descrdiv">
+                Captures the entire screen buffer of this device, yielding a
+                <code>
+                <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">
+                        MonkeyImage
+                </a>
+                </code> object containing a screen capture of the current display.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#touch">touch</a>
+                </span>
+               (<em>integer</em> x,
+                 <em>integer</em> y,
+                 <em>integer</em> type)
+            </nobr>
+            <div class="jd-descrdiv">
+                Sends a touch event specified by type to the screen location specified
+                by x and y.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#touch">type</a>
+                </span>
+                   (<em>string</em> message)
+            </nobr>
+            <div class="jd-descrdiv">
+                Sends the characters contained in message to this device, as if they
+                had been typed on the device's keyboard. This is equivalent to calling
+                <code><a href="#press">press()</a></code> for each keycode in <code>message</code>
+                using the key event type <code><a href="#ACTION_DOWN_AND_UP"></a>DOWN_AND_UP</code>.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#touch">wake</a>
+                </span>
+                   ()
+            </nobr>
+            <div class="jd-descrdiv">
+                Wakes the screen of this device.
+            </div>
+        </td>
+    </tr>
+</table>
+<!-- ========= ENUM CONSTANTS DETAIL ======== -->
+<h2>Constants</h2>
+<A NAME="ACTION_DOWN"></a>
+<div class="jd-details api">
+    <h4 class="jd-details-title">
+        <span class="normal">
+            <em>string</em>
+        </span>
+            DOWN
+    </h4>
+    <div class="jd-details-descr">
+        <div class="jd-tagdata jd-tagdescr">
+            <p>
+                <code><a href="#press">press()</a></code> or
+                <code><a href="#press">touch()</a></code> value.
+                Specifies that a DOWN event type should be sent to the device, corresponding to
+                pressing down on a key or touching the screen.
+            </p>
+        </div>
+    </div>
+</div>
+<A NAME="ACTION_UP"></A>
+<div class="jd-details api">
+    <h4 class="jd-details-title">
+        <span class="normal">
+            <em>string</em>
+        </span>
+            UP
+    </h4>
+    <div class="jd-details-descr">
+        <div class="jd-tagdata jd-tagdescr">
+            <p>
+                <code><a href="#press">press()</a></code> or
+                <code><a href="#press">touch()</a></code> value.
+                Specifies that an UP event type should be sent to the device, corresponding to
+                releasing a key or lifting up from the screen.
+            </p>
+        </div>
+    </div>
+</div>
+<A NAME="ACTION_DOWN_AND_UP"></A>
+
+<div class="jd-details api">
+    <h4 class="jd-details-title">
+        <span class="normal">
+            <em>string</em>
+        </span>
+            DOWN_AND_UP
+    </h4>
+    <div class="jd-details-descr">
+        <div class="jd-tagdata jd-tagdescr">
+            <p>
+                <code><a href="#press">press()</a></code>,
+                <code><a href="#press">touch()</a></code> or
+                <code><a href="#type">type()</a></code> value.
+                Specifies that a DOWN event type followed by an UP event type should be sent to the
+                device, corresponding to typing a key or clicking the screen.
+            </p>
+        </div>
+    </div>
+</div>
+<!-- ========= METHOD DETAIL ======== -->
+<!-- Public methods -->
+<h2>Public Methods</h2>
+<A NAME="broadcastIntent"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">broadcastIntent</span>
+      <span class="normal">
+      (
+            <em>string</em> uri,
+            <em>string</em> action,
+            <em>string</em> data,
+            <em>string</em> mimetype,
+            <em>iterable</em> categories
+            <em>dictionary</em> extras,
+            <em>component</em> component,
+            <em>iterable</em> flags)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Broadcasts an Intent to this device, as if the Intent were coming from an
+            application. See {@link android.content.Intent Intent} for more information about the
+            arguments.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>uri</th>
+            <td>
+                The URI for the Intent.
+                (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}).
+            </td>
+        </tr>
+        <tr>
+            <th>action</th>
+            <td>
+                The action for this Intent
+                (see {@link android.content.Intent#setAction(java.lang.String) Intent.setAction()}).
+            </td>
+        </tr>
+        <tr>
+            <th>data</th>
+            <td>
+                The data URI for this Intent
+                (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}).
+            </td>
+        </tr>
+        <tr>
+            <th>mimetype</th>
+            <td>
+                The MIME type for the Intent
+                (see {@link android.content.Intent#setType(java.lang.String) Intent.setType()}).
+            </td>
+        </tr>
+        <tr>
+            <th>categories</th>
+            <td>
+                An iterable data structure containing strings that define categories for this
+                Intent
+                (see
+                {@link android.content.Intent#addCategory(java.lang.String) Intent.addCategory()}).
+            </td>
+        </tr>
+        <tr>
+            <th>extras</th>
+            <td>
+                A dictionary of extra data for this Intent
+                (see {@link android.content.Intent#putExtra(java.lang.String,java.lang.String)
+                Intent.putExtra()}
+                for an example).
+                <p>
+                    The key for each dictionary item should be a <em>string</em>. The item's value
+                    can be any simple or structured data type.
+                </p>
+            </td>
+        </tr>
+        <tr>
+            <th>component</th>
+            <td>
+                The component for this Intent (see {@link android.content.ComponentName}).
+                Using this argument will direct the Intent to a specific class within a specific
+                Android package.
+            </td>
+        </tr>
+        <tr>
+            <th>flags</th>
+            <td>
+                An iterable data structure containing flags that control how the Intent is handled
+                (see {@link android.content.Intent#setFlags(int) Intent.setFlags()}).
+            </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="drag"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">drag</span>
+      <span class="normal">
+      (
+            <em>tuple</em> start,
+            <em>tuple</em> end,
+            <em>float</em> duration,
+            <em>integer</em> steps)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Simulates a drag gesture (touch, hold, and move) on this device's screen.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>start</th>
+          <td>
+            The starting point of the drag gesture, in the form of a <em>tuple</em>
+            (x,y) where x and y are <em>integers</em>.
+          </td>
+        </tr>
+        <tr>
+          <th>end</th>
+          <td>
+            The end point of the drag gesture, in the form of a <em>tuple</em> (x,y)
+            where x and y are <em>integers</em>.
+          </td>
+        </tr>
+        <tr>
+            <th>duration</th>
+            <td>The duration of the drag gesture in seconds. The default is 1.0 seconds.</td>
+        </tr>
+        <tr>
+            <th>steps</th>
+            <td>The number of steps to take when interpolating points. The default is 10.</td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="getProperty"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>object</em>
+      </span>
+      <span class="sympad">getProperty</span>
+      <span class="normal">
+        (<em>string</em> key)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Given the name of a system environment variable, returns its value for this device.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>key</th>
+          <td>
+            The name of the system environment variable. The available variable names are listed in
+            <a href="#table1">Table 1. Property variable names</a> at the end of this topic.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            The value of the variable. The data format varies according to the variable requested.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="getSystemProperty"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>object</em>
+      </span>
+      <span class="sympad">getSystemProperty</span>
+      <span class="normal">
+      (<em>string</em> key)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Synonym for <code><a href="#getProperty">getProperty()</a></code>.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>key</th>
+          <td>
+            The name of the system environment variable. The available variable names are listed in
+            <a href="#table1">Table 1. Property Variable Names</a>.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            The value of the variable. The data format varies according to the variable requested.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="installPackage"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">installPackage</span>
+      <span class="normal">
+      (<em>string</em> path)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Installs the Android application or test package contained in packageFile
+            onto this device. If the application or test package is already installed, it is
+            replaced.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>path</th>
+          <td>
+            The fully-qualified path and filename of the <code>.apk</code> file to install.
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="instrument"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>dictionary</em>
+      </span>
+      <span class="sympad">instrument</span>
+      <span class="normal">
+      (
+            <em>string</em> className,
+            <em>dictionary</em> args)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Runs the specified component with Android instrumentation, and returns the results
+            in a dictionary whose exact format is dictated by the component being run. The
+            component must already be present on this device.
+        </p>
+        <p>
+            Use this method to start a test case that uses one of Android's test case classes.
+            See <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing
+            Fundamentals</a> to learn more about unit testing with the Android testing
+            framework.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>className</th>
+          <td>
+            The name of an Android component that is already installed on this device, in the
+            standard form packagename/classname, where packagename is the
+            Android package name of a <code>.apk</code> file on this device, and
+            classname is the class name of an Android component (Activity,
+            ContentProvider, Service, or BroadcastReceiver) in that file. Both
+            packagename and classname must be fully qualified. See
+            {@link android.content.ComponentName} for more details.
+          </td>
+        </tr>
+        <tr>
+          <th>args</th>
+          <td>
+            A dictionary containing flags and their values. These are passed to the component as it
+            is started. If the flag does not take a value, set its dictionary value to an empty
+            string.
+          </td>
+        </tr>
+      </table>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+        <ul class="nolist">
+            <li>
+                <p>
+                    A dictionary containing the component's output. The contents of the dictionary
+                    are defined by the component itself.
+                </p>
+                <p>
+                    If you use {@link android.test.InstrumentationTestRunner} as the class name in
+                    the componentName argument, then the result dictionary contains
+                    the single key "stream". The value of "stream" is a <em>string</em> containing
+                    the test output, as if <code>InstrumentationTestRunner</code> was run from the
+                    command line. The format of this output is described in
+                    <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+                    Testing in Other IDEs</a>.
+                </p>
+            </li>
+        </ul>
+    </div>
+    </div>
+  </div>
+</div>
+<A NAME="press"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">press</span>
+      <span class="normal">
+      (<em>string</em> name,
+      <em>integer</em> type)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Sends the key event specified by <code>type</code> to the key specified by
+            <code>keycode</code>.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>name</th>
+          <td>
+            The name of the keycode to send. See {@link android.view.KeyEvent} for a list of
+            keycode names. Use the keycode name, not its integer value.
+          </td>
+        </tr>
+        <tr>
+          <th>type</th>
+          <td>
+            The type of key event to send. The allowed values are <code><a href="#ACTION_DOWN">
+            DOWN</a></code>, <code><a href="#ACTION_UP">UP</a></code>, and
+            <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>.
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="reboot"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">reboot</span>
+      <span class="normal">
+      (<em>string</em> bootloadType)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+                Reboots this device into the bootloader specified by <code>bootloadType</code>.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>into</th>
+          <td>
+            The type of bootloader to reboot into. The allowed values are
+            "bootloader", "recovery", or "None".
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="removePackage"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">removePackage</span>
+      <span class="normal">
+      (<em>string</em> package)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Deletes the specified package from this device, including its data and cache.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>package</th>
+          <td>
+            The Android package name of an <code>.apk</code> file on this device.
+          </td>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="shell"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>object</em>
+      </span>
+      <span class="sympad">shell</span>
+      <span class="normal">
+      (<em>string</em> cmd)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Executes an <code>adb</code> shell command and returns the result, if any.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>cmd</th>
+          <td>
+            The command to execute in the <code>adb</code> shell. The form of these commands is
+            described in the topic <a href="{@docRoot}guide/developing/tools/adb.html">Android
+            Debug Bridge</a>.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            The results of the command, if any. The format of the results is determined by the
+            command.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="startActivity"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">startActivity</span>
+      <span class="normal">
+      (
+            <em>string</em> uri,
+            <em>string</em> action,
+            <em>string</em> data,
+            <em>string</em> mimetype,
+            <em>iterable</em> categories
+            <em>dictionary</em> extras,
+            <em>component</em> component,
+            <em>iterable</em> flags)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+           Starts an Activity on this device by sending an Intent constructed from the
+           supplied arguments.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>uri</th>
+          <td>
+            The URI for the Intent.
+            (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}).
+          </td>
+        </tr>
+        <tr>
+            <th>action</th>
+            <td>
+                The action for the Intent
+                (see {@link android.content.Intent#setAction(java.lang.String) Intent.setAction()}).
+            </td>
+        </tr>
+        <tr>
+            <th>data</th>
+            <td>
+                The data URI for the Intent
+                (see {@link android.content.Intent#setData(android.net.Uri) Intent.setData()}).
+            </td>
+        </tr>
+        <tr>
+            <th>mimetype</th>
+            <td>
+                The MIME type for the Intent
+                (see {@link android.content.Intent#setType(java.lang.String) Intent.setType()}).
+            </td>
+        </tr>
+        <tr>
+            <th>categories</th>
+            <td>
+                An iterable data structure containing strings that define categories for the
+                Intent
+                (see
+                {@link android.content.Intent#addCategory(java.lang.String) Intent.addCategory()}).
+            </td>
+        </tr>
+        <tr>
+            <th>extras</th>
+            <td>
+                A dictionary of extra data for the Intent
+                (see
+                {@link android.content.Intent#putExtra(java.lang.String,java.lang.String)
+                Intent.putExtra()}
+                for an example).
+                <p>
+                    The key for each dictionary item should be a <em>string</em>. The item's value
+                    can be any simple or structured data type.
+                </p>
+            </td>
+        </tr>
+        <tr>
+            <th>component</th>
+            <td>
+                The component for the Intent
+                (see {@link android.content.ComponentName}). Using this argument will direct the
+                Intent to a specific class within a specific Android package.
+            </td>
+        </tr>
+        <tr>
+            <th>flags</th>
+            <td>
+                An iterable data structure containing flags that control how the Intent is handled
+                (see {@link android.content.Intent#setFlags(int) Intent.setFlags()}).
+            </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="takeSnapshot"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <code>
+            <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">
+                MonkeyImage
+            </a>
+        </code>
+      </span>
+      <span class="sympad">takeSnapshot</span>
+      <span class="normal">
+      ()
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Captures the entire screen buffer of this device, yielding a
+            screen capture of the current display.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            A <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">
+            MonkeyImage</a> object containing the image of the current display.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="touch"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">touch</span>
+      <span class="normal">
+      (
+            <em>integer</em> x,
+            <em>integer</em> y,
+            <em>string</em> type)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Sends a touch event specified by type to the screen location specified
+            by x and y.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>x</th>
+          <td>
+            The horizontal position of the touch in actual device pixels, starting from the left of
+            the screen in its current orientation.
+          </td>
+        </tr>
+        <tr>
+          <th>y</th>
+          <td>
+            The vertical position of the touch in actual device pixels, starting from the top of
+            the screen in its current orientation.
+          </td>
+        </tr>
+        <tr>
+            <th>type</th>
+            <td>
+                The type of key event to send. The allowed values are <code><a href="#ACTION_DOWN">
+                DOWN</a></code>, <code><a href="#ACTION_UP">UP</a></code>, and
+                <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>.
+            </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="type"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">type</span>
+      <span class="normal">
+      (<em>string</em> message)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Sends the characters contained in message to this device, as if they
+            had been typed on the device's keyboard. This is equivalent to calling
+            <code><a href="#press">press()</a></code> for each keycode in <code>message</code>
+            using the key event type <code><a href="#ACTION_DOWN_AND_UP">DOWN_AND_UP</a></code>.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>message</th>
+          <td>
+              A string containing the characters to send.
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="wake"></A>
+<div class="jd-details api">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">wake</span>
+      <span class="normal">
+      ()
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Wakes the screen of this device.
+        </p>
+    </div>
+  </div>
+</div>
+<hr></hr>
+<h2>Appendix</h2>
+    <p class="table-caption" id="table1">
+        <strong>Table 1.</strong>Property variable names used with
+        <span class="sympad"><a href="#getProperty">getProperty()</a></span> and
+        <span class="sympad"><a href="#getSystemProperty">getSystemProperty()</a></span>.
+    </p>
+    <table>
+        <tr>
+            <th>
+                Property Group
+            </th>
+            <th>
+                Property
+            </th>
+            <th>
+                Description
+            </th>
+            <th>
+                Notes
+            </th>
+        </tr>
+        <tr>
+            <td rowspan="17"><code>build</code></td>
+            <td><code>board</code></td>
+            <td>Code name for the device's system board</td>
+            <td rowspan="17">
+                See {@link android.os.Build}
+            </td>
+        </tr>
+        <tr>
+            <td><code>brand</code></td>
+            <td>The carrier or provider for which the OS is customized.</td>
+        </tr>
+            <tr>
+            <td><code>device</code></td>
+            <td>The device design name.</td>
+        </tr>
+            <tr>
+            <td><code>fingerprint</code></td>
+            <td>A unique identifier for the currently-running build.</td>
+        </tr>
+            <tr>
+            <td><code>host</code></td>
+            <td></td>
+        </tr>
+            <tr>
+            <td><code>ID</code></td>
+            <td>A changelist number or label.</td>
+        </tr>
+            <tr>
+            <td><code>model</code></td>
+            <td>The end-user-visible name for the device.</td>
+        </tr>
+            <tr>
+            <td><code>product</code></td>
+            <td>The overall product name.</td>
+        </tr>
+            <tr>
+            <td><code>tags</code></td>
+            <td>Comma-separated tags that describe the build, such as "unsigned" and "debug".</td>
+        </tr>
+            <tr>
+            <td><code>type</code></td>
+            <td>The build type, such as "user" or "eng".</td>
+        </tr>
+        <tr>
+            <td><code>user</code></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td><code>CPU_ABI</code></td>
+            <td>
+                The name of the native code instruction set, in the form CPU type plus
+                ABI convention.
+            </td>
+        </tr>
+        <tr>
+            <td><code>manufacturer</code></td>
+            <td>The product/hardware manufacturer.</td>
+        </tr>
+        <tr>
+            <td><code>version.incremental</code></td>
+            <td>
+                The internal code used by the source control system to represent this version
+                of the software.
+            </td>
+        </tr>
+        <tr>
+            <td><code>version.release</code></td>
+            <td>The user-visible name of this version of the software.</td>
+        </tr>
+        <tr>
+            <td><code>version.sdk</code></td>
+            <td>The user-visible SDK version associated with this version of the OS.</td>
+        </tr>
+        <tr>
+            <td><code>version.codename</code></td>
+            <td>
+                The current development codename, or "REL" if this version of the software has been
+                released.
+            </td>
+        </tr>
+        <tr>
+            <td rowspan="3"><code>display</code></td>
+            <td><code>width</code></td>
+            <td>The device's display width in pixels.</td>
+            <td rowspan="3">
+                See
+                {@link android.util.DisplayMetrics} for details.
+            </td>
+        </tr>
+        <tr>
+            <td><code>height</code></td>
+            <td>The device's display height in pixels.</td>
+        </tr>
+        <tr>
+            <td><code>density</code></td>
+            <td>
+                The logical density of the display. This is a factor that scales
+                DIP (Density-Independent Pixel) units to the device's resolution. DIP is adjusted so
+                that 1 DIP is equivalent to one pixel on a 160 pixel-per-inch display. For example,
+                on a 160-dpi screen, density = 1.0, while on a 120-dpi screen, density = .75.
+                <p>
+                    The value does not exactly follow the real screen size, but is adjusted to
+                    conform to large changes in the display DPI. See
+                    {@link android.util.DisplayMetrics#density} for more details.
+                </p>
+            </td>
+        </tr>
+        <tr>
+            <td rowspan="6"><code>am.current</code></td>
+            <td><code>package</code></td>
+            <td>The Android package name of the currently running package.</td>
+            <td rowspan="6">
+                The <code>am.current</code> keys return information about the currently-running
+                Activity.
+            </td>
+        </tr>
+        <tr>
+            <td><code>action</code></td>
+            <td>
+                The current activity's action. This has the same format as the <code>name</code>
+                attribute of the <code>action</code> element in a package manifest.
+            </td>
+        </tr>
+        <tr>
+            <td><code>comp.class</code></td>
+            <td>
+                The class name of the component that started the current Activity. See
+                <code><a href="#comppackage">comp.package</a></code> for more details.</td>
+        </tr>
+        <tr>
+            <td><a name="comppackage"><code>comp.package</code></a></td>
+            <td>
+                The package name of the component that started the current Activity. A component
+                is specified by a package name and the name of class that the package contains.
+            </td>
+        </tr>
+        <tr>
+            <td><code>data</code></td>
+            <td>The data (if any) contained in the Intent that started the current Activity.</td>
+        </tr>
+        <tr>
+            <td><code>categories</code></td>
+            <td>The categories specified by the Intent that started the current Activity.</td>
+        </tr>
+        <tr>
+            <td rowspan="3"><code>clock</code></td>
+            <td><code>realtime</code></td>
+            <td>
+                The number of milliseconds since the device rebooted, including deep-sleep
+                time.
+            </td>
+            <td rowspan="3">
+                See {@link android.os.SystemClock} for more information.
+            </td>
+        </tr>
+        <tr>
+            <td><code>uptime</code></td>
+            <td>
+                The number of milliseconds since the device rebooted, <em>not</em> including
+                deep-sleep time
+            </td>
+        </tr>
+        <tr>
+            <td><code>millis</code></td>
+            <td>current time since the UNIX epoch, in milliseconds.</td>
+        </tr>
+    </table>
diff --git a/docs/html/guide/developing/tools/MonkeyImage.jd b/docs/html/guide/developing/tools/MonkeyImage.jd
new file mode 100644
index 0000000..ae85cb5
--- /dev/null
+++ b/docs/html/guide/developing/tools/MonkeyImage.jd
@@ -0,0 +1,435 @@
+page.title=MonkeyImage
+@jd:body
+<style>
+    h4.jd-details-title {background-color: #DEE8F1;}
+</style>
+
+<p>
+    A monkeyrunner class to hold an image of the device or emulator's screen. The image is
+    copied from the screen buffer during a screenshot. This object's methods allow you to
+    convert the image into various storage formats, write the image to a file, copy parts of
+    the image, and compare this object to other <code>MonkeyImage</code> objects.
+</p>
+<p>
+    You do not need to create new instances of <code>MonkeyImage</code>. Instead, use
+<code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html#takeSnapshot">
+MonkeyDevice.takeSnapshot()</a></code> to create a new instance from a screenshot. For example, use:
+</p>
+<pre>
+newimage = MonkeyDevice.takeSnapshot()
+</pre>
+<h2>Summary</h2>
+<table id="pubmethods" class="jd-sumtable">
+    <tr>
+        <th colspan="12" style="background-color: #E2E2E2">Methods</th>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>string</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#convertToBytes">convertToBytes</a>
+                </span>
+                (<em>string</em> format)
+            </nobr>
+            <div class="jd-descrdiv">
+                Converts the current image to a particular format and returns it as a
+                <em>string</em> that you can then access as an <em>iterable</em> of binary bytes.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>tuple</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#getRawPixel">getRawPixel</a>
+                </span>
+                (<em>integer</em> x,
+                <em>integer</em> y)
+            </nobr>
+            <div class="jd-descrdiv">
+                Returns the single pixel at the image location (x,y), as an
+                a <em>tuple</em> of <em>integer</em>, in the form (a,r,g,b).
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>integer</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#getRawPixelInt">getRawPixelInt</a>
+                </span>
+                (<em>integer</em> x,
+                 <em>integer</em> y)
+            </nobr>
+            <div class="jd-descrdiv">
+                Returns the single pixel at the image location (x,y), as
+                a 32-bit <em>integer</em>.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <code>
+                    <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a>
+                </code>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#getSubImage">getSubImage</a>
+                </span>
+                (<em>tuple</em> rect)
+            </nobr>
+            <div class="jd-descrdiv">
+                Creates a new <code>MonkeyImage</code> object from a rectangular selection of the
+                current image.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>boolean</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#sameAs">sameAs</a>
+                </span>
+         (<code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code>
+            other,
+            <em>float</em> percent)
+            </nobr>
+            <div class="jd-descrdiv">
+                Compares this <code>MonkeyImage</code> object to another and returns the result of
+                the comparison. The <code>percent</code> argument specifies the percentage
+                difference that is allowed for the two images to be "equal".
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>void</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#writeToFile">writeToFile</a>
+                </span>
+                (<em>string</em> path,
+                <em>string</em> format)
+            </nobr>
+            <div class="jd-descrdiv">
+                Writes the current image to the file specified by <code>filename</code>, in the
+                format specified by <code>format</code>.
+            </div>
+        </td>
+    </tr>
+</table>
+<!-- ========= METHOD DETAIL ======== -->
+<!-- Public methods -->
+<h2>Public Methods</h2>
+<A NAME="convertToBytes"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>string</em>
+      </span>
+      <span class="sympad">convertToBytes</span>
+      <span class="normal">
+      (
+            <em>string</em> format)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Converts the current image to a particular format and returns it as a <em>string</em>
+            that you can then access as an <em>iterable</em> of binary bytes.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>format</th>
+            <td>
+                The desired output format. All of the common raster output formats are supported.
+                The default value is "png" (Portable Network Graphics).
+            </td>
+        </tr>
+        </table>
+    </div>
+</div>
+</div>
+<A NAME="getRawPixel"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>tuple</em>
+      </span>
+      <span class="sympad">getRawPixel</span>
+      <span class="normal">
+        (<em>integer</em> x,
+         <em>integer</em> y)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Returns the single pixel at the image location (x,y), as an
+            a <em>tuple</em> of <em>integer</em>, in the form (a,r,g,b).
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>x</th>
+          <td>
+            The horizontal position of the pixel, starting with 0 at the left of the screen in the
+            orientation it had when the screenshot was taken.
+          </td>
+        </tr>
+        <tr>
+          <th>y</th>
+          <td>
+            The vertical position of the pixel, starting with 0 at the top of the screen in the
+            orientation it had when the screenshot was taken.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            A tuple of integers representing the pixel, in the form (a,r,g,b) where
+            a is the alpha channel value, and r, g, and b are the red, green, and blue values,
+            respectively.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="getRawPixelInt"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>tuple</em>
+      </span>
+      <span class="sympad">getRawPixelInt</span>
+      <span class="normal">
+        (<em>integer</em> x,
+         <em>integer</em> y)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Returns the single pixel at the image location (x,y), as an
+            an <em>integer</em>. Use this method to economize on memory.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>x</th>
+          <td>
+            The horizontal position of the pixel, starting with 0 at the left of the screen in the
+            orientation it had when the screenshot was taken.
+          </td>
+        </tr>
+        <tr>
+          <th>y</th>
+          <td>
+            The vertical position of the pixel, starting with 0 at the top of the screen in the
+            orientation it had when the screenshot was taken.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            The a,r,g, and b values of the pixel as 8-bit values combined into a 32-bit
+            integer, with a as the leftmost 8 bits, r the next rightmost, and so forth.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="getSubImage"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+          <code>
+              <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a>
+          </code>
+      </span>
+      <span class="sympad">getSubImage</span>
+      <span class="normal">
+        (<em>tuple</em> rect)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+           Creates a new <code>MonkeyImage</code> object from a rectangular selection of the
+           current image.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>rect</th>
+          <td>
+            A tuple (x, y, w, h) specifying the selection. x and y specify the 0-based pixel
+            position of the upper left-hand corner of the selection. w specifies the width of the
+            region, and h specifies its height, both in units of pixels.
+            <p>
+                The image's orientation is the same as the screen orientation at the time the
+                screenshot was made.
+            </p>
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            A new <code>MonkeyImage</code> object containing the selection.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="sameAs"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>boolean</em>
+      </span>
+      <span class="sympad">sameAs</span>
+      <span class="normal">
+      (
+       <code>
+           <a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a>
+       </code> otherImage,
+       <em>float</em> percent
+      )
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+           Compares this <code>MonkeyImage</code> object to another and returns the result of
+           the comparison. The <code>percent</code> argument specifies the percentage
+           difference that is allowed for the two images to be "equal".
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>other</th>
+          <td>
+            Another <code>MonkeyImage</code> object to compare to this one.
+          </td>
+        </tr>
+        <tr>
+          <th>
+            percent
+          </th>
+          <td>
+            A float in the range 0.0 to 1.0, inclusive, indicating
+            the percentage of pixels that need to be the same for the method to return
+            <code>true</code>. The default is 1.0, indicating that all the pixels
+            must match.
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            Boolean <code>true</code> if the images match, or boolean <code>false</code> otherwise.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="writeToFile"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">writeToFile</span>
+      <span class="normal">
+      (<em>string</em> filename,
+       <em>string</em> format)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+           Writes the current image to the file specified by <code>filename</code>, in the
+           format specified by <code>format</code>.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>path</th>
+          <td>
+            The fully-qualified filename and extension of the output file.
+          </td>
+        </tr>
+        <tr>
+            <th>
+                format
+            </th>
+            <td>
+                The output format to use for the file. If no format is provided, then the
+                method tries to guess the format from the filename's extension. If no
+                extension is provided and no format is specified, then the default format of
+                "png" (Portable Network Graphics) is used.
+            </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
diff --git a/docs/html/guide/developing/tools/MonkeyRunner.jd b/docs/html/guide/developing/tools/MonkeyRunner.jd
new file mode 100644
index 0000000..871e06d
--- /dev/null
+++ b/docs/html/guide/developing/tools/MonkeyRunner.jd
@@ -0,0 +1,445 @@
+page.title=MonkeyRunner
+@jd:body
+<style>
+    h4.jd-details-title {background-color: #DEE8F1;}
+</style>
+<p>
+    A monkeyrunner class that contains static utility methods.
+</p>
+<h2>Summary</h2>
+<table id="pubmethods" class="jd-sumtable">
+    <tr>
+        <th colspan="12" style="background-color: #E2E2E2">Methods</th>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#alert">alert</a>
+                </span>
+                (<em>string</em> message,
+                 <em>string</em> title,
+                 <em>string</em> okTitle)
+            </nobr>
+            <div class="jd-descrdiv">
+                Displays an alert dialog to the process running the current
+                program.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>integer</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#choice">choice</a>
+                </span>
+                (<em>string</em> message,
+                 <em>iterable</em> choices,
+                 <em>string</em> title)
+            </nobr>
+            <div class="jd-descrdiv">
+                Displays a dialog with a list of choices to the process running the current program.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#help">help</a>
+                </span>
+                (<em>string</em> format)
+            </nobr>
+            <div class="jd-descrdiv">
+                Displays the monkeyrunner API reference in a style similar to that of Python's
+                <code>pydoc</code> tool, using the specified format.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <em>string</em>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#input">input</a>
+                </span>
+                (<em>string</em> message,
+                 <em>string</em> initialValue,
+                 <em>string</em> title,
+                 <em>string</em> okTitle,
+                 <em>string</em> cancelTitle)
+            </nobr>
+            <div class="jd-descrdiv">
+                Displays a dialog that accepts input.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                void
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#sleep">sleep</a>
+                </span>
+                (<em>float</em> seconds)
+            </nobr>
+            <div class="jd-descrdiv">
+                Pauses the current program for the specified number of seconds.
+            </div>
+        </td>
+    </tr>
+    <tr class="api" >
+        <td class="jd-typecol">
+            <nobr>
+                <code>
+                    <a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a>
+                </code>
+            </nobr>
+        </td>
+        <td class="jd-linkcol" width="100%">
+            <nobr>
+                <span class="sympad">
+                    <a href="#waitForConnection">waitForConnection</a>
+                </span>
+                (<em>float</em> timeout,
+                <em>string</em> deviceId)
+            </nobr>
+            <div class="jd-descrdiv">
+                Tries to make a connection between the <code>monkeyrunner</code> backend and the
+                specified device or emulator.
+            </div>
+        </td>
+    </tr>
+</table>
+<!-- ========= METHOD DETAIL ======== -->
+<!-- Public methods -->
+<h2>Public Methods</h2>
+<A NAME="alert"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>string</em>
+      </span>
+      <span class="sympad">alert</span>
+      <span class="normal">
+      (
+            <em>string</em> message,
+            <em>string</em> title,
+            <em>string</em> okTitle)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Displays an alert dialog to the process running the current
+            program. The dialog is modal, so the program pauses until the user clicks the dialog's
+            button.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>message</th>
+            <td>
+                The message to display in the dialog.
+            </td>
+        </tr>
+        <tr>
+            <th>title</th>
+            <td>
+                The dialog's title. The default value is "Alert".
+            </td>
+        </tr>
+        <tr>
+            <th>okTitle</th>
+            <td>
+                The text displayed in the dialog button. The default value is "OK".
+            </td>
+        </tr>
+        </table>
+    </div>
+</div>
+</div>
+<A NAME="choice"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>integer</em>
+      </span>
+      <span class="sympad">choice</span>
+      <span class="normal">
+        (<em>string</em> message,
+         <em>iterable</em> choices,
+         <em>string</em> title)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Displays a dialog with a list of choices to the process running the current program. The
+            dialog is modal, so the program pauses until the user clicks one of the dialog's
+            buttons.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>message</th>
+          <td>
+            The prompt message displayed in the dialog.
+          </td>
+        </tr>
+        <tr>
+          <th>choices</th>
+          <td>
+            A Python iterable containing one or more objects that are displayed as strings. The
+            recommended form is an array of strings.
+          </td>
+        </tr>
+        <tr>
+            <th>
+                title
+            </th>
+            <td>
+               The dialog's title. The default is "Input".
+            </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            If the user makes a selection and clicks the "OK" button, the method returns
+            the 0-based index of the selection within the iterable.
+            If the user clicks the "Cancel" button, the method returns -1.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="help"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">help</span>
+      <span class="normal">
+        (<em>string</em> format)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Displays the monkeyrunner API reference in a style similar to that of Python's
+            <code>pydoc</code> tool, using the specified format.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>format</th>
+          <td>
+            The markup format to use in the output. The possible values are "text" for plain text
+            or "html" for HTML.
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="input"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <em>string</em>
+      </span>
+      <span class="sympad">input</span>
+      <span class="normal">
+        (<em>string</em> message
+         <em>string</em> initialValue,
+         <em>string</em> title,
+         <em>string</em> okTitle,
+         <em>string</em> cancelTitle)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Displays a dialog that accepts input and returns it to the program. The dialog is
+            modal, so the program pauses until the user clicks one of the dialog's buttons.
+        </p>
+        <p>
+            The dialog contains two buttons, one of which displays the okTitle value
+            and the other the cancelTitle value. If the user clicks the okTitle button,
+            the current value of the input box is returned. If the user clicks the cancelTitle
+            button, an empty string is returned.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>message</th>
+          <td>
+            The prompt message displayed in the dialog.
+          </td>
+        </tr>
+        <tr>
+          <th>initialValue</th>
+          <td>
+            The initial value to display in the dialog. The default is an empty string.
+          </td>
+        </tr>
+        <tr>
+          <th>title</th>
+          <td>
+            The dialog's title. The default is "Input".
+          </td>
+        </tr>
+        <tr>
+          <th>okTitle</th>
+          <td>
+            The text displayed in the okTitle button. The default is "OK".
+          </td>
+        </tr>
+        <tr>
+          <th>cancelTitle</th>
+          <td>
+            The text displayed in the cancelTitle button. The default is "Cancel".
+          </td>
+        </tr>
+      </table>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+            If the user clicks the okTitle button, then the method returns the current value of
+            the dialog's input box. If the user clicks the cancelTitle button, the method returns
+            an empty string.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+<A NAME="sleep"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+        void
+      </span>
+      <span class="sympad">sleep</span>
+      <span class="normal">
+      (
+       <em>float</em> seconds
+      )
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Pauses the current program for the specified number of seconds.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>seconds</th>
+          <td>
+            The number of seconds to pause.
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+</div>
+<A NAME="waitForConnection"></A>
+<div class="jd-details api ">
+    <h4 class="jd-details-title">
+      <span class="normal">
+          <code>
+              <a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a>
+          </code>
+      </span>
+      <span class="sympad">waitForConnection</span>
+      <span class="normal">
+      (<em>float</em> timeout,
+       <em>string</em> deviceId)
+      </span>
+    </h4>
+  <div class="jd-details-descr">
+
+    <div class="jd-tagdata jd-tagdescr">
+        <p>
+            Tries to make a connection between the <code>monkeyrunner</code> backend and the
+            specified device or emulator.
+        </p>
+    </div>
+    <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Arguments</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>timeout</th>
+          <td>
+            The number of seconds to wait for a connection. The default is to wait forever.
+          </td>
+        </tr>
+        <tr>
+            <th>
+                deviceId
+            </th>
+            <td>
+                A regular expression that specifies the serial number of the device or emulator. See
+                the topic
+                <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
+                for a description of device and emulator serial numbers.
+            </td>
+        </tr>
+      </table>
+    </div>
+        <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist">
+        <li>
+          A <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code>
+          instance for the device or emulator. Use this object to control and communicate with the
+          device or emulator.
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
diff --git a/docs/html/guide/developing/tools/index.jd b/docs/html/guide/developing/tools/index.jd
index 6e9fde1..0e10377 100644
--- a/docs/html/guide/developing/tools/index.jd
+++ b/docs/html/guide/developing/tools/index.jd
@@ -3,27 +3,27 @@
 
 <img src="{@docRoot}assets/images/android_wrench.png" alt="" align="right">
 
-<p>The Android SDK includes a variety of custom tools that help you develop mobile 
-applications on the Android platform. The most important of these are the Android 
-Emulator and the Android Development Tools plugin for Eclipse, but the SDK also 
-includes a variety of other tools for debugging, packaging, and installing your 
+<p>The Android SDK includes a variety of custom tools that help you develop mobile
+applications on the Android platform. The most important of these are the Android
+Emulator and the Android Development Tools plugin for Eclipse, but the SDK also
+includes a variety of other tools for debugging, packaging, and installing your
 applications on the emulator. </p>
-    
+
  <dl>
   <dt><a href="adt.html">Android Development Tools Plugin</a> (for the Eclipse IDE)</dt>
-          <dd>The ADT plugin adds powerful extensions to the Eclipse integrated environment, 
-          making creating and debugging your Android applications easier and faster. If you 
-          use Eclipse, the ADT plugin gives you an incredible boost in developing Android 
+          <dd>The ADT plugin adds powerful extensions to the Eclipse integrated environment,
+          making creating and debugging your Android applications easier and faster. If you
+          use Eclipse, the ADT plugin gives you an incredible boost in developing Android
           applications.</dd>
   <dt><a href="emulator.html">Android Emulator</a></dt>
-    <dd>A QEMU-based device-emulation tool that you can use to design, 
+    <dd>A QEMU-based device-emulation tool that you can use to design,
     debug, and test your applications in an actual Android run-time environment. </dd>
 
   <dt><a href="avd.html">Android Virtual Devices (AVDs)</a></dt>
     <dd>Virtual device configurations that you create, to model device
         characteristics in the Android Emulator. In each configuration, you can
         specify the Android platform to run, the hardware options, and the
-        emulator skin to use. Each AVD functions as an independent device with 
+        emulator skin to use. Each AVD functions as an independent device with
         it's own storage for user data, SD card, and so on. </dd>
 
  <dt><a href="hierarchy-viewer.html">Hierarchy Viewer</a></dt>
@@ -37,53 +37,53 @@
 efficiency.
     </dd>
 
-	  <dt><a href="draw9patch.html">Draw 9-patch</a></dt>
-	    <dd>The Draw 9-patch tool allows you to easily create a 
-	    {@link android.graphics.NinePatch} graphic using a WYSIWYG editor. It also previews stretched 
-	     versions of the image, and highlights the area in which content is allowed.
-	    </dd>
+      <dt><a href="draw9patch.html">Draw 9-patch</a></dt>
+        <dd>The Draw 9-patch tool allows you to easily create a
+        {@link android.graphics.NinePatch} graphic using a WYSIWYG editor. It also previews stretched
+         versions of the image, and highlights the area in which content is allowed.
+        </dd>
 
-  <dt><a href="ddms.html" >Dalvik Debug Monitor 
+  <dt><a href="ddms.html" >Dalvik Debug Monitor
       Service</a> (ddms)</dt>
-	    <dd>Integrated with Dalvik, the Android platform's custom VM, this tool 
-	    lets you manage processes on an emulator or device and assists in debugging. 
-	    You can use it to kill processes, select a specific process to debug, 
-	    generate trace data, view heap and thread information, take screenshots 
-	    of the emulator or device, and more. </dd>
-			
+        <dd>Integrated with Dalvik, the Android platform's custom VM, this tool
+        lets you manage processes on an emulator or device and assists in debugging.
+        You can use it to kill processes, select a specific process to debug,
+        generate trace data, view heap and thread information, take screenshots
+        of the emulator or device, and more. </dd>
+
   <dt><a href="adb.html" >Android Debug Bridge</a> (adb)</dt>
-                  <dd>The adb tool lets you install your application's .apk files on an 
-                  emulator or device and access the emulator or device from a command line. 
-                  You can also use it to link a standard debugger to application code running 
+                  <dd>The adb tool lets you install your application's .apk files on an
+                  emulator or device and access the emulator or device from a command line.
+                  You can also use it to link a standard debugger to application code running
                   on an Android emulator or device.</dd>
 
-  <dt><a href="aapt.html">Android Asset 
+  <dt><a href="aapt.html">Android Asset
           Packaging Tool</a> (aapt)</dt>
-                  <dd>The aapt tool lets you create .apk files containing the binaries and 
+                  <dd>The aapt tool lets you create .apk files containing the binaries and
                   resources of Android applications.</dd>
 
-  <dt><a href="aidl.html" >Android Interface 
+  <dt><a href="aidl.html" >Android Interface
   Description Language</a> (aidl)</dt>
             <dd>Lets you generate code for an interprocess interface, such as what
             a service might use.</dd>
 
   <dt><a href="adb.html#sqlite">sqlite3</a></dt>
-      <dd>Included as a convenience, this tool lets you access the SQLite data 
+      <dd>Included as a convenience, this tool lets you access the SQLite data
       files created and used by Android applications.</dd>
 
   <dt><a href="traceview.html" >Traceview</a></dt>
-            <dd> This tool produces graphical analysis views of trace log data that you 
+            <dd> This tool produces graphical analysis views of trace log data that you
             can generate from your Android application. </dd>
 
   <dt><a href="othertools.html#mksdcard">mksdcard</a></dt>
-            <dd>Helps you create a disk image that you can use with the emulator, 
+            <dd>Helps you create a disk image that you can use with the emulator,
                       to simulate the presence of an external storage card (such as an SD card).</dd>
 
   <dt><a href="othertools.html#dx">dx</a></dt>
-                  <dd>The dx tool rewrites .class bytecode into Android bytecode 
+                  <dd>The dx tool rewrites .class bytecode into Android bytecode
                                           (stored in .dex files.)</dd>
 
-  <dt><a href="monkey.html">UI/Application 
+  <dt><a href="monkey.html">UI/Application
   Exerciser Monkey</a></dt>
       <dd>The Monkey is a program that runs on your emulator or device and generates pseudo-random
       streams of user events such as clicks, touches, or gestures, as well as a number of system-
@@ -92,9 +92,9 @@
 
   <dt><a  href="othertools.html#android">android</a></dt>
             <dd>A script that lets you manage AVDs and generate <a
-                        href="http://ant.apache.org/" title="Ant">Ant</a> build files that 
+                        href="http://ant.apache.org/" title="Ant">Ant</a> build files that
                         you can use to compile your Android applications. </dd>
-                        
+
   <dt><a  href="zipalign.html">zipalign</a></dt>
             <dd>An important .apk optimization tool. This tool ensures that all uncompressed data starts
             with a particular alignment relative to the start of the file. This should always be used
diff --git a/docs/html/guide/developing/tools/monkeyrunner_concepts.jd b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd
new file mode 100644
index 0000000..1838905
--- /dev/null
+++ b/docs/html/guide/developing/tools/monkeyrunner_concepts.jd
@@ -0,0 +1,308 @@
+page.title=monkeyrunner
+@jd:body
+
+<div id="qv-wrapper">
+  <div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li>
+        <a href="#SampleProgram">A Simple monkeyrunner Program</a>
+    </li>
+    <li>
+        <a href="#APIClasses">The monkeyrunner API</a>
+    </li>
+    <li>
+        <a href="#RunningMonkeyRunner">Running monkeyrunner</a>
+    </li>
+    <li>
+        <a href="#Help">monkeyrunner Built-in Help</a>
+    </li>
+    <li>
+        <a href="#Plugins">Extending monkeyrunner with Plugins</a>
+    </li>
+  </ol>
+  <h2>See Also</h2>
+      <ol>
+        <li>
+            <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>
+        </li>
+      </ol>
+  </div>
+</div>
+<p>
+    The monkeyrunner tool provides an API for writing programs that control an Android device
+    or emulator from outside of Android code. With monkeyrunner, you can write a Python program
+    that installs an Android application or test package, runs it, sends keystrokes to it,
+    takes screenshots of its user interface, and stores screenshots on the workstation. The
+    monkeyrunner tool is primarily designed to test applications and devices at the
+    functional/framework level and for running unit test suites, but you are free to use it for
+    other purposes.
+</p>
+<p>
+    The monkeyrunner tool is not related to the
+    <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>,
+    also known as the <code>monkey</code> tool. The <code>monkey</code> tool runs in an
+    <code><a href="{@docRoot}guide/developing/tools/adb.html">adb</a></code> shell directly on the
+    device or emulator and generates pseudo-random streams of user and system events. In comparison,
+    the monkeyrunner tool controls devices and emulators from a workstation by sending specific
+    commands and events from an API.
+</p>
+<p>
+    The monkeyrunner tool provides these unique features for Android testing:
+</p>
+<ul>
+    <li>
+        Multiple device control: The monkeyrunner API can apply one or more
+        test suites across multiple devices or emulators. You can physically attach all the devices
+        or start up all the emulators (or both) at once, connect to each one in turn
+        programmatically, and then run one or more tests. You can also start up an emulator
+        configuration programmatically, run one or more tests, and then shut down the emulator.
+    </li>
+    <li>
+        Functional testing: monkeyrunner can run an automated start-to-finish test of an Android
+        application. You provide input values with keystrokes or touch events, and view the results
+        as screenshots.
+    </li>
+    <li>
+        Regression testing - monkeyrunner can test application stability by running an application
+        and comparing its output screenshots to a set of screenshots that are known to be correct.
+    </li>
+    <li>
+        Extensible automation - Since monkeyrunner is an API toolkit, you can develop an entire
+        system of Python-based modules and programs for controlling Android devices. Besides using
+        the monkeyrunner API itself, you can use the standard Python
+        <code><a href="http://docs.python.org/library/os.html">os</a></code> and
+        <code><a href="http://docs.python.org/library/subprocess.html">subprocess</a></code>
+        modules to call Android tools such as
+        <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>.
+        <p>
+            You can also add your own classes to the monkeyrunner API. This is described
+            in more detail in the section
+            <a href="#Plugins">Extending monkeyrunner with plugins</a>.
+        </p>
+    </li>
+</ul>
+<p>
+    The monkeyrunner tool uses <a href="http://www.jython.org/">Jython</a>, a
+    implementation of Python that uses the Java programming language. Jython allows the
+    monkeyrunner API to interact easily with the Android framework. With Jython you can
+    use Python syntax to access the constants, classes, and methods of the API.
+</p>
+
+<h2 id="SampleProgram">A Simple monkeyrunner Program</h2>
+<p>
+    Here is a simple monkeyrunner program that connects to a device, creating a
+    <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code>
+    object. Using the <code>MonkeyDevice</code> object, the program installs an Android application
+    package, runs one of its activities, and sends key events to the activity.
+    The program then takes a screenshot of the result, creating a
+    <code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code> object.
+    From this object, the program writes out a <code>.png</code> file containing the screenshot.
+</p>
+<pre>
+# Imports the monkeyrunner modules used by this program
+from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
+
+# Connects to the current device, returning a MonkeyDevice object
+device = MonkeyRunner.waitForConnection()
+
+# Installs the Android package. Notice that this method returns a boolean, so you can test
+# to see if the installation worked.
+device.installPackage('myproject/bin/MyApplication.apk')
+
+# Runs an activity in the application
+device.startActivity(component='com.example.android.myapplication.MainActivity')
+
+# Presses the Menu button
+device.press('KEYCODE_MENU','DOWN_AND_UP')
+
+# Takes a screenshot
+result = device.takeSnapShot
+
+# Writes the screenshot to a file
+result.writeToFile('myproject/shot1.png','png')
+</pre>
+
+<h2 id="APIClasses">The monkeyrunner API</h2>
+<p>
+    The monkeyrunner API is contained in three modules in the package
+    <code>com.android.monkeyrunner</code>:
+</p>
+<ul>
+    <li>
+        <code><a href="{@docRoot}guide/developing/tools/MonkeyRunner.html">MonkeyRunner</a></code>:
+        A class of utility methods for monkeyrunner programs. This class provides a method for
+        connecting monkeyrunner to a device or emulator. It also provides methods for
+        creating UIs for a monkeyrunner program and for displaying the built-in help.
+    </li>
+    <li>
+        <code><a href="{@docRoot}guide/developing/tools/MonkeyDevice.html">MonkeyDevice</a></code>:
+        Represents a device or emulator. This class provides methods for installing and
+        uninstalling packages, starting an Activity, and sending keyboard or touch events to an
+        application. You also use this class to run test packages.
+    </li>
+    <li>
+        <code><a href="{@docRoot}guide/developing/tools/MonkeyImage.html">MonkeyImage</a></code>:
+        Represents a screen capture image. This class provides methods for capturing screens,
+        converting bitmap images to various formats, comparing two MonkeyImage objects, and
+        writing an image to a file.
+    </li>
+</ul>
+<p>
+    In a Python program, you access each class as a Python module. The monkeyrunner tool
+    does not import these modules automatically. To import a module, use the
+    Python <code>from</code> statement:
+</p>
+<pre>
+from com.android.monkeyrunner import &lt;module&gt;
+</pre>
+<p>
+    where <code>&lt;module&gt;</code> is the class name you want to import. You can import more
+    than one module in the same <code>from</code> statement by separating the module names with
+    commas.
+</p>
+<h2 id="RunningMonkeyRunner">Running monkeyrunner</h2>
+<p>
+    You can either run monkeyrunner programs from a file, or enter monkeyrunner statements in
+    an interactive session. You do both by invoking the <code>monkeyrunner</code> command
+    which is found in the <code>tools/</code> subdirectory of your SDK directory.
+    If you provide a filename as an argument, the <code>monkeyrunner</code> command
+    runs the file's contents as a Python program; otherwise, it starts an interactive session.
+</p>
+<p>
+    The syntax of the <code>monkeyrunner</code> command is
+</p>
+<pre>
+monkeyrunner -plugin &lt;plugin_jar&gt; &lt;program_filename&gt; &lt;program_options&gt;
+</pre>
+<p>
+Table 1 explains the flags and arguments.
+</p>
+<p class="table-caption" id="table1">
+  <strong>Table 1.</strong> <code>monkeyrunner</code> flags and arguments.</p>
+
+<table>
+    <tr>
+        <th>Argument</th>
+        <th>Description</th>
+    </tr>
+    <tr>
+        <td>
+            <nobr>
+                <code>-plugin &lt;plugin_jar&gt;</code>
+            </nobr>
+        </td>
+        <td>
+            (Optional) Specifies a <code>.jar</code> file containing a plugin for monkeyrunner.
+            To learn more about monkeyrunner plugins, see
+            <a href="#Plugins">Extending monkeyrunner with plugins</a>. To specify more than one
+            file, include the argument multiple times.
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <nobr>
+                <code>&lt;program_filename&gt;</code>
+            </nobr>
+        </td>
+        <td>
+            If you provide this argument, the <code>monkeyrunner</code> command runs the contents
+            of the file as a Python program. If the argument is not provided, the command starts an
+            interactive session.
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <code>&lt;program_options&gt;</code>
+        </td>
+        <td>
+            (Optional) Flags and arguments for the program in &lt;program_file&gt;.
+        </td>
+    </tr>
+</table>
+<h2 id="Help">monkeyrunner Built-in Help</h2>
+<p>
+    You can generate an API reference for monkeyrunner by running:
+</p>
+<pre>
+monkeyrunner &lt;format&gt; help.py &lt;outfile&gt;
+</pre>
+<p>
+The arguments are:
+</p>
+    <ul>
+        <li>
+            <code>&lt;format&gt;</code> is either <code>text</code> for plain text output
+            or <code>html</code> for HTML output.
+        </li>
+        <li>
+            <code>&lt;outfile&gt;</code> is a path-qualified name for the output file.
+        </li>
+    </ul>
+<h2 id="Plugins">Extending monkeyrunner with Plugins</h2>
+<p>
+    You can extend the monkeyrunner API with classes you write in the Java programming language
+    and build into one or more <code>.jar</code> files. You can use this feature to extend the
+    monkeyrunner API with your own classes or to extend the existing classes. You can also use this
+    feature to initialize the monkeyrunner environment.
+</p>
+<p>
+    To provide a plugin to monkeyrunner, invoke the <code>monkeyrunner</code> command with the
+    <code>-plugin &lt;plugin_jar&gt;</code> argument described in
+    <a href="#table1">table 1</a>.
+</p>
+<p>
+    In your plugin code, you can import and extend the the main monkeyrunner classes
+    <code>MonkeyDevice</code>, <code>MonkeyImage</code>, and <code>MonkeyRunner</code> in
+    <code>com.android.monkeyrunner</code> (see <a href="#APIClasses">The monkeyrunner API</a>).
+</p>
+<p>
+    Note that plugins do not give you access to the Android SDK. You can't import packages
+    such as <code>com.android.app</code>. This is because monkeyrunner interacts with the
+    device or emulator below the level of the framework APIs.
+</p>
+<h3>The plugin startup class</h3>
+<p>
+    The <code>.jar</code> file for a plugin can specify a class that is instantiated before
+    script processing starts. To specify this class, add the key
+    <code>MonkeyRunnerStartupRunner</code> to the <code>.jar</code> file's
+    manifest. The value should be the name of the class to run at startup. The following
+    snippet shows how you would do this within an <code>ant</code> build script:
+</p>
+<pre>
+&lt;jar jarfile=&quot;myplugin&quot; basedir="&#36;&#123;build.dir&#125;&quot;&gt;
+&lt;manifest&gt;
+&lt;attribute name=&quot;MonkeyRunnerStartupRunner&quot; value=&quot;com.myapp.myplugin&quot;/&gt;
+&lt;/manifest&gt;
+&lt;/jar&gt;
+
+
+</pre>
+<p>
+    To get access to monkeyrunner's runtime environment, the startup class can implement
+    <code>com.google.common.base.Predicate&lt;PythonInterpreter&gt;</code>. For example, this
+    class sets up some variables in the default namespace:
+</p>
+<pre>
+package com.android.example;
+
+import com.google.common.base.Predicate;
+import org.python.util.PythonInterpreter;
+
+public class Main implements Predicate&lt;PythonInterpreter&gt; {
+    &#64;Override
+    public boolean apply(PythonInterpreter anInterpreter) {
+
+        /*
+        * Examples of creating and initializing variables in the monkeyrunner environment's
+        * namespace. During execution, the monkeyrunner program can refer to the variables "newtest"
+        * and "use_emulator"
+        *
+        */
+        anInterpreter.set("newtest", "enabled");
+        anInterpreter.set("use_emulator", 1);
+
+        return true;
+    }
+}
+</pre>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 2b803424..a43e334 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -254,34 +254,45 @@
             <li><a href="<?cs var:toroot?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
           </ul>
       </li>
-      <li class="toggle-list">
-           <div>
-                <a href="<?cs var:toroot ?>guide/topics/testing/index.html">
-                   <span class="en">Testing</span>
-               </a> <span class="new">new!</span>
-           </div>
-           <ul>
-              <li><a href="<?cs var:toroot?>guide/topics/testing/testing_android.html">
-                <span class="en">Testing Fundamentals</span></a>
-              </li>
-              <li><a href="<?cs var:toroot?>guide/topics/testing/activity_testing.html">
-                <span class="en">Activity Testing</span></a>
-              </li>
-              <li><a href="<?cs var:toroot ?>guide/topics/testing/contentprovider_testing.html">
-                <span class="en">Content Provider Testing</span></a>
-              </li>
-              <li><a href="<?cs var:toroot ?>guide/topics/testing/service_testing.html">
-                <span class="en">Service Testing</span></a>
-              </li>
-              <li><a href="<?cs var:toroot ?>guide/topics/testing/what_to_test.html">
-                <span class="en">What To Test</span></a>
-              </li>
-           </ul>
-      </li>
-     <li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html">
+      <li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html">
             <span class="en">Device Administration</span>
          </a> <span class="new">new!</span>
-    </li>
+      </li>
+      <li class="toggle-list">
+           <div>
+                <a href="<?cs var:toroot?>guide/topics/testing/index.html">
+                   <span class="en">Testing</span>
+               </a>
+           </div>
+           <ul>
+              <li>
+                <a href="<?cs var:toroot?>guide/topics/testing/testing_android.html">
+                <span class="en">Testing Fundamentals</span></a>
+                <span class="new">new!</span>
+              </li>
+              <li>
+                <a href="<?cs var:toroot?>guide/topics/testing/activity_testing.html">
+                <span class="en">Activity Testing</span></a>
+                <span class="new">new!</span>
+              </li>
+              <li>
+                <a href="<?cs var:toroot?>guide/topics/testing/contentprovider_testing.html">
+                <span class="en">Content Provider Testing</span></a>
+                <span class="new">new!</span>
+              </li>
+              <li>
+                <a href="<?cs var:toroot?>guide/topics/testing/service_testing.html">
+                <span class="en">Service Testing</span></a>
+                <span class="new">new!</span>
+              </li>
+              <li>
+                <a href="<?cs var:toroot ?>guide/topics/testing/what_to_test.html">
+                <span class="en">What To Test</span></a>
+                <span class="new">new!</span>
+              </li>
+
+           </ul>
+      </li>
     </ul>
   </li>
 
@@ -336,6 +347,7 @@
                   <span class="en">Testing in Eclipse, with ADT</span>
                 </a>
               </li>
+
               <li>
                 <a href="<?cs var:toroot ?>guide/developing/testing/testing_otheride.html">
                   <span class="en">Testing in Other IDEs</span>
@@ -363,6 +375,34 @@
               <li><a href="<?cs var:toroot ?>guide/developing/tools/layoutopt.html">layoutopt</a></li>
               <li><a href="<?cs var:toroot ?>guide/developing/tools/othertools.html#mksdcard">mksdcard</a></li>
               <li><a href="<?cs var:toroot ?>guide/developing/tools/monkey.html">Monkey</a></li>
+                            <li class="toggle-list">
+                 <div>
+                     <a href="<?cs var:toroot?>guide/developing/tools/monkeyrunner_concepts.html">
+                     <span class="en">monkeyrunner</span>
+                  </a>
+                      <span class="new">new!</span>
+                  </div>
+                  <ul>
+                      <li>
+                          <a href="<?cs var:toroot?>guide/developing/tools/MonkeyDevice.html">
+                                <span class="en">MonkeyDevice</span>
+                        </a>
+                        <span class="new">new!</span>
+                    </li>
+                    <li>
+                        <a href="<?cs var:toroot?>guide/developing/tools/MonkeyImage.html">
+                            <span class="en">MonkeyImage</span>
+                        </a>
+                        <span class="new">new!</span>
+                    </li>
+                    <li>
+                        <a href="<?cs var:toroot?>guide/developing/tools/MonkeyRunner.html">
+                            <span class="en">MonkeyRunner</span>
+                        </a>
+                        <span class="new">new!</span>
+                    </li>
+                  </ul>
+              </li>
               <li><a href="<?cs var:toroot ?>guide/developing/tools/adb.html#sqlite">sqlite3</a></li>
               <li><a href="<?cs var:toroot ?>guide/developing/tools/traceview.html" >Traceview</a></li>
               <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html" >zipalign</a></li>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 1d6ab25..d868599 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -129,9 +129,8 @@
 
   <tr>
     <td><code>raw/</code></td>
-    <td><p>Arbitrary files to save in their raw form. Files in here are not compressed by the
-system. To open these resources with a raw {@link java.io.InputStream}, call {@link
-android.content.res.Resources#openRawResource(int)
+    <td><p>Arbitrary files to save in their raw form. To open these resources with a raw
+{@link java.io.InputStream}, call {@link android.content.res.Resources#openRawResource(int)
 Resources.openRawResource()} with the resource ID, which is {@code R.raw.<em>filename</em>}.</p>
       <p>However, if you need access to original file names and file hierarchy, you might consider
 saving some resources in the {@code
diff --git a/docs/html/guide/topics/testing/index.jd b/docs/html/guide/topics/testing/index.jd
index b75656f..762a897 100644
--- a/docs/html/guide/topics/testing/index.jd
+++ b/docs/html/guide/topics/testing/index.jd
@@ -59,6 +59,20 @@
         which guides you through a more complex testing scenario.
     </li>
 </ul>
+<h4>Tools</h4>
+<ul>
+    <li>
+        The
+        <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>,
+        usually called Monkey, is a command-line tool that sends pseudo-random
+        streams of keystrokes, touches, and gestures to a device.
+    </li>
+    <li>
+     The <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a> tool
+        is an API and execution environment. You use monkeyrunner with Python programs
+        to test applications and devices.
+    </li>
+</ul>
 <h4>Samples</h4>
 <ul>
     <li>
diff --git a/docs/html/guide/topics/testing/testing_android.jd b/docs/html/guide/topics/testing/testing_android.jd
index 2a4c949..d4b0dcc 100755
--- a/docs/html/guide/topics/testing/testing_android.jd
+++ b/docs/html/guide/topics/testing/testing_android.jd
@@ -38,7 +38,7 @@
         <a href="#TestResults">Seeing Test Results</a>
     </li>
     <li>
-        <a href="#Monkeys">Monkey and MonkeyRunner</a>
+        <a href="#Monkeys">monkey and monkeyrunner</a>
     </li>
     <li>
        <a href="#PackageNames">Working With Package Names</a>
@@ -77,6 +77,13 @@
           <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
           Testing in Other IDEs</a>
         </li>
+        <li>
+          <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">
+          monkeyrunner</a>
+        </li>
+        <li>
+     <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>
+        </li>
       </ol>
   </div>
 </div>
@@ -112,10 +119,10 @@
     </li>
     <li>
         The SDK also provides
-        <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a>, an API for
-        testing devices with Jython scripts, and <a
-        href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a>, a command-line tool for
-        stress-testing UIs by sending pseudo-random events to a device.
+  <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a>, an API
+        testing devices with Python programs, and <a
+        href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>,
+        a command-line tool for stress-testing UIs by sending pseudo-random events to a device.
     </li>
 </ul>
 <p>
@@ -540,25 +547,28 @@
     <a href="{@docRoot}guide/developing/testing/testing_otheride.html#RunTestsCommand">
     Testing in Other IDEs</a>.
 </p>
-<h2 id="Monkeys">Monkey and MonkeyRunner</h2>
+<h2 id="Monkeys">monkey and monkeyrunner</h2>
 <p>
     The SDK provides two tools for functional-level application testing:
 </p>
     <ul>
         <li>
-            <a href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a> is a command-line
-            tool that sends pseudo-random streams of keystrokes, touches, and gestures to a
-            device. You run it with the <a href="{@docRoot}guide/developing/tools/adb.html">
-            Android Debug Bridge</a> (adb) tool. You use it to stress-test your application and
-            report back errors that are encountered. You can repeat a stream of events by
-            running the tool each time with the same random number seed.
+The <a href="{@docRoot}guide/developing/tools/monkey.html">UI/Application Exerciser Monkey</a>,
+            usually called "monkey", is a command-line tool that sends pseudo-random streams of
+            keystrokes, touches, and gestures to a device. You run it with the
+            <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a> (adb) tool.
+            You use it to stress-test your application and report back errors that are encountered.
+            You can repeat a stream of events by running the tool each time with the same random
+            number seed.
         </li>
         <li>
-            <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a> is a
-            Jython API that you use in test programs written in Python. The API includes functions
-            for connecting to a device, installing and uninstalling packages, taking screenshots,
-            comparing two images, and running a test package against an application. Using the API
-            with Python, you can write a wide range of large, powerful, and complex tests.
+    The <a href="{@docRoot}guide/developing/tools/monkeyrunner_concepts.html">monkeyrunner</a> tool
+            is an API and execution environment for test programs written in Python. The API
+            includes functions for connecting to a device, installing and uninstalling packages,
+            taking screenshots, comparing two images, and running a test package against an
+            application. Using the API, you can write a wide range of large, powerful, and complex
+            tests. You run programs that use the API with the <code>monkeyrunner</code> command-line
+            tool.
         </li>
     </ul>
 <h2 id="PackageNames">Working With Package names</h2>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 3b4ccb0e..cef057e 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:9.7,16.4,0.1,40.4,33.4&chl=
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:7.9,15.0,0.1,40.8,36.2&chl=
 Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
 6fad0c" />
 
@@ -62,13 +62,13 @@
   <th>API Level</th>
   <th>Distribution</th>
 </tr>
-<tr><td>Android 1.5</td><td>3</td><td>9.7%</td></tr>
-<tr><td>Android 1.6</td><td>4</td><td>16.4%</td></tr>
-<tr><td>Android 2.1</td><td>7</td><td>40.4%</td></tr>
-<tr><td>Android 2.2</td><td>8</td><td>33.4%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>7.9%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>15.0%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>40.8%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>36.2%</td></tr>
 </table>
 
-<p><em>Data collected during two weeks ending on October 1, 2010</em></p>
+<p><em>Data collected during two weeks ending on November 1, 2010</em></p>
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 
 </div><!-- end dashboard-panel -->
@@ -96,19 +96,18 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
-chxl=0:|2010/04/01|04/15|05/01|05/15|06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|2010/10/01|1:|0
-%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&
-chxtc=0,5&chd=t:99.6,99.6,99.6,99.7,100.6,101.1,99.9,100.0,100.0,99.8,99.9,100.0,100.0|61.5,61.7,62.
-3,63.5,73.0,76.4,78.6,81.1,84.5,86.6,88.0,89.3,90.3|29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1,68.
-2,70.4,72.2,73.9|4.0,28.3,32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8|0.0,0.0,0.0,0.0,0.8
-,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4&chm=tAndroid+1.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid+1
-.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|tAndroid+2.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid+2
-.1,2f4708,3,1,15,,t:-30:-40|b,89cf19,3,4,0|tAndroid+2.2,131d02,4,9,15,,t::-5|B,6fad0c,4,5,0&chg=7,25
-&chdl=Android+1.5|Android+1.6|Android+2.0.1|Android+2.1|Android+2.2&chco=add274,9ad145,84c323,6ba213
-,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
+chxl=0%3A%7C2010/05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/
+01%7C10/15%7C2010/11/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25
+%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.9,99.3,100.3,100.8,99.7,99.
+8,99.8,99.7,99.8,99.9,99.9,99.9,99.9|61.6,63.1,72.7,76.1,78.4,80.9,84.3,86.5,87.9,89.2,90.2,91.1,92.
+0|32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8,75.3,77.0|0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3,
+27.8,32.1,33.4,34.5,36.2&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,
+5b831d,1,0,15,,t::-5|b,aadb5e,1,2,0|tAndroid%202.1,38540b,2,0,15,,t::-5|b,91da1e,2,3,0|tAndroid%202.
+2,131d02,3,7,15,,t::-5|B,6fad0c,3,4,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1|
+Android%202.2&chco=add274,94d134,73ad18,507d08" />
 
-<p><em>Last historical dataset collected during two weeks ending on October 1, 2010</em></p>
+<p><em>Last historical dataset collected during two weeks ending on November 1, 2010</em></p>
 
 
 </div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/faq/framework.jd b/docs/html/resources/faq/framework.jd
index f4b8db0..4a7a3fc 100644
--- a/docs/html/resources/faq/framework.jd
+++ b/docs/html/resources/faq/framework.jd
@@ -68,12 +68,17 @@
 <p>For sharing complex non-persistent user-defined objects for short
 duration, the following approaches are recommended:
 </p>
-  <h4>The android.app.Application class</h4>
-  <p>The android.app.Application is a base class for those who need to
-maintain global application state. It can be accessed via
-getApplication() from any Activity or Service. It has a couple of
-life-cycle methods and will be instantiated by Android automatically if
-your register it in AndroidManifest.xml.</p>
+  <h4>Singleton class</h4>
+  <p>You can take advantage of the fact that your application
+components run in the same process through the use of a singleton.
+This is a class that is designed to have only one instance.  It
+has a static method with a name such as <code>getInstance()</code>
+that returns the instance; the first time this method is called,
+it creates the global instance.  Because all callers get the same
+instance, they can use this as a point of interaction.  For
+example activity A may retrieve the instance and call setValue(3);
+later activity B may retrieve the instance and call getValue() to
+retrieve the last set value.</p>
 
   <h4>A public static field/method</h4>
   <p>An alternate way to make data accessible across Activities/Services is to use <em>public static</em>
@@ -90,18 +95,6 @@
 intent extras. The recipient activity retrieves the object using this
 key.</p>
 
-  <h4>A Singleton class</h4>
-  <p>There are advantages to using a static Singleton, such as you can
-refer to them without casting getApplication() to an
-application-specific class, or going to the trouble of hanging an
-interface on all your Application subclasses so that your various
-modules can refer to that interface instead. </p>
-<p>But, the life cycle of a static is not well under your control; so
-to abide by the life-cycle model, the application class should initiate and
-tear down these static objects in the onCreate() and onTerminate() methods
-of the Application Class</p>
-</p>
-
 <h3>Persistent Objects</h3>
 
 <p>Even while an application appears to continue running, the system
@@ -146,15 +139,11 @@
 <h2>If an Activity starts a remote service, is there any way for the
 Service to pass a message back to the Activity?</h2>
 
-<p>The remote service can define a callback interface and register it with the
-clients to callback into the clients. The 
-{@link android.os.RemoteCallbackList RemoteCallbackList} class provides methods to
-register and unregister clients with the service, and send and receive
-messages.</p>
-
-<p>The sample code for remote service callbacks is given in <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">ApiDemos/RemoteService</a></p>
-
+<p>See the {@link android.app.Service} documentation's for examples of
+how clients can interact with a service.  You can take advantage of the
+fact that your components run in the same process to greatly simplify
+service interaction from the generic remote case, as shown by the "Local
+Service Sample".  In some cases techniques like singletons may also make sense.
 
 
 <a name="6" id="6"></a>
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index a25fad4..289348a 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -51,7 +51,7 @@
      */
     public ColorDrawable(int color) {
         this(null);
-        mState.mBaseColor = mState.mUseColor = color;
+        setColor(color);
     }
 
     private ColorDrawable(ColorState state) {
@@ -72,6 +72,25 @@
     }
 
     /**
+     * Gets the drawable's color value.
+     *
+     * @return int The color to draw.
+     */
+    public int getColor() {
+        return mState.mUseColor;
+    }
+
+    /**
+     * Sets the drawable's color value. This action will clobber the results of prior calls to
+     * {@link #setAlpha(int)} on this object, which side-affected the underlying color.
+     *
+     * @param color The color to draw.
+     */
+    public void setColor(int color) {
+        mState.mBaseColor = mState.mUseColor = color;
+    }
+
+    /**
      * Returns the alpha value of this drawable's color.
      *
      * @return A value between 0 and 255.
@@ -131,7 +150,7 @@
     }
 
     final static class ColorState extends ConstantState {
-        int mBaseColor; // initial color. never changes
+        int mBaseColor; // base color, independent of setAlpha()
         int mUseColor;  // basecolor modulated by setAlpha()
         int mChangingConfigurations;
 
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index f8ad5cc..dfd6ac8 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -199,6 +199,7 @@
             throw new IllegalStateException("Resize only support for 1D allocations at this time.");
         }
         mRS.nAllocationResize1D(mID, dimX);
+        mRS.finish();  // Necessary because resize is fifoed and update is async.
 
         int typeID = mRS.nAllocationGetType(mID);
         mType = new Type(typeID, mRS);
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 9fd905f..1081c35 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -262,11 +262,15 @@
         DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
         DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
         DEVICE_OUT_AUX_DIGITAL = 0x400,
+        DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
+        DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
         DEVICE_OUT_DEFAULT = 0x8000,
         DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
                 DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
                 DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
-                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),
+                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL |
+                DEVICE_OUT_ANLG_DOCK_HEADSET | DEVICE_OUT_DGTL_DOCK_HEADSET |
+                DEVICE_OUT_DEFAULT),
         DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                 DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
 
@@ -309,6 +313,8 @@
         FORCE_WIRED_ACCESSORY,
         FORCE_BT_CAR_DOCK,
         FORCE_BT_DESK_DOCK,
+        FORCE_ANALOG_DOCK,
+        FORCE_DIGITAL_DOCK,
         NUM_FORCE_CONFIG,
         FORCE_DEFAULT = FORCE_NONE
     };
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index b009e1b..4fd281b 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -37,11 +37,11 @@
 // buffers.  This is the mode that will be used when CPU access to the buffer is
 // required.
 //
-// When Android native buffer use has been enabled, the OMX node must support
-// only color formats in the range [OMX_COLOR_FormatAndroidPrivateStart,
-// OMX_COLOR_FormatAndroidPrivateEnd).  The node should then expect to receive
+// When Android native buffer use has been enabled for a given port, the video
+// color format for the port is to be interpreted as an Android pixel format
+// rather than an OMX color format.  The node should then expect to receive
 // UseAndroidNativeBuffer calls (via OMX_SetParameter) rather than UseBuffer
-// calls.
+// calls for that port.
 struct EnableAndroidNativeBuffersParams {
     OMX_U32 nSize;
     OMX_VERSIONTYPE nVersion;
@@ -75,16 +75,6 @@
     OMX_BOOL bStoreMetaData;
 };
 
-// Color formats in the range [OMX_COLOR_FormatAndroidPrivateStart,
-// OMX_COLOR_FormatAndroidPrivateEnd) will be converted to a gralloc pixel
-// format when used to allocate Android native buffers via gralloc.  The
-// conversion is done by subtracting OMX_COLOR_FormatAndroidPrivateStart from
-// the color format reported by the codec.
-enum {
-    OMX_COLOR_FormatAndroidPrivateStart = 0xA0000000,
-    OMX_COLOR_FormatAndroidPrivateEnd = 0xB0000000,
-};
-
 // A pointer to this struct is passed to OMX_SetParameter when the extension
 // index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is
 // given.  This call will only be performed if a prior call was made with the
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 1f5ed7c..c692bc1 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -78,10 +78,6 @@
 
         if (dest) {
             *dest = ptr;
-        } else {
-            if (rsc->props.mLogScripts) {
-                LOGV("ScriptC::setupScript, NULL var binding address.");
-            }
         }
     }
 }
@@ -404,16 +400,14 @@
     const ScriptCState::SymbolTable_t *sym;
     ScriptC *s = (ScriptC *)pContext;
     sym = ScriptCState::lookupSymbol(name);
-    if (sym) {
-        return sym->mPtr;
+    if (!sym) {
+        sym = ScriptCState::lookupSymbolCL(name);
     }
-    sym = ScriptCState::lookupSymbolCL(name);
-    if (sym) {
-        return sym->mPtr;
+    if (!sym) {
+        sym = ScriptCState::lookupSymbolGL(name);
     }
-    s->mEnviroment.mIsThreadable = false;
-    sym = ScriptCState::lookupSymbolGL(name);
     if (sym) {
+        s->mEnviroment.mIsThreadable &= sym->threadable;
         return sym->mPtr;
     }
     LOGE("ScriptC sym lookup failed for %s", name);
@@ -425,7 +419,6 @@
 
 void ScriptCState::runCompiler(Context *rsc, ScriptC *s)
 {
-    LOGV("%p ScriptCState::runCompiler ", rsc);
     {
         StopWatch compileTimer("RenderScript compile time");
         s->mBccScript = bccCreateScript();
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index e5b5ba9..86a7ad3 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -89,6 +89,7 @@
     struct SymbolTable_t {
         const char * mName;
         void * mPtr;
+        bool threadable;
     };
     //static SymbolTable_t gSyms[];
     static const SymbolTable_t * lookupSymbol(const char *);
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 9fadee0..ecae306 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -436,120 +436,120 @@
 //                 ::= d  # double
 
 static ScriptCState::SymbolTable_t gSyms[] = {
-    { "__divsi3", (void *)&SC_divsi3 },
+    { "__divsi3", (void *)&SC_divsi3, true },
 
     // allocation
-    { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX },
-    { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY },
-    { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ },
-    { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD },
-    { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces },
-    { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation },
+    { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX, true },
+    { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY, true },
+    { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ, true },
+    { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD, true },
+    { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces, true },
+    { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation, true },
 
-    { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX },
-    { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY },
-    { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ },
+    { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX, true },
+    { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY, true },
+    { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ, true },
 
-    { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject },
-    { "_Z10rsIsObject10rs_element", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject10rs_element", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject },
-    { "_Z10rsIsObject7rs_type", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_type", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject },
-    { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject },
-    { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject },
-    { "_Z10rsIsObject9rs_script", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject9rs_script", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject },
-    { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject },
-    { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject },
-    { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject },
-    { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject },
-    { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject, true },
 
-    { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject },
-    { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject },
-    { "_Z10rsIsObject7rs_font", (void *)&SC_isObject },
+    { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_font", (void *)&SC_isObject, true },
 
 
-    { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty },
+    { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty, true },
 
 
     // Debug
-    { "_Z7rsDebugPKcf", (void *)&SC_debugF },
-    { "_Z7rsDebugPKcff", (void *)&SC_debugFv2 },
-    { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3 },
-    { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4 },
-    { "_Z7rsDebugPKcd", (void *)&SC_debugD },
-    { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4 },
-    { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3 },
-    { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2 },
-    { "_Z7rsDebugPKci", (void *)&SC_debugI32 },
-    { "_Z7rsDebugPKcj", (void *)&SC_debugU32 },
+    { "_Z7rsDebugPKcf", (void *)&SC_debugF, true },
+    { "_Z7rsDebugPKcff", (void *)&SC_debugFv2, true },
+    { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3, true },
+    { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4, true },
+    { "_Z7rsDebugPKcd", (void *)&SC_debugD, true },
+    { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4, true },
+    { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3, true },
+    { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2, true },
+    { "_Z7rsDebugPKci", (void *)&SC_debugI32, true },
+    { "_Z7rsDebugPKcj", (void *)&SC_debugU32, true },
     // Both "long" and "unsigned long" need to be redirected to their
     // 64-bit counterparts, since we have hacked Slang to use 64-bit
     // for "long" on Arm (to be similar to Java).
-    { "_Z7rsDebugPKcl", (void *)&SC_debugLL64 },
-    { "_Z7rsDebugPKcm", (void *)&SC_debugULL64 },
-    { "_Z7rsDebugPKcx", (void *)&SC_debugLL64 },
-    { "_Z7rsDebugPKcy", (void *)&SC_debugULL64 },
-    { "_Z7rsDebugPKcPKv", (void *)&SC_debugP },
+    { "_Z7rsDebugPKcl", (void *)&SC_debugLL64, true },
+    { "_Z7rsDebugPKcm", (void *)&SC_debugULL64, true },
+    { "_Z7rsDebugPKcx", (void *)&SC_debugLL64, true },
+    { "_Z7rsDebugPKcy", (void *)&SC_debugULL64, true },
+    { "_Z7rsDebugPKcPKv", (void *)&SC_debugP, true },
 
     // RS Math
-    { "_Z6rsRandi", (void *)&SC_randi },
-    { "_Z6rsRandii", (void *)&SC_randi2 },
-    { "_Z6rsRandf", (void *)&SC_randf },
-    { "_Z6rsRandff", (void *)&SC_randf2 },
-    { "_Z6rsFracf", (void *)&SC_frac },
+    { "_Z6rsRandi", (void *)&SC_randi, true },
+    { "_Z6rsRandii", (void *)&SC_randi2, true },
+    { "_Z6rsRandf", (void *)&SC_randf, true },
+    { "_Z6rsRandff", (void *)&SC_randf2, true },
+    { "_Z6rsFracf", (void *)&SC_frac, true },
 
     // time
-    { "_Z8rsSecondv", (void *)&SC_second },
-    { "_Z8rsMinutev", (void *)&SC_minute },
-    { "_Z6rsHourv", (void *)&SC_hour },
-    { "_Z5rsDayv", (void *)&SC_day },
-    { "_Z7rsMonthv", (void *)&SC_month },
-    { "_Z6rsYearv", (void *)&SC_year },
-    { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis },
-    { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos },
-    { "_Z7rsGetDtv", (void*)&SC_getDt },
+    { "_Z8rsSecondv", (void *)&SC_second, true },
+    { "_Z8rsMinutev", (void *)&SC_minute, true },
+    { "_Z6rsHourv", (void *)&SC_hour, true },
+    { "_Z5rsDayv", (void *)&SC_day, true },
+    { "_Z7rsMonthv", (void *)&SC_month, true },
+    { "_Z6rsYearv", (void *)&SC_year, true },
+    { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis, true },
+    { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos, true },
+    { "_Z7rsGetDtv", (void*)&SC_getDt, false },
 
-    { "_Z14rsSendToClienti", (void *)&SC_toClient },
-    { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2 },
-    { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking },
-    { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2 },
+    { "_Z14rsSendToClienti", (void *)&SC_toClient, false },
+    { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2, false },
+    { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking, false },
+    { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2, false },
 
-    { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach },
-    //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2 },
+    { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach, false },
+    //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2, true },
 
 ////////////////////////////////////////////////////////////////////
 
-    //{ "sinf_fast", (void *)&SC_sinf_fast },
-    //{ "cosf_fast", (void *)&SC_cosf_fast },
+    //{ "sinf_fast", (void *)&SC_sinf_fast, true },
+    //{ "cosf_fast", (void *)&SC_cosf_fast, true },
 
-    { NULL, NULL }
+    { NULL, NULL, false }
 };
 
 const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym)
diff --git a/libs/rs/rsScriptC_LibCL.cpp b/libs/rs/rsScriptC_LibCL.cpp
index 1b1a752..7031f84 100644
--- a/libs/rs/rsScriptC_LibCL.cpp
+++ b/libs/rs/rsScriptC_LibCL.cpp
@@ -88,11 +88,6 @@
     return tanf(v * M_PI);
 }
 
-    //{ "logb", (void *)& },
-    //{ "mad", (void *)& },
-    //{ "nan", (void *)& },
-    //{ "tgamma", (void *)& },
-
 //////////////////////////////////////////////////////////////////////////////
 // Integer
 //////////////////////////////////////////////////////////////////////////////
@@ -198,105 +193,105 @@
 
 static ScriptCState::SymbolTable_t gSyms[] = {
     // OpenCL math
-    { "_Z4acosf", (void *)&acosf },
-    { "_Z5acoshf", (void *)&acoshf },
-    { "_Z6acospif", (void *)&SC_acospi },
-    { "_Z4asinf", (void *)&asinf },
-    { "_Z5asinhf", (void *)&asinhf },
-    { "_Z6asinpif", (void *)&SC_asinpi },
-    { "_Z4atanf", (void *)&atanf },
-    { "_Z5atan2ff", (void *)&atan2f },
-    { "_Z6atanpif", (void *)&SC_atanpi },
-    { "_Z7atan2piff", (void *)&SC_atan2pi },
-    { "_Z4cbrtf", (void *)&cbrtf },
-    { "_Z4ceilf", (void *)&ceilf },
-    { "_Z8copysignff", (void *)&copysignf },
-    { "_Z3cosf", (void *)&cosf },
-    { "_Z4coshf", (void *)&coshf },
-    { "_Z5cospif", (void *)&SC_cospi },
-    { "_Z4erfcf", (void *)&erfcf },
-    { "_Z3erff", (void *)&erff },
-    { "_Z3expf", (void *)&expf },
-    { "_Z4exp2f", (void *)&exp2f },
-    { "_Z5exp10f", (void *)&SC_exp10 },
-    { "_Z5expm1f", (void *)&expm1f },
-    { "_Z4fabsf", (void *)&fabsf },
-    { "_Z4fdimff", (void *)&fdimf },
-    { "_Z5floorf", (void *)&floorf },
-    { "_Z3fmafff", (void *)&fmaf },
-    { "_Z4fmaxff", (void *)&fmaxf },
-    { "_Z4fminff", (void *)&fminf },  // float fmin(float, float)
-    { "_Z4fmodff", (void *)&fmodf },
-    { "_Z5fractfPf", (void *)&SC_fract },
-    { "_Z5frexpfPi", (void *)&frexpf },
-    { "_Z5hypotff", (void *)&hypotf },
-    { "_Z5ilogbf", (void *)&ilogbf },
-    { "_Z5ldexpfi", (void *)&ldexpf },
-    { "_Z6lgammaf", (void *)&lgammaf },
-    { "_Z3logf", (void *)&logf },
-    { "_Z4log2f", (void *)&SC_log2 },
-    { "_Z5log10f", (void *)&log10f },
-    { "_Z5log1pf", (void *)&log1pf },
-    //{ "logb", (void *)& },
-    //{ "mad", (void *)& },
-    { "modf", (void *)&modff },
-    //{ "nan", (void *)& },
-    { "_Z9nextafterff", (void *)&nextafterf },
-    { "_Z3powff", (void *)&powf },
-    { "_Z4pownfi", (void *)&SC_pown },
-    { "_Z4powrff", (void *)&SC_powr },
-    { "_Z9remainderff", (void *)&remainderf },
-    { "remquo", (void *)&remquof },
-    { "_Z4rintf", (void *)&rintf },
-    { "_Z5rootnfi", (void *)&SC_rootn },
-    { "_Z5roundf", (void *)&roundf },
-    { "_Z5rsqrtf", (void *)&SC_rsqrt },
-    { "_Z3sinf", (void *)&sinf },
-    { "sincos", (void *)&SC_sincos },
-    { "_Z4sinhf", (void *)&sinhf },
-    { "_Z5sinpif", (void *)&SC_sinpi },
-    { "_Z4sqrtf", (void *)&sqrtf },
-    { "_Z3tanf", (void *)&tanf },
-    { "_Z4tanhf", (void *)&tanhf },
-    { "_Z5tanpif", (void *)&SC_tanpi },
-    //{ "tgamma", (void *)& },
-    { "_Z5truncf", (void *)&truncf },
+    { "_Z4acosf", (void *)&acosf, true },
+    { "_Z5acoshf", (void *)&acoshf, true },
+    { "_Z6acospif", (void *)&SC_acospi, true },
+    { "_Z4asinf", (void *)&asinf, true },
+    { "_Z5asinhf", (void *)&asinhf, true },
+    { "_Z6asinpif", (void *)&SC_asinpi, true },
+    { "_Z4atanf", (void *)&atanf, true },
+    { "_Z5atan2ff", (void *)&atan2f, true },
+    { "_Z6atanpif", (void *)&SC_atanpi, true },
+    { "_Z7atan2piff", (void *)&SC_atan2pi, true },
+    { "_Z4cbrtf", (void *)&cbrtf, true },
+    { "_Z4ceilf", (void *)&ceilf, true },
+    { "_Z8copysignff", (void *)&copysignf, true },
+    { "_Z3cosf", (void *)&cosf, true },
+    { "_Z4coshf", (void *)&coshf, true },
+    { "_Z5cospif", (void *)&SC_cospi, true },
+    { "_Z4erfcf", (void *)&erfcf, true },
+    { "_Z3erff", (void *)&erff, true },
+    { "_Z3expf", (void *)&expf, true },
+    { "_Z4exp2f", (void *)&exp2f, true },
+    { "_Z5exp10f", (void *)&SC_exp10, true },
+    { "_Z5expm1f", (void *)&expm1f, true },
+    { "_Z4fabsf", (void *)&fabsf, true },
+    { "_Z4fdimff", (void *)&fdimf, true },
+    { "_Z5floorf", (void *)&floorf, true },
+    { "_Z3fmafff", (void *)&fmaf, true },
+    { "_Z4fmaxff", (void *)&fmaxf, true },
+    { "_Z4fminff", (void *)&fminf, true },  // float fmin(float, float)
+    { "_Z4fmodff", (void *)&fmodf, true },
+    { "_Z5fractfPf", (void *)&SC_fract, true },
+    { "_Z5frexpfPi", (void *)&frexpf, true },
+    { "_Z5hypotff", (void *)&hypotf, true },
+    { "_Z5ilogbf", (void *)&ilogbf, true },
+    { "_Z5ldexpfi", (void *)&ldexpf, true },
+    { "_Z6lgammaf", (void *)&lgammaf, true },
+    { "_Z3logf", (void *)&logf, true },
+    { "_Z4log2f", (void *)&SC_log2, true },
+    { "_Z5log10f", (void *)&log10f, true },
+    { "_Z5log1pf", (void *)&log1pf, true },
+    //{ "logb", (void *)&, true },
+    //{ "mad", (void *)&, true },
+    { "modf", (void *)&modff, true },
+    //{ "nan", (void *)&, true },
+    { "_Z9nextafterff", (void *)&nextafterf, true },
+    { "_Z3powff", (void *)&powf, true },
+    { "_Z4pownfi", (void *)&SC_pown, true },
+    { "_Z4powrff", (void *)&SC_powr, true },
+    { "_Z9remainderff", (void *)&remainderf, true },
+    { "remquo", (void *)&remquof, true },
+    { "_Z4rintf", (void *)&rintf, true },
+    { "_Z5rootnfi", (void *)&SC_rootn, true },
+    { "_Z5roundf", (void *)&roundf, true },
+    { "_Z5rsqrtf", (void *)&SC_rsqrt, true },
+    { "_Z3sinf", (void *)&sinf, true },
+    { "sincos", (void *)&SC_sincos, true },
+    { "_Z4sinhf", (void *)&sinhf, true },
+    { "_Z5sinpif", (void *)&SC_sinpi, true },
+    { "_Z4sqrtf", (void *)&sqrtf, true },
+    { "_Z3tanf", (void *)&tanf, true },
+    { "_Z4tanhf", (void *)&tanhf, true },
+    { "_Z5tanpif", (void *)&SC_tanpi, true },
+    //{ "tgamma", (void *)&, true },
+    { "_Z5truncf", (void *)&truncf, true },
 
     // OpenCL Int
-    { "_Z3absi", (void *)&SC_abs_i32 },
-    { "_Z3abss", (void *)&SC_abs_i16 },
-    { "_Z3absc", (void *)&SC_abs_i8 },
-    { "_Z3clzj", (void *)&SC_clz_u32 },
-    { "_Z3clzt", (void *)&SC_clz_u16 },
-    { "_Z3clzh", (void *)&SC_clz_u8 },
-    { "_Z3clzi", (void *)&SC_clz_i32 },
-    { "_Z3clzs", (void *)&SC_clz_i16 },
-    { "_Z3clzc", (void *)&SC_clz_i8 },
-    { "_Z3maxjj", (void *)&SC_max_u32 },
-    { "_Z3maxtt", (void *)&SC_max_u16 },
-    { "_Z3maxhh", (void *)&SC_max_u8 },
-    { "_Z3maxii", (void *)&SC_max_i32 },
-    { "_Z3maxss", (void *)&SC_max_i16 },
-    { "_Z3maxcc", (void *)&SC_max_i8 },
-    { "_Z3minjj", (void *)&SC_min_u32 },
-    { "_Z3mintt", (void *)&SC_min_u16 },
-    { "_Z3minhh", (void *)&SC_min_u8 },
-    { "_Z3minii", (void *)&SC_min_i32 },
-    { "_Z3minss", (void *)&SC_min_i16 },
-    { "_Z3mincc", (void *)&SC_min_i8 },
+    { "_Z3absi", (void *)&SC_abs_i32, true },
+    { "_Z3abss", (void *)&SC_abs_i16, true },
+    { "_Z3absc", (void *)&SC_abs_i8, true },
+    { "_Z3clzj", (void *)&SC_clz_u32, true },
+    { "_Z3clzt", (void *)&SC_clz_u16, true },
+    { "_Z3clzh", (void *)&SC_clz_u8, true },
+    { "_Z3clzi", (void *)&SC_clz_i32, true },
+    { "_Z3clzs", (void *)&SC_clz_i16, true },
+    { "_Z3clzc", (void *)&SC_clz_i8, true },
+    { "_Z3maxjj", (void *)&SC_max_u32, true },
+    { "_Z3maxtt", (void *)&SC_max_u16, true },
+    { "_Z3maxhh", (void *)&SC_max_u8, true },
+    { "_Z3maxii", (void *)&SC_max_i32, true },
+    { "_Z3maxss", (void *)&SC_max_i16, true },
+    { "_Z3maxcc", (void *)&SC_max_i8, true },
+    { "_Z3minjj", (void *)&SC_min_u32, true },
+    { "_Z3mintt", (void *)&SC_min_u16, true },
+    { "_Z3minhh", (void *)&SC_min_u8, true },
+    { "_Z3minii", (void *)&SC_min_i32, true },
+    { "_Z3minss", (void *)&SC_min_i16, true },
+    { "_Z3mincc", (void *)&SC_min_i8, true },
 
     // OpenCL 6.11.4
-    { "_Z5clampfff", (void *)&SC_clamp_f32 },
-    { "_Z7degreesf", (void *)&SC_degrees },
-    { "_Z3maxff", (void *)&SC_max_f32 },
-    { "_Z3minff", (void *)&SC_min_f32 },
-    { "_Z3mixfff", (void *)&SC_mix_f32 },
-    { "_Z7radiansf", (void *)&SC_radians },
-    { "_Z4stepff", (void *)&SC_step_f32 },
-    //{ "smoothstep", (void *)& },
-    { "_Z4signf", (void *)&SC_sign_f32 },
+    { "_Z5clampfff", (void *)&SC_clamp_f32, true },
+    { "_Z7degreesf", (void *)&SC_degrees, true },
+    { "_Z3maxff", (void *)&SC_max_f32, true },
+    { "_Z3minff", (void *)&SC_min_f32, true },
+    { "_Z3mixfff", (void *)&SC_mix_f32, true },
+    { "_Z7radiansf", (void *)&SC_radians, true },
+    { "_Z4stepff", (void *)&SC_step_f32, true },
+    //{ "smoothstep", (void *)&, true },
+    { "_Z4signf", (void *)&SC_sign_f32, true },
 
-    { NULL, NULL }
+    { NULL, NULL, false }
 };
 
 const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolCL(const char *sym)
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index b991cab..5b07e7b 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -441,53 +441,53 @@
 //                 ::= d  # double
 
 static ScriptCState::SymbolTable_t gSyms[] = {
-    { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment },
-    { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore },
-    { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex },
-    { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster },
-    { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler },
-    { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture },
+    { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment, false },
+    { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore, false },
+    { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex, false },
+    { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster, false },
+    { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler, false },
+    { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture, false },
 
-    { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix },
-    { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix },
-    { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix },
+    { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix, false },
+    { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix, false },
+    { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix, false },
 
-    { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix },
+    { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix, false },
 
-    { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor },
+    { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor, false },
 
-    { "_Z11rsgGetWidthv", (void *)&SC_getWidth },
-    { "_Z12rsgGetHeightv", (void *)&SC_getHeight },
+    { "_Z11rsgGetWidthv", (void *)&SC_getWidth, false },
+    { "_Z12rsgGetHeightv", (void *)&SC_getHeight, false },
 
-    { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2 },
-    { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture },
-    { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject },
+    { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2, false },
+    { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture, false },
+    { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject, false },
 
-    { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect },
-    { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad },
-    { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords },
-    { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace },
+    { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect, false },
+    { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad, false },
+    { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords, false },
+    { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace, false },
 
-    { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh },
-    { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive },
-    { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange },
-    { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox },
+    { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh, false },
+    { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive, false },
+    { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange, false },
+    { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox, false },
 
-    { "_Z13rsgClearColorffff", (void *)&SC_ClearColor },
-    { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth },
+    { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false },
+    { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false },
 
-    { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText },
-    { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc },
-    { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText },
-    { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc },
+    { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText, false },
+    { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc, false },
+    { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText, false },
+    { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc, false },
 
-    { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont },
-    { "_Z12rsgFontColorffff", (void *)&SC_FontColor },
+    { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont, false },
+    { "_Z12rsgFontColorffff", (void *)&SC_FontColor, false },
 
     // misc
-    { "_Z5colorffff", (void *)&SC_color },
+    { "_Z5colorffff", (void *)&SC_color, false },
 
-    { NULL, NULL }
+    { NULL, NULL, false }
 };
 
 const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolGL(const char *sym)
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b936c4d..58d4c56 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -1408,8 +1408,13 @@
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
     int32_t eventType = POWER_MANAGER_BUTTON_EVENT;
-    if (eventEntry->type == EventEntry::TYPE_MOTION) {
+    switch (eventEntry->type) {
+    case EventEntry::TYPE_MOTION: {
         const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
+        if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+            return;
+        }
+
         if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
             switch (motionEntry->action) {
             case AMOTION_EVENT_ACTION_DOWN:
@@ -1427,6 +1432,15 @@
                 break;
             }
         }
+        break;
+    }
+    case EventEntry::TYPE_KEY: {
+        const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
+        if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+            return;
+        }
+        break;
+    }
     }
 
     CommandEntry* commandEntry = postCommandLocked(
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 23f34d2..a49bb37 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -307,10 +307,13 @@
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+
         intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
         context.registerReceiver(mReceiver, intentFilter);
 
         // Register for media button intent broadcasts.
@@ -1816,6 +1819,12 @@
                     case Intent.EXTRA_DOCK_STATE_CAR:
                         config = AudioSystem.FORCE_BT_CAR_DOCK;
                         break;
+                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
+                        config = AudioSystem.FORCE_ANALOG_DOCK;
+                        break;
+                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
+                        config = AudioSystem.FORCE_DIGITAL_DOCK;
+                        break;
                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
                     default:
                         config = AudioSystem.FORCE_NONE;
@@ -1927,6 +1936,32 @@
                         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
                     }
                 }
+            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
+                int state = intent.getIntExtra("state", 0);
+                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
+                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+                if (state == 0 && isConnected) {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+                } else if (state == 1 && !isConnected)  {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
+                }
+            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
+                int state = intent.getIntExtra("state", 0);
+                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
+                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+                if (state == 0 && isConnected) {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+                } else if (state == 1 && !isConnected)  {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+                }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                 synchronized (mScoClients) {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a4818ff..5442791 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -243,6 +243,8 @@
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
     public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+    public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
+    public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
     public static final int DEVICE_OUT_DEFAULT = 0x8000;
     // input devices
     public static final int DEVICE_IN_COMMUNICATION = 0x10000;
@@ -273,6 +275,8 @@
     public static final int FORCE_WIRED_ACCESSORY = 5;
     public static final int FORCE_BT_CAR_DOCK = 6;
     public static final int FORCE_BT_DESK_DOCK = 7;
+    public static final int FORCE_ANALOG_DOCK = 8;
+    public static final int FORCE_DIGITAL_DOCK = 9;
     public static final int FORCE_DEFAULT = FORCE_NONE;
 
     // usage for serForceUse
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 381b77a..532a2df 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -83,6 +83,11 @@
     private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U;
     private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL;
 
+    // Drm file types
+    public static final int FILE_TYPE_FL      = 51;
+    private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL;
+    private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL;
+
     // Other popular file types
     public static final int FILE_TYPE_TEXT          = 100;
     public static final int FILE_TYPE_HTML          = 101;
@@ -189,6 +194,8 @@
         addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST);
         addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST);
 
+        addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl");
+
         addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT);
         addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
         addFileType("HTML", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
@@ -222,6 +229,11 @@
                 fileType <= LAST_PLAYLIST_FILE_TYPE);
     }
 
+    public static boolean isDrmFileType(int fileType) {
+        return (fileType >= FIRST_DRM_FILE_TYPE &&
+                fileType <= LAST_DRM_FILE_TYPE);
+    }
+
     public static MediaFileType getFileType(String path) {
         int lastDot = path.lastIndexOf(".");
         if (lastDot < 0)
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index e5fa0f8..5aabddf 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -26,6 +26,7 @@
 import android.content.IContentProvider;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.drm.DrmManagerClient;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.Process;
@@ -360,6 +361,7 @@
     private ArrayList<FileCacheEntry> mPlayLists;
     private HashMap<String, Uri> mGenreCache;
 
+    private DrmManagerClient mDrmManagerClient = null;
 
     public MediaScanner(Context c) {
         native_setup();
@@ -447,6 +449,11 @@
                 }
             }
 
+            if (System.getProperty("drm.service.enabled").equals("true")
+                    && MediaFile.isDrmFileType(mFileType)) {
+                mFileType = getFileTypeFromDrm(path);
+            }
+
             String key = path;
             if (mCaseInsensitivePaths) {
                 key = path.toLowerCase();
@@ -874,6 +881,27 @@
             }
         }
 
+        private int getFileTypeFromDrm(String path) {
+            if (!System.getProperty("drm.service.enabled").equals("true")) {
+                return 0;
+            }
+
+            int resultFileType = 0;
+
+            if (mDrmManagerClient == null) {
+                mDrmManagerClient = new DrmManagerClient(mContext);
+            }
+
+            if (mDrmManagerClient.canHandle(path, null)) {
+                String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
+                if (drmMimetype != null) {
+                    mMimeType = drmMimetype;
+                    resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype);
+                }
+            }
+            return resultFileType;
+        }
+
     }; // end of anonymous MediaScannerClient instance
 
     private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 51647434..57ab3a1 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -23,6 +23,7 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.Files;
@@ -45,6 +46,7 @@
     private final String mVolumeName;
     private final Uri mObjectsUri;
     private final String mMediaStoragePath;
+    private final String mExternalStoragePath;
 
     // true if the database has been modified in the current MTP session
     private boolean mDatabaseModified;
@@ -77,7 +79,6 @@
             Files.FileColumns.DATE_MODIFIED, // 5
     };
     private static final String ID_WHERE = Files.FileColumns._ID + "=?";
-    private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
     private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                             + Files.FileColumns.FORMAT + "=?";
@@ -98,6 +99,7 @@
         mMediaProvider = context.getContentResolver().acquireProvider("media");
         mVolumeName = volumeName;
         mMediaStoragePath = storagePath;
+        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
         mObjectsUri = Files.getMtpObjectsUri(volumeName);
         mMediaScanner = new MediaScanner(context);
         openDevicePropertiesDatabase(context);
@@ -112,6 +114,16 @@
         }
     }
 
+    private String externalToMediaPath(String path) {
+        // convert external storage path to media path
+        if (path != null && mMediaStoragePath != null
+                && mExternalStoragePath != null
+                && path.startsWith(mExternalStoragePath)) {
+            path = mMediaStoragePath + path.substring(mExternalStoragePath.length());
+        }
+        return path;
+    }
+
     private void openDevicePropertiesDatabase(Context context) {
         mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
         int version = mDevicePropDb.getVersion();
@@ -482,7 +494,7 @@
         try {
             c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null);
             if (c != null && c.moveToNext()) {
-                path = c.getString(1);
+                path = externalToMediaPath(c.getString(1));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in getObjectFilePath", e);
@@ -763,7 +775,7 @@
                 return true;
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in getObjectProperty", e);
+            Log.e(TAG, "RemoteException in getObjectInfo", e);
         } finally {
             if (c != null) {
                 c.close();
@@ -786,7 +798,7 @@
             c = mMediaProvider.query(mObjectsUri, PATH_SIZE_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null);
             if (c != null && c.moveToNext()) {
-                String path = c.getString(1);
+                String path = externalToMediaPath(c.getString(1));
                 path.getChars(0, path.length(), outFilePath, 0);
                 outFilePath[path.length()] = 0;
                 outFileLength[0] = c.getLong(2);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0d8abe2..9e1b436 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1710,16 +1710,11 @@
         return err;
     }
 
-    // Check that the color format is in the correct range.
-    CHECK(OMX_COLOR_FormatAndroidPrivateStart <= def.format.video.eColorFormat);
-    CHECK(def.format.video.eColorFormat < OMX_COLOR_FormatAndroidPrivateEnd);
-
     err = native_window_set_buffers_geometry(
             mNativeWindow.get(),
             def.format.video.nFrameWidth,
             def.format.video.nFrameHeight,
-            def.format.video.eColorFormat
-                - OMX_COLOR_FormatAndroidPrivateStart);
+            def.format.video.eColorFormat);
 
     if (err != 0) {
         LOGE("native_window_set_buffers_geometry failed: %s (%d)",
@@ -2109,6 +2104,17 @@
                     CODEC_LOGV(
                             "output crop (%ld, %ld, %ld, %ld)",
                             rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
+
+                    if (mNativeWindow != NULL) {
+                        android_native_rect_t crop;
+                        crop.left = rect.nLeft;
+                        crop.top = rect.nTop;
+                        crop.right = crop.left + rect.nWidth - 1;
+                        crop.bottom = crop.top + rect.nHeight - 1;
+
+                        CHECK_EQ(0, native_window_set_crop(
+                                    mNativeWindow.get(), &crop));
+                    }
                 } else {
                     CODEC_LOGE("getConfig(OMX_IndexConfigCommonOutputCrop) "
                                "returned error 0x%08x", err);
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 1629e9f..6c05e03 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -39,7 +39,7 @@
         ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
         ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
         ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
-        ".mkv", ".mka", ".webm", ".ts"
+        ".mkv", ".mka", ".webm", ".ts", ".fl"
     };
     static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
index 91dc2b2..0f1aa4e 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -145,7 +145,7 @@
                 case KeyEvent.KEYCODE_MEDIA_PLAY:
                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                    /* Suppress PLAYPAUSE toggle when phone is ringing or
+                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
                      * in-call to avoid music playback */
                     if (mTelephonyManager == null) {
                         mTelephonyManager = (TelephonyManager) getContext().getSystemService(
@@ -155,11 +155,13 @@
                             mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
                         return true;  // suppress key event
                     }
-                case KeyEvent.KEYCODE_HEADSETHOOK: 
-                case KeyEvent.KEYCODE_MEDIA_STOP: 
-                case KeyEvent.KEYCODE_MEDIA_NEXT: 
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
-                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                     Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                     intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
@@ -191,12 +193,15 @@
         } else if (event.getAction() == KeyEvent.ACTION_UP) {
             switch (keyCode) {
                 case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK: 
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 
-                case KeyEvent.KEYCODE_MEDIA_STOP: 
-                case KeyEvent.KEYCODE_MEDIA_NEXT: 
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
-                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                     Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                     intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index c034ec9..c870503 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -795,6 +795,7 @@
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_CAMERA:
                 return false;
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 1383354..4644a7c 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -209,7 +209,12 @@
                 mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
                         : R.string.lockscreen_sound_off_label);
             }
-            mCallback.pokeWakelock();
+            // Don't poke the wake lock when returning to a state where the handle is
+            // not grabbed since that can happen when the system (instead of the user)
+            // cancels the grab.
+            if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) {
+                mCallback.pokeWakelock();
+            }
         }
     }
 
@@ -231,10 +236,11 @@
 
         /** {@inheritDoc} */
         public void onGrabbedStateChange(View v, int grabbedState) {
+            // Don't poke the wake lock when returning to a state where the handle is
+            // not grabbed since that can happen when the system (instead of the user)
+            // cancels the grab.
             if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) {
                 mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT);
-            } else {
-                mCallback.pokeWakelock();
             }
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 3691d97..e944f9d 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -627,13 +627,6 @@
             // should be executed...  do it!
             mPanelChordingKey = 0;
             mPanelMayLongPress = false;
-            InputMethodManager imm = (InputMethodManager)
-                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-            if (imm != null) {
-                mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
-            }
-            
         }
 
         return false;
@@ -1259,7 +1252,7 @@
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                /* Suppress PLAYPAUSE toggle when phone is ringing or in-call
+                /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call
                  * to avoid music playback */
                 if (mTelephonyManager == null) {
                     mTelephonyManager = (TelephonyManager) getContext().getSystemService(
@@ -1275,6 +1268,7 @@
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                 intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
@@ -1455,6 +1449,7 @@
             }
 
             case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MUTE:
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -1462,6 +1457,7 @@
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                 intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7f49da9..e950ae5 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -54,6 +54,7 @@
 import com.android.internal.view.BaseInputHandler;
 import com.android.internal.widget.PointerLocationView;
 
+import android.telephony.TelephonyManager;
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
@@ -63,6 +64,7 @@
 import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputQueue;
 import android.view.InputHandler;
 import android.view.KeyEvent;
@@ -217,7 +219,7 @@
     WindowState mKeyguard = null;
     KeyguardViewMediator mKeyguardMediator;
     GlobalActions mGlobalActions;
-    boolean mShouldTurnOffOnKeyUp;
+    volatile boolean mPowerKeyHandled;
     RecentApplicationsDialog mRecentAppsDialog;
     Handler mHandler;
     
@@ -477,28 +479,47 @@
         }
     }
 
-    Runnable mPowerLongPress = new Runnable() {
+    private void interceptPowerKeyDown(boolean handled) {
+        mPowerKeyHandled = handled;
+        if (!handled) {
+            mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
+        }
+    }
+
+    private boolean interceptPowerKeyUp(boolean canceled) {
+        if (!mPowerKeyHandled) {
+            mHandler.removeCallbacks(mPowerLongPress);
+            return !canceled;
+        } else {
+            mPowerKeyHandled = true;
+            return false;
+        }
+    }
+
+    private final Runnable mPowerLongPress = new Runnable() {
         public void run() {
-            // The context isn't read
-            if (mLongPressOnPowerBehavior < 0) {
-                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
-                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
-            }
-            switch (mLongPressOnPowerBehavior) {
-            case LONG_PRESS_POWER_NOTHING:
-                break;
-            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
-                mShouldTurnOffOnKeyUp = false;
-                performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
-                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
-                showGlobalActionsDialog();
-                break;
-            case LONG_PRESS_POWER_SHUT_OFF:
-                mShouldTurnOffOnKeyUp = false;
-                performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
-                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
-                ShutdownThread.shutdown(mContext, true);
-                break;
+            if (!mPowerKeyHandled) {
+                // The context isn't read
+                if (mLongPressOnPowerBehavior < 0) {
+                    mLongPressOnPowerBehavior = mContext.getResources().getInteger(
+                            com.android.internal.R.integer.config_longPressOnPowerBehavior);
+                }
+                switch (mLongPressOnPowerBehavior) {
+                case LONG_PRESS_POWER_NOTHING:
+                    break;
+                case LONG_PRESS_POWER_GLOBAL_ACTIONS:
+                    mPowerKeyHandled = true;
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+                    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+                    showGlobalActionsDialog();
+                    break;
+                case LONG_PRESS_POWER_SHUT_OFF:
+                    mPowerKeyHandled = true;
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+                    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+                    ShutdownThread.shutdown(mContext, true);
+                    break;
+                }
             }
         }
     };
@@ -1111,12 +1132,22 @@
                 com.android.internal.R.anim.lock_screen_behind_enter);
     }
     
-    static ITelephony getPhoneInterface() {
-        return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+    static ITelephony getTelephonyService() {
+        ITelephony telephonyService = ITelephony.Stub.asInterface(
+                ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+        if (telephonyService == null) {
+            Log.w(TAG, "Unable to find ITelephony interface.");
+        }
+        return telephonyService;
     }
 
-    static IAudioService getAudioInterface() {
-        return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));
+    static IAudioService getAudioService() {
+        IAudioService audioService = IAudioService.Stub.asInterface(
+                ServiceManager.checkService(Context.AUDIO_SERVICE));
+        if (audioService == null) {
+            Log.w(TAG, "Unable to find IAudioService interface.");
+        }
+        return audioService;
     }
 
     boolean keyguardOn() {
@@ -1131,7 +1162,7 @@
     /** {@inheritDoc} */
     @Override
     public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
-            int keyCode, int metaState, int repeatCount, int policyFlags) {
+            int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
         final boolean keyguardOn = keyguardOn();
         final boolean down = (action == KeyEvent.ACTION_DOWN);
         final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0);
@@ -1164,11 +1195,9 @@
                         // and his ONLY options are to answer or reject the call.)
                         boolean incomingRinging = false;
                         try {
-                            ITelephony phoneServ = getPhoneInterface();
-                            if (phoneServ != null) {
-                                incomingRinging = phoneServ.isRinging();
-                            } else {
-                                Log.w(TAG, "Unable to find ITelephony interface");
+                            ITelephony telephonyService = getTelephonyService();
+                            if (telephonyService != null) {
+                                incomingRinging = telephonyService.isRinging();
                             }
                         } catch (RemoteException ex) {
                             Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
@@ -1824,23 +1853,6 @@
     }
 
     /**
-     * @return Whether a telephone call is in progress right now.
-     */
-    boolean isInCall() {
-        final ITelephony phone = getPhoneInterface();
-        if (phone == null) {
-            Log.w(TAG, "couldn't get ITelephony reference");
-            return false;
-        }
-        try {
-            return phone.isOffhook();
-        } catch (RemoteException e) {
-            Log.w(TAG, "ITelephony.isOffhhook threw RemoteException " + e);
-            return false;
-        }
-    }
-
-    /**
      * @return Whether music is being played right now.
      */
     boolean isMusicActive() {
@@ -1857,9 +1869,8 @@
      * @param keycode
      */
     void handleVolumeKey(int stream, int keycode) {
-        final IAudioService audio = getAudioInterface();
-        if (audio == null) {
-            Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference");
+        IAudioService audioService = getAudioService();
+        if (audioService == null) {
             return;
         }
         try {
@@ -1867,7 +1878,7 @@
             // during the call, but we do it as a precaution for the rare possibility
             // that the music stops right before we call this
             mBroadcastWakeLock.acquire();
-            audio.adjustStreamVolume(stream,
+            audioService.adjustStreamVolume(stream,
                 keycode == KeyEvent.KEYCODE_VOLUME_UP
                             ? AudioManager.ADJUST_RAISE
                             : AudioManager.ADJUST_LOWER,
@@ -1878,41 +1889,15 @@
             mBroadcastWakeLock.release();
         }
     }
-    
-    static boolean isMediaKey(int code) {
-        if (code == KeyEvent.KEYCODE_HEADSETHOOK || 
-                code == KeyEvent.KEYCODE_MEDIA_PLAY ||
-                code == KeyEvent.KEYCODE_MEDIA_PAUSE ||
-                code == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                code == KeyEvent.KEYCODE_MEDIA_STOP || 
-                code == KeyEvent.KEYCODE_MEDIA_NEXT ||
-                code == KeyEvent.KEYCODE_MEDIA_PREVIOUS || 
-                code == KeyEvent.KEYCODE_MEDIA_REWIND ||
-                code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
-            return true;
-        }
-        return false;    
-    }
- 
+
     /** {@inheritDoc} */
     @Override
-    public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-            int policyFlags, boolean isScreenOn) {
-        int result = ACTION_PASS_TO_USER;
+    public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+            int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+        final boolean down = action == KeyEvent.ACTION_DOWN;
+        final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0;
 
-        if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
-        }
-
-        final boolean isWakeKey = (policyFlags
-                & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
-
-        // If the key is injected, pretend that the screen is on and don't let the
-        // device go to sleep.  This feature is mainly used for testing purposes.
         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
-        if (isInjected) {
-            isScreenOn = true;
-        }
 
         // If screen is off then we treat the case where the keyguard is open but hidden
         // the same as if it were open and in front.
@@ -1927,202 +1912,210 @@
                   + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
         }
 
-        if (keyguardActive) {
-            if (isScreenOn) {
-                // when the screen is on, always give the event to the keyguard
-                result |= ACTION_PASS_TO_USER;
-            } else {
-                // otherwise, don't pass it to the user
-                result &= ~ACTION_PASS_TO_USER;
+        if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
+            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+        }
 
-                if (isWakeKey && down) {
+        // Basic policy based on screen state and keyguard.
+        // FIXME: This policy isn't quite correct.  We shouldn't care whether the screen
+        //        is on or off, really.  We should care about whether the device is in an
+        //        interactive state or is in suspend pretending to be "off".
+        //        The primary screen might be turned off due to proximity sensor or
+        //        because we are presenting media on an auxiliary screen or remotely controlling
+        //        the device some other way (which is why we have an exemption here for injected
+        //        events).
+        int result;
+        if (isScreenOn || isInjected) {
+            // When the screen is on or if the key is injected pass the key to the application.
+            result = ACTION_PASS_TO_USER;
+        } else {
+            // When the screen is off and the key is not injected, determine whether
+            // to wake the device but don't pass the key to the application.
+            result = 0;
 
-                    // tell the mediator about a wake key, it may decide to
-                    // turn on the screen depending on whether the key is
-                    // appropriate.
-                    if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode)
-                            && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                                || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
-                        // when keyguard is showing and screen off, we need
-                        // to handle the volume key for calls and  music here
-                        if (isInCall()) {
-                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-                        } else if (isMusicActive()) {
-                            handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
-                        }
-                    }
+            final boolean isWakeKey = (policyFlags
+                    & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+            if (down && isWakeKey) {
+                if (keyguardActive) {
+                    // If the keyguard is showing, let it decide what to do with the wake key.
+                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
+                } else {
+                    // Otherwise, wake the device ourselves.
+                    result |= ACTION_POKE_USER_ACTIVITY;
                 }
             }
-        } else if (!isScreenOn) {
-            // If we are in-call with screen off and keyguard is not showing,
-            // then handle the volume key ourselves.
-            // This is necessary because the phone app will disable the keyguard
-            // when the proximity sensor is in use.
-            if (isInCall() &&
-                     (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                                || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
-                result &= ~ACTION_PASS_TO_USER;
-                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-            }
-            if (isWakeKey) {
-                // a wake key has a sole purpose of waking the device; don't pass
-                // it to the user
-                result |= ACTION_POKE_USER_ACTIVITY;
-                result &= ~ACTION_PASS_TO_USER;
-            }
         }
 
-        if (keyCode == KeyEvent.KEYCODE_ENDCALL
-                || keyCode == KeyEvent.KEYCODE_POWER) {
-            if (down) {
-                boolean handled = false;
-                boolean hungUp = false;
-                // key repeats are generated by the window manager, and we don't see them
-                // here, so unless the driver is doing something it shouldn't be, we know
-                // this is the real press event.
-                ITelephony phoneServ = getPhoneInterface();
-                if (phoneServ != null) {
-                    try {
-                        if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
-                            handled = hungUp = phoneServ.endCall();
-                        } else if (keyCode == KeyEvent.KEYCODE_POWER) {
-                            if (phoneServ.isRinging()) {
-                                // Pressing Power while there's a ringing incoming
-                                // call should silence the ringer.
-                                phoneServ.silenceRinger();
-                                handled = true;
-                            } else if (phoneServ.isOffhook() &&
-                                       ((mIncallPowerBehavior
-                                         & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
-                                        != 0)) {
-                                // Otherwise, if "Power button ends call" is enabled,
-                                // the Power button will hang up any current active call.
-                                handled = hungUp = phoneServ.endCall();
+        // Handle special keys.
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_UP: {
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                // If an incoming call is ringing, either VOLUME key means
+                                // "silence ringer".  We handle these keys here, rather than
+                                // in the InCallScreen, to make sure we'll respond to them
+                                // even if the InCallScreen hasn't come to the foreground yet.
+                                // Look for the DOWN event here, to agree with the "fallback"
+                                // behavior in the InCallScreen.
+                                Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                      + " VOLUME key-down while ringing: Silence ringer!");
+
+                                // Silence the ringer.  (It's safe to call this
+                                // even if the ringer has already been silenced.)
+                                telephonyService.silenceRinger();
+
+                                // And *don't* pass this key thru to the current activity
+                                // (which is probably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                                break;
+                            }
+                            if (telephonyService.isOffhook()
+                                    && (result & ACTION_PASS_TO_USER) == 0) {
+                                // If we are in call but we decided not to pass the key to
+                                // the application, handle the volume change here.
+                                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+                                break;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+
+                    if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
+                        // If music is playing but we decided not to pass the key to the
+                        // application, handle the volume change here.
+                        handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
+                        break;
+                    }
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_ENDCALL: {
+                result &= ~ACTION_PASS_TO_USER;
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    boolean hungUp = false;
+                    if (telephonyService != null) {
+                        try {
+                            hungUp = telephonyService.endCall();
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+                    interceptPowerKeyDown(!isScreenOn || hungUp);
+                } else {
+                    if (interceptPowerKeyUp(canceled)) {
+                        if ((mEndcallBehavior
+                                & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
+                            if (goHome()) {
+                                break;
                             }
                         }
-                    } catch (RemoteException ex) {
-                        Log.w(TAG, "ITelephony threw RemoteException" + ex);
+                        if ((mEndcallBehavior
+                                & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
+                            result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
+                        }
                     }
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_POWER: {
+                result &= ~ACTION_PASS_TO_USER;
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    boolean hungUp = false;
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                // Pressing Power while there's a ringing incoming
+                                // call should silence the ringer.
+                                telephonyService.silenceRinger();
+                            } else if ((mIncallPowerBehavior
+                                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+                                    && telephonyService.isOffhook()) {
+                                // Otherwise, if "Power button ends call" is enabled,
+                                // the Power button will hang up any current active call.
+                                hungUp = telephonyService.endCall();
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+                    interceptPowerKeyDown(!isScreenOn || hungUp);
                 } else {
-                    Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
-                }
-
-                if (!isScreenOn
-                        || (handled && keyCode != KeyEvent.KEYCODE_POWER)
-                        || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) {
-                    mShouldTurnOffOnKeyUp = false;
-                } else {
-                    // Only try to turn off the screen if we didn't already hang up.
-                    mShouldTurnOffOnKeyUp = true;
-                    mHandler.postDelayed(mPowerLongPress,
-                            ViewConfiguration.getGlobalActionKeyTimeout());
-                    result &= ~ACTION_PASS_TO_USER;
-                }
-            } else {
-                mHandler.removeCallbacks(mPowerLongPress);
-                if (mShouldTurnOffOnKeyUp) {
-                    mShouldTurnOffOnKeyUp = false;
-                    boolean gohome, sleeps;
-                    if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
-                        gohome = (mEndcallBehavior
-                                  & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
-                        sleeps = (mEndcallBehavior
-                                  & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
-                    } else {
-                        gohome = false;
-                        sleeps = true;
+                    if (interceptPowerKeyUp(canceled)) {
+                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
                     }
-                    if (keyguardActive
-                            || (sleeps && !gohome)
-                            || (gohome && !goHome() && sleeps)) {
-                        // They must already be on the keyguard or home screen,
-                        // go to sleep instead unless the event was injected.
-                        if (!isInjected) {
-                            Log.d(TAG, "I'm tired mEndcallBehavior=0x"
-                                    + Integer.toHexString(mEndcallBehavior));
-                            result &= ~ACTION_POKE_USER_ACTIVITY;
-                            result |= ACTION_GO_TO_SLEEP;
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (!telephonyService.isIdle()) {
+                                // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+                                // to avoid music playback.
+                                break;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
                         }
                     }
-                    result &= ~ACTION_PASS_TO_USER;
                 }
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                if ((result & ACTION_PASS_TO_USER) == 0) {
+                    // Only do this if we would otherwise not pass it to the user. In that
+                    // case, the PhoneWindow class will do the same thing, except it will
+                    // only do it if the showing app doesn't process the key on its own.
+                    long when = whenNanos / 1000000;
+                    KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0,
+                            0, scanCode, flags, InputDevice.SOURCE_KEYBOARD);
+                    mBroadcastWakeLock.acquire();
+                    mHandler.post(new PassHeadsetKey(keyEvent));
+                }
+                break;
             }
-        } else if (isMediaKey(keyCode)) {
-            // This key needs to be handled even if the screen is off.
-            // If others need to be handled while it's off, this is a reasonable
-            // pattern to follow.
-            if ((result & ACTION_PASS_TO_USER) == 0) {
-                // Only do this if we would otherwise not pass it to the user. In that
-                // case, the PhoneWindow class will do the same thing, except it will
-                // only do it if the showing app doesn't process the key on its own.
-                long when = whenNanos / 1000000;
-                KeyEvent keyEvent = new KeyEvent(when, when,
-                        down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
-                        keyCode, 0);
-                mBroadcastWakeLock.acquire();
-                mHandler.post(new PassHeadsetKey(keyEvent));
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_CALL) {
-            // If an incoming call is ringing, answer it!
-            // (We handle this key here, rather than in the InCallScreen, to make
-            // sure we'll respond to the key even if the InCallScreen hasn't come to
-            // the foreground yet.)
 
-            // We answer the call on the DOWN event, to agree with
-            // the "fallback" behavior in the InCallScreen.
-            if (down) {
-                try {
-                    ITelephony phoneServ = getPhoneInterface();
-                    if (phoneServ != null) {
-                        if (phoneServ.isRinging()) {
-                            Log.i(TAG, "interceptKeyTq:"
-                                  + " CALL key-down while ringing: Answer the call!");
-                            phoneServ.answerRingingCall();
+            case KeyEvent.KEYCODE_CALL: {
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                      + " CALL key-down while ringing: Answer the call!");
+                                telephonyService.answerRingingCall();
 
-                            // And *don't* pass this key thru to the current activity
-                            // (which is presumably the InCallScreen.)
-                            result &= ~ACTION_PASS_TO_USER;
+                                // And *don't* pass this key thru to the current activity
+                                // (which is presumably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
                         }
-                    } else {
-                        Log.w(TAG, "CALL button: Unable to find ITelephony interface");
                     }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
                 }
-            }
-        } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP)
-                   || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            // If an incoming call is ringing, either VOLUME key means
-            // "silence ringer".  We handle these keys here, rather than
-            // in the InCallScreen, to make sure we'll respond to them
-            // even if the InCallScreen hasn't come to the foreground yet.
-
-            // Look for the DOWN event here, to agree with the "fallback"
-            // behavior in the InCallScreen.
-            if (down) {
-                try {
-                    ITelephony phoneServ = getPhoneInterface();
-                    if (phoneServ != null) {
-                        if (phoneServ.isRinging()) {
-                            Log.i(TAG, "interceptKeyTq:"
-                                  + " VOLUME key-down while ringing: Silence ringer!");
-                            // Silence the ringer.  (It's safe to call this
-                            // even if the ringer has already been silenced.)
-                            phoneServ.silenceRinger();
-
-                            // And *don't* pass this key thru to the current activity
-                            // (which is probably the InCallScreen.)
-                            result &= ~ACTION_PASS_TO_USER;
-                        }
-                    } else {
-                        Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
-                    }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
-                }
+                break;
             }
         }
-
         return result;
     }
 
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 65d9ef7..86d4c9f 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -356,7 +356,9 @@
         break;
     case AudioSystem::FOR_MEDIA:
         if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
-            config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
+            config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+            config != AudioSystem::FORCE_ANALOG_DOCK &&
+            config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) {
             LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
             return;
         }
@@ -372,7 +374,10 @@
         break;
     case AudioSystem::FOR_DOCK:
         if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
-            config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+            config != AudioSystem::FORCE_BT_DESK_DOCK &&
+            config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+            config != AudioSystem::FORCE_ANALOG_DOCK &&
+            config != AudioSystem::FORCE_DIGITAL_DOCK) {
             LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
         }
         forceVolumeReeval = true;
@@ -1366,6 +1371,7 @@
 
 void AudioPolicyManagerBase::closeA2dpOutputs()
 {
+
     LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
 
     if (mDuplicatedOutput != 0) {
@@ -1558,6 +1564,8 @@
             if (device) break;
             device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
             if (device) break;
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+            if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
             if (mPhoneState != AudioSystem::MODE_IN_CALL) {
@@ -1617,6 +1625,12 @@
         if (device2 == 0) {
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
         }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+        }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
+        }
 #ifdef WITH_A2DP
         if (mA2dpOutput != 0) {
             if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
@@ -1797,7 +1811,9 @@
         (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
         AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
         AudioSystem::DEVICE_OUT_WIRED_HEADSET |
-        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
+        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE |
+        AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET |
+        AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) &&
         (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) &&
         streamDesc.mCanBeMuted) {
         volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index bee8872..f993093 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -103,7 +103,6 @@
             FileReader file = new FileReader(DOCK_STATE_PATH);
             int len = file.read(buffer, 0, 1024);
             mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "This kernel does not have dock station support");
         } catch (Exception e) {
@@ -158,13 +157,17 @@
                         {
                             String whichSound = null;
                             if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-                                if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                                if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                                    (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                                    (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                                     whichSound = Settings.System.DESK_UNDOCK_SOUND;
                                 } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                                     whichSound = Settings.System.CAR_UNDOCK_SOUND;
                                 }
                             } else {
-                                if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                                if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                                    (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                                    (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                                     whichSound = Settings.System.DESK_DOCK_SOUND;
                                 } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                                     whichSound = Settings.System.CAR_DOCK_SOUND;
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
deleted file mode 100644
index 6f0a91d..0000000
--- a/services/java/com/android/server/HeadsetObserver.java
+++ /dev/null
@@ -1,183 +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 com.android.server;
-
-import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.UEventObserver;
-import android.util.Slog;
-import android.media.AudioManager;
-
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-
-/**
- * <p>HeadsetObserver monitors for a wired headset.
- */
-class HeadsetObserver extends UEventObserver {
-    private static final String TAG = HeadsetObserver.class.getSimpleName();
-    private static final boolean LOG = true;
-
-    private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
-    private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
-    private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
-
-    private static final int BIT_HEADSET = (1 << 0);
-    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
-    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
-    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
-
-    private int mHeadsetState;
-    private int mPrevHeadsetState;
-    private String mHeadsetName;
-
-    private final Context mContext;
-    private final WakeLock mWakeLock;  // held while there is a pending route change
-
-    public HeadsetObserver(Context context) {
-        mContext = context;
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
-        mWakeLock.setReferenceCounted(false);
-
-        startObserving(HEADSET_UEVENT_MATCH);
-
-        init();  // set initial status
-    }
-
-    @Override
-    public void onUEvent(UEventObserver.UEvent event) {
-        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
-
-        try {
-            update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
-        } catch (NumberFormatException e) {
-            Slog.e(TAG, "Could not parse switch state from event " + event);
-        }
-    }
-
-    private synchronized final void init() {
-        char[] buffer = new char[1024];
-
-        String newName = mHeadsetName;
-        int newState = mHeadsetState;
-        mPrevHeadsetState = mHeadsetState;
-        try {
-            FileReader file = new FileReader(HEADSET_STATE_PATH);
-            int len = file.read(buffer, 0, 1024);
-            newState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
-            file = new FileReader(HEADSET_NAME_PATH);
-            len = file.read(buffer, 0, 1024);
-            newName = new String(buffer, 0, len).trim();
-
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "This kernel does not have wired headset support");
-        } catch (Exception e) {
-            Slog.e(TAG, "" , e);
-        }
-
-        update(newName, newState);
-    }
-
-    private synchronized final void update(String newName, int newState) {
-        // Retain only relevant bits
-        int headsetState = newState & SUPPORTED_HEADSETS;
-        int newOrOld = headsetState | mHeadsetState;
-        int delay = 0;
-        // reject all suspect transitions: only accept state changes from:
-        // - a: 0 heaset to 1 headset
-        // - b: 1 headset to 0 headset
-        if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
-            return;
-        }
-
-        mHeadsetName = newName;
-        mPrevHeadsetState = mHeadsetState;
-        mHeadsetState = headsetState;
-
-        if (headsetState == 0) {
-            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-            mContext.sendBroadcast(intent);
-            // It can take hundreds of ms flush the audio pipeline after
-            // apps pause audio playback, but audio route changes are
-            // immediate, so delay the route change by 1000ms.
-            // This could be improved once the audio sub-system provides an
-            // interface to clear the audio pipeline.
-            delay = 1000;
-        } else {
-            // Insert the same delay for headset connection so that the connection event is not
-            // broadcast before the disconnection event in case of fast removal/insertion
-            if (mHandler.hasMessages(0)) {
-                delay = 1000;
-            }
-        }
-        mWakeLock.acquire();
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
-                                                           mHeadsetState,
-                                                           mPrevHeadsetState,
-                                                           mHeadsetName),
-                                    delay);
-    }
-
-    private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
-        int allHeadsets = SUPPORTED_HEADSETS;
-        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
-            if ((curHeadset & allHeadsets) != 0) {
-                sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
-                allHeadsets &= ~curHeadset;
-            }
-        }
-    }
-
-    private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
-        if ((headsetState & headset) != (prevHeadsetState & headset)) {
-            //  Pack up the values and broadcast them to everyone
-            Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            int state = 0;
-            int microphone = 0;
-
-            if ((headset & HEADSETS_WITH_MIC) != 0) {
-                microphone = 1;
-            }
-            if ((headsetState & headset) != 0) {
-                state = 1;
-            }
-            intent.putExtra("state", state);
-            intent.putExtra("name", headsetName);
-            intent.putExtra("microphone", microphone);
-
-            if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
-            // TODO: Should we require a permission?
-            ActivityManagerNative.broadcastStickyIntent(intent, null);
-        }
-    }
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
-            mWakeLock.release();
-        }
-    };
-}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index cb4071a..e7eb129 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -404,17 +404,18 @@
         }
         
         @SuppressWarnings("unused")
-        public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-                int policyFlags, boolean isScreenOn) {
+        public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+                int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
-                    whenNanos, keyCode, down, policyFlags, isScreenOn);
+                    whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
         }
         
         @SuppressWarnings("unused")
         public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
-                int flags, int keyCode, int metaState, int repeatCount, int policyFlags) {
+                int flags, int keyCode, int scanCode, int metaState, int repeatCount,
+                int policyFlags) {
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
-                    action, flags, keyCode, metaState, repeatCount, policyFlags);
+                    action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
         }
         
         @SuppressWarnings("unused")
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 07da0fa..20e8bbe 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1881,11 +1881,10 @@
             String id = pair.first;
             ArrayList<String> subtypes = pair.second;
             builder.append(id);
-            if (subtypes.size() > 0) {
-                builder.append(subtypes.get(0));
-                for (int i = 1; i < subtypes.size(); ++i) {
-                    builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypes.get(i));
-                }
+            // Inputmethod and subtypes are saved in the settings as follows:
+            // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+            for (String subtypeId: subtypes) {
+                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
             }
         }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 237ab80..54f7441 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -121,7 +121,7 @@
         WindowManagerService wm = null;
         BluetoothService bluetooth = null;
         BluetoothA2dpService bluetoothA2dp = null;
-        HeadsetObserver headset = null;
+        WiredAccessoryObserver wiredAccessory = null;
         DockObserver dock = null;
         UsbObserver usb = null;
         UiModeManagerService uiMode = null;
@@ -388,14 +388,6 @@
             }
 
             try {
-                Slog.i(TAG, "Headset Observer");
-                // Listen for wired headset changes
-                headset = new HeadsetObserver(context);
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting HeadsetObserver", e);
-            }
-
-            try {
                 Slog.i(TAG, "Dock Observer");
                 // Listen for dock station changes
                 dock = new DockObserver(context, power);
@@ -404,6 +396,14 @@
             }
 
             try {
+                Slog.i(TAG, "Wired Accessory Observer");
+                // Listen for wired headset changes
+                wiredAccessory = new WiredAccessoryObserver(context);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting WiredAccessoryObserver", e);
+            }
+
+            try {
                 Slog.i(TAG, "USB Observer");
                 // Listen for USB changes
                 usb = new UsbObserver(context);
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 747af26..a7a0c68 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -103,6 +103,8 @@
 
     private int mDataConnectionNetworkType;
 
+    private int mOtaspMode;
+
     static final int PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
                 PhoneStateListener.LISTEN_CALL_STATE |
@@ -225,6 +227,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+                        try {
+                            r.callback.onOtaspChanged(mOtaspMode);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -467,6 +476,25 @@
         }
     }
 
+    public void notifyOtaspChanged(int otaspMode) {
+        if (!checkNotifyPermission("notifyOtaspChanged()" )) {
+            return;
+        }
+        synchronized (mRecords) {
+            mOtaspMode = otaspMode;
+            for (Record r : mRecords) {
+                if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+                    try {
+                        r.callback.onOtaspChanged(otaspMode);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index c29e4a9..55ebded 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5706,20 +5706,20 @@
         
         /* Provides an opportunity for the window manager policy to intercept early key
          * processing as soon as the key has been read from the device. */
-        public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-                int policyFlags, boolean isScreenOn) {
-            return mPolicy.interceptKeyBeforeQueueing(whenNanos,
-                    keyCode, down, policyFlags, isScreenOn);
+        public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+                int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+            return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags,
+                    keyCode, scanCode, policyFlags, isScreenOn);
         }
         
         /* Provides an opportunity for the window manager policy to process a key before
          * ordinary dispatch. */
         public boolean interceptKeyBeforeDispatching(InputChannel focus,
-                int action, int flags, int keyCode, int metaState, int repeatCount,
+                int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
                 int policyFlags) {
             WindowState windowState = getWindowStateForInputChannel(focus);
             return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
-                    keyCode, metaState, repeatCount, policyFlags);
+                    keyCode, scanCode, metaState, repeatCount, policyFlags);
         }
         
         /* Called when the current input focus changes.
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
new file mode 100644
index 0000000..ab92fdf
--- /dev/null
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -0,0 +1,258 @@
+/*
+ * 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.server;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.UEventObserver;
+import android.util.Slog;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
+ */
+class WiredAccessoryObserver extends UEventObserver {
+    private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
+    private static final boolean LOG = true;
+    private static final int MAX_AUDIO_PORTS = 2; /* h2w & USB Audio */
+    private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w",
+                                                    "/sys/class/switch/h2w/state",
+                                                    "/sys/class/switch/h2w/name"},
+                                                   {"DEVPATH=/devices/virtual/switch/usb_audio",
+                                                    "/sys/class/switch/usb_audio/state",
+                                                    "/sys/class/switch/usb_audio/name"} };
+
+    private static final int BIT_HEADSET = (1 << 0);
+    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+    private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
+    private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
+    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                   BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL);
+    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
+
+    private int mHeadsetState;
+    private int mPrevHeadsetState;
+    private String mHeadsetName;
+    private int switchState;
+
+    private final Context mContext;
+    private final WakeLock mWakeLock;  // held while there is a pending route change
+
+    public WiredAccessoryObserver(Context context) {
+        mContext = context;
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
+        mWakeLock.setReferenceCounted(false);
+
+        // At any given time both headsets could be inserted
+        // one on the board and one on the dock
+        // observe two UEVENTs
+        for (int i = 0; i <= MAX_AUDIO_PORTS; i++) {
+            startObserving(uEventInfo[i][0]);
+        }
+        init();  // set initial status
+    }
+
+    @Override
+    public void onUEvent(UEventObserver.UEvent event) {
+        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
+
+        try {
+            if ((event.get("SWITCH_NAME")).equals("usb_audio")) {
+                if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) {
+                    switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                     BIT_USB_HEADSET_DGTL)) |
+                                   (Integer.parseInt(event.get("SWITCH_STATE")) << 2));
+                } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) {
+                    switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                     BIT_USB_HEADSET_ANLG)) |
+                                   (Integer.parseInt(event.get("SWITCH_STATE")) << 3));
+                }
+                else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC));
+            }
+            else {
+                switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) |
+                              (Integer.parseInt(event.get("SWITCH_STATE"))));
+            }
+            update(event.get("SWITCH_NAME"), switchState);
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Could not parse switch state from event " + event);
+        }
+    }
+
+    private synchronized final void init() {
+        char[] buffer = new char[1024];
+
+        String newName = mHeadsetName;
+        int newState = mHeadsetState;
+        mPrevHeadsetState = mHeadsetState;
+
+        for (int i = 0; i <= MAX_AUDIO_PORTS; i++) {
+            try {
+                FileReader file = new FileReader(uEventInfo[i][1]);
+                int len = file.read(buffer, 0, 1024);
+                newState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+                file = new FileReader(uEventInfo[i][2]);
+                len = file.read(buffer, 0, 1024);
+                newName = new String(buffer, 0, len).trim();
+
+            } catch (FileNotFoundException e) {
+                Slog.w(TAG, "This kernel does not have wired headset support");
+            } catch (Exception e) {
+                Slog.e(TAG, "" , e);
+            }
+
+            update(newName, newState);
+        }
+    }
+
+    private synchronized final void update(String newName, int newState) {
+        // Retain only relevant bits
+        int headsetState = newState & SUPPORTED_HEADSETS;
+        int newOrOld = headsetState | mHeadsetState;
+        int delay = 0;
+        int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
+        int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
+        int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
+        boolean h2wStateChange = true;
+        boolean usbStateChange = true;
+        // reject all suspect transitions: only accept state changes from:
+        // - a: 0 heaset to 1 headset
+        // - b: 1 headset to 0 headset
+        Log.v(TAG, "newState = "+newState+", headsetState = "+headsetState+", mHeadsetState = "+mHeadsetState);
+        if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) {
+            Log.e(TAG, "unsetting h2w flag");
+            h2wStateChange = false;
+        }
+        // - c: 0 usb headset to 1 usb headset
+        // - d: 1 usb headset to 0 usb headset
+        if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) {
+            Log.e(TAG, "unsetting usb flag");
+            usbStateChange = false;
+        }
+        if (!h2wStateChange && !usbStateChange) {
+            Log.e(TAG, "invalid transition, returning ...");
+            return;
+        }
+
+        mHeadsetName = newName;
+        mPrevHeadsetState = mHeadsetState;
+        mHeadsetState = headsetState;
+
+        if (headsetState == 0) {
+            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+            mContext.sendBroadcast(intent);
+            // It can take hundreds of ms flush the audio pipeline after
+            // apps pause audio playback, but audio route changes are
+            // immediate, so delay the route change by 1000ms.
+            // This could be improved once the audio sub-system provides an
+            // interface to clear the audio pipeline.
+            delay = 1000;
+        } else {
+            // Insert the same delay for headset connection so that the connection event is not
+            // broadcast before the disconnection event in case of fast removal/insertion
+            if (mHandler.hasMessages(0)) {
+                delay = 1000;
+            }
+        }
+        mWakeLock.acquire();
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
+                                                           mHeadsetState,
+                                                           mPrevHeadsetState,
+                                                           mHeadsetName),
+                                    delay);
+    }
+
+    private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
+        int allHeadsets = SUPPORTED_HEADSETS;
+        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+            if ((curHeadset & allHeadsets) != 0) {
+                sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
+                allHeadsets &= ~curHeadset;
+            }
+        }
+    }
+
+    private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
+        if ((headsetState & headset) != (prevHeadsetState & headset)) {
+
+            int state = 0;
+            if ((headsetState & headset) != 0) {
+                state = 1;
+            }
+            if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL)) {
+                Intent intent;
+
+                //  Pack up the values and broadcast them to everyone
+                if (headset == BIT_USB_HEADSET_ANLG) {
+                    intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra("state", state);
+                    intent.putExtra("name", headsetName);
+                    ActivityManagerNative.broadcastStickyIntent(intent, null);
+                } else if (headset == BIT_USB_HEADSET_DGTL) {
+                    intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra("state", state);
+                    intent.putExtra("name", headsetName);
+                    ActivityManagerNative.broadcastStickyIntent(intent, null);
+                }
+
+                if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName);
+                // TODO: Should we require a permission?
+            }
+            if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) {
+
+                //  Pack up the values and broadcast them to everyone
+                Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                //int state = 0;
+                int microphone = 0;
+
+                if ((headset & HEADSETS_WITH_MIC) != 0) {
+                    microphone = 1;
+                }
+
+                intent.putExtra("state", state);
+                intent.putExtra("name", headsetName);
+                intent.putExtra("microphone", microphone);
+
+                if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
+                // TODO: Should we require a permission?
+                ActivityManagerNative.broadcastStickyIntent(intent, null);
+            }
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
+            mWakeLock.release();
+        }
+    };
+}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 599163b..d4c4ba4 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -857,7 +857,7 @@
         JNIEnv* env = jniEnv();
         jint wmActions = env->CallIntMethod(mCallbacksObj,
                 gCallbacksClassInfo.interceptKeyBeforeQueueing,
-                when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn);
+                when, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
         if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
             wmActions = 0;
         }
@@ -926,7 +926,7 @@
         jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
                 gCallbacksClassInfo.interceptKeyBeforeDispatching,
                 inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
-                keyEvent->getKeyCode(), keyEvent->getMetaState(),
+                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
                 keyEvent->getRepeatCount(), policyFlags);
         bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
 
@@ -1358,10 +1358,10 @@
             "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
 
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
-            "interceptKeyBeforeQueueing", "(JIZIZ)I");
+            "interceptKeyBeforeQueueing", "(JIIIIIZ)I");
 
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
-            "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z");
+            "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z");
 
     GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
             "checkInjectEventsPermission", "(II)Z");
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 38f44d8..eda9b71 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -148,6 +148,14 @@
      */
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
+    /**
+     * Listen for changes to OTASP mode.
+     *
+     * @see #onOtaspChanged
+     * @hide
+     */
+    public static final int LISTEN_OTASP_CHANGED                            = 0x00000200;
+
     public PhoneStateListener() {
     }
 
@@ -252,6 +260,21 @@
         // default implementation empty
     }
 
+
+    /**
+     * The Over The Air Service Provisioning (OTASP) has changed. Requires
+     * the READ_PHONE_STATE permission.
+     * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
+     *   means the value is currently unknown and the system should wait until
+     *   <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
+     *   making the decisision to perform OTASP or not.
+     *
+     * @hide
+     */
+    public void onOtaspChanged(int otaspMode) {
+        // default implementation empty
+    }
+
     /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
@@ -292,9 +315,14 @@
         public void onDataActivity(int direction) {
             Message.obtain(mHandler, LISTEN_DATA_ACTIVITY, direction, 0, null).sendToTarget();
         }
+
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
             Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength).sendToTarget();
         }
+
+        public void onOtaspChanged(int otaspMode) {
+            Message.obtain(mHandler, LISTEN_OTASP_CHANGED, otaspMode, 0).sendToTarget();
+        }
     };
 
     Handler mHandler = new Handler() {
@@ -329,6 +357,9 @@
                 case LISTEN_SIGNAL_STRENGTHS:
                     PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj);
                     break;
+                case LISTEN_OTASP_CHANGED:
+                    PhoneStateListener.this.onOtaspChanged(msg.arg1);
+                    break;
             }
         }
     };
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 6a163dd..14808b6 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -148,6 +148,14 @@
         }
     }
 
+    public void notifyOtaspChanged(Phone sender, int otaspMode) {
+        try {
+            mRegistry.notifyOtaspChanged(otaspMode);
+        } catch (RemoteException ex) {
+            // system process is dead
+        }
+    }
+
     private void log(String s) {
         Log.d(LOG_TAG, "[PhoneNotifier] " + s);
     }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 856d663..082c097 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -32,5 +32,6 @@
     void onDataConnectionStateChanged(int state, int networkType);
     void onDataActivity(int direction);
     void onSignalStrengthsChanged(in SignalStrength signalStrength);
+    void onOtaspChanged(in int otaspMode);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 7a1587b..3c4bb12 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -66,7 +66,7 @@
     boolean showCallScreenWithDialpad(boolean showDialpad);
 
     /**
-     * End call or go to the Home screen
+     * End call if there is a call in progress, otherwise does nothing.
      *
      * @return whether it hung up
      */
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 6407a4e..3c83e50 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -38,4 +38,5 @@
             in LinkCapabilities linkCapabilities, int networkType);
     void notifyDataConnectionFailed(String reason, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
+    void notifyOtaspChanged(in int otaspMode);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 74e8c1b..554a7ba 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -750,6 +750,10 @@
         mNotifier.notifyDataConnection(this, reason, apnType);
     }
 
+    public void notifyOtaspChanged(int otaspMode) {
+        mNotifier.notifyOtaspChanged(this, otaspMode);
+    }
+
     public abstract String getPhoneName();
 
     public abstract int getPhoneType();
diff --git a/telephony/java/com/android/internal/telephony/PhoneNotifier.java b/telephony/java/com/android/internal/telephony/PhoneNotifier.java
index 691271f..b1cf953 100644
--- a/telephony/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/PhoneNotifier.java
@@ -42,4 +42,5 @@
 
     public void notifyDataActivity(Phone sender);
 
+    public void notifyOtaspChanged(Phone sender, int otaspMode);
 }
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index e8bbe5e..3f9ffc3 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -53,6 +53,12 @@
 
     public SignalStrength mSignalStrength;
 
+    /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
+    static public final int OTASP_UNINITIALIZED = 0;
+    static public final int OTASP_UNKNOWN = 1;
+    static public final int OTASP_NEEDED = 2;
+    static public final int OTASP_NOT_NEEDED = 3;
+
     /**
      * A unique identifier to track requests associated with a poll
      * and ignore stale responses.  The value is a count-down of
@@ -268,9 +274,11 @@
 
     public abstract void handleMessage(Message msg);
 
+    protected abstract Phone getPhone();
     protected abstract void handlePollStateResult(int what, AsyncResult ar);
     protected abstract void updateSpnDisplay();
     protected abstract void setPowerStateToDesired();
+    protected abstract void log(String s);
 
     /**
      * Clean up existing voice and data connection then turn off radio power.
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 6e53ec5..b9d5673 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -61,6 +61,7 @@
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
@@ -83,10 +84,6 @@
     static final String LOG_TAG = "CDMA";
     private static final boolean DBG = true;
 
-    // Min values used to by needsActivation
-    private static final String UNACTIVATED_MIN2_VALUE = "000000";
-    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
-
     // Default Emergency Callback Mode exit timer
     private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
 
@@ -1170,19 +1167,7 @@
      */
     @Override
     public boolean needsOtaServiceProvisioning() {
-        String cdmaMin = getCdmaMin();
-        boolean needsProvisioning;
-        if (cdmaMin == null || (cdmaMin.length() < 6)) {
-            if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: illegal cdmaMin='"
-                                    + cdmaMin + "' assume provisioning needed.");
-            needsProvisioning = true;
-        } else {
-            needsProvisioning = (cdmaMin.equals(UNACTIVATED_MIN_VALUE)
-                    || cdmaMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
-                    || SystemProperties.getBoolean("test_cdma_setup", false);
-        }
-        if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: ret=" + needsProvisioning);
-        return needsProvisioning;
+        return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED;
     }
 
     private static final String IS683A_FEATURE_CODE = "*228";
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 3669e60..325c2e1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -354,6 +354,24 @@
                     || (foregroundCall.getState() == CdmaCall.State.ACTIVE)
                     || !backgroundCall.getState().isAlive());
 
+        if (!ret) {
+            log(String.format("canDial is false\n" +
+                              "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
+                              "&& pendingMO == null::=%s\n" +
+                              "&& !ringingCall.isRinging()::=%s\n" +
+                              "&& !disableCall.equals(\"true\")::=%s\n" +
+                              "&& (!foregroundCall.getState().isAlive()::=%s\n" +
+                              "   || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" +
+                              "   ||!backgroundCall.getState().isAlive())::=%s)",
+                    serviceState,
+                    serviceState != ServiceState.STATE_POWER_OFF,
+                    pendingMO == null,
+                    !ringingCall.isRinging(),
+                    !disableCall.equals("true"),
+                    !foregroundCall.getState().isAlive(),
+                    foregroundCall.getState() == CdmaCall.State.ACTIVE,
+                    !backgroundCall.getState().isAlive()));
+        }
         return ret;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 7d2013b..11e04d4 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -22,6 +22,7 @@
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
@@ -65,6 +66,13 @@
     CdmaCellLocation cellLoc;
     CdmaCellLocation newCellLoc;
 
+    // Min values used to by getOtasp()
+    private static final String UNACTIVATED_MIN2_VALUE = "000000";
+    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
+
+    // Current Otasp value
+    int mCurrentOtaspMode = OTASP_UNINITIALIZED;
+
      /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
     private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
     private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
@@ -446,6 +454,13 @@
                     if (!mIsMinInfoReady) {
                         mIsMinInfoReady = true;
                     }
+                    int otaspMode = getOtasp();
+                    if (mCurrentOtaspMode != otaspMode) {
+                        Log.d(LOG_TAG, "call phone.notifyOtaspChanged old otaspMode=" +
+                                mCurrentOtaspMode + " new otaspMode=" + otaspMode);
+                        mCurrentOtaspMode = otaspMode;
+                        phone.notifyOtaspChanged(mCurrentOtaspMode);
+                    }
                     phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
                             null);
                 } else {
@@ -642,6 +657,11 @@
         curPlmn = plmn;
     }
 
+    @Override
+    protected Phone getPhone() {
+        return phone;
+    }
+
     /**
      * Handle the result of one of the pollState()-related requests
      */
@@ -1641,10 +1661,6 @@
         return false;
     }
 
-    protected void log(String s) {
-        Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
-    }
-
     public String getMdnNumber() {
         return mMdn;
     }
@@ -1700,6 +1716,32 @@
         }
     }
 
+    /**
+     * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
+     */
+    int getOtasp() {
+        int provisioningState;
+        if (mMin == null || (mMin.length() < 6)) {
+            if (DBG) Log.d(LOG_TAG, "getOtasp: bad mMin='" + mMin + "'");
+            provisioningState = OTASP_UNKNOWN;
+        } else {
+            if ((mMin.equals(UNACTIVATED_MIN_VALUE)
+                    || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
+                    || SystemProperties.getBoolean("test_cdma_setup", false)) {
+                provisioningState = OTASP_NEEDED;
+            } else {
+                provisioningState = OTASP_NOT_NEEDED;
+            }
+        }
+        if (DBG) Log.d(LOG_TAG, "getOtasp: state=" + provisioningState);
+        return provisioningState;
+    }
+
+    @Override
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
+    }
+
     private void hangupAndPowerOff() {
         // hang up all active voice calls
         phone.mCT.ringingCall.hangupIfAlive();
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 6332415..bc41b01 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -22,6 +22,7 @@
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
@@ -226,6 +227,9 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
         phone.getContext().registerReceiver(mIntentReceiver, filter);
+
+        // Gsm doesn't support OTASP so its not needed
+        phone.notifyOtaspChanged(OTASP_NOT_NEEDED);
     }
 
     public void dispose() {
@@ -246,6 +250,11 @@
         if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized");
     }
 
+    @Override
+    public Phone getPhone() {
+        return phone;
+    }
+
     /**
      * Registration point for transition into GPRS attached.
      * @param h handler to notify
@@ -1676,7 +1685,8 @@
         }
     }
 
-    private void log(String s) {
+    @Override
+    protected void log(String s) {
         Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s);
     }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
index 8cb05cc..7bbe696 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
@@ -56,4 +56,7 @@
 
     public void notifyDataActivity(Phone sender) {
     }
+
+    public void notifyOtaspChanged(Phone sender, int otaspMode) {
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 24fba72..e97b1e6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -60,6 +60,20 @@
     // ---- Public Helper methods ----
 
     /**
+     * Returns the native delegate associated to a given {@link Bitmap_Delegate} object.
+     */
+    public static Bitmap_Delegate getDelegate(Bitmap bitmap) {
+        return sManager.getDelegate(bitmap.mNativeBitmap);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
+     */
+    public static Bitmap_Delegate getDelegate(int native_bitmap) {
+        return sManager.getDelegate(native_bitmap);
+    }
+
+    /**
      * Creates and returns a {@link Bitmap} initialized with the given file content.
      */
     public static Bitmap createBitmap(File input, Density density) throws IOException {
@@ -118,6 +132,13 @@
         return BufferedImage.TYPE_INT_ARGB;
     }
 
+    /**
+     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
+     */
+    public BufferedImage getImage() {
+        return mImage;
+    }
+
     // ---- native methods ----
 
     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
@@ -127,8 +148,7 @@
         // create the image
         BufferedImage image = new BufferedImage(width, height, imageType);
 
-        // fill it
-        //image.setRGB(x, y, rgb)
+        // FIXME fill the bitmap!
 
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
deleted file mode 100644
index 24da812..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ /dev/null
@@ -1,1247 +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 com.android.layoutlib.api.ILayoutLog;
-
-import android.graphics.Paint.Align;
-import android.graphics.Paint.FontInfo;
-import android.graphics.Paint.Style;
-import android.graphics.Region.Op;
-
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.util.List;
-import java.util.Stack;
-
-/**
- * Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
- */
-public class Canvas extends _Original_Canvas {
-
-    private BufferedImage mBufferedImage;
-    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
-    private final ILayoutLog mLogger;
-
-    public Canvas() {
-        mLogger = null;
-        // the mBufferedImage will be taken from a bitmap in #setBitmap()
-    }
-
-    public Canvas(Bitmap bitmap) {
-        mLogger = null;
-        mBufferedImage = Bitmap_Delegate.getImage(bitmap);
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int nativeCanvas) {
-        mLogger = null;
-        throw new UnsupportedOperationException("Can't create Canvas(int)");
-    }
-
-    // custom constructors for our use.
-    public Canvas(int width, int height, ILayoutLog logger) {
-        mLogger = logger;
-        mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int width, int height) {
-        this(width, height, null /* logger*/);
-    }
-
-    // custom mehtods
-    public BufferedImage getImage() {
-        return mBufferedImage;
-    }
-
-    public Graphics2D getGraphics2d() {
-        return mGraphicsStack.peek();
-    }
-
-    public void dispose() {
-        while (mGraphicsStack.size() > 0) {
-            mGraphicsStack.pop().dispose();
-        }
-    }
-
-    /**
-     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
-     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
-     */
-    private Graphics2D getCustomGraphics(Paint paint) {
-        // make new one
-        Graphics2D g = getGraphics2d();
-        g = (Graphics2D)g.create();
-
-        // configure it
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-
-        Style style = paint.getStyle();
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            PathEffect e = paint.getPathEffect();
-            if (e instanceof DashPathEffect) {
-                DashPathEffect dpe = (DashPathEffect)e;
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter(),
-                        dpe.getIntervals(),
-                        dpe.getPhase()));
-            } else {
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter()));
-            }
-        }
-
-        Xfermode xfermode = paint.getXfermode();
-        if (xfermode instanceof PorterDuffXfermode) {
-            PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
-
-            setModeInGraphics(mode, g, falpha);
-        } else {
-            if (mLogger != null && xfermode != null) {
-                mLogger.warning(String.format(
-                        "Xfermode '%1$s' is not supported in the Layout Editor.",
-                        xfermode.getClass().getCanonicalName()));
-            }
-            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-        }
-
-        Shader shader = paint.getShader();
-        if (shader != null) {
-            java.awt.Paint shaderPaint = shader.getJavaPaint();
-            if (shaderPaint != null) {
-                g.setPaint(shaderPaint);
-            } else {
-                if (mLogger != null) {
-                    mLogger.warning(String.format(
-                            "Shader '%1$s' is not supported in the Layout Editor.",
-                            shader.getClass().getCanonicalName()));
-                }
-            }
-        }
-
-        return g;
-    }
-
-    private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) {
-        switch (mode) {
-            case CLEAR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
-                break;
-            case DARKEN:
-                break;
-            case DST:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
-                break;
-            case DST_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
-                break;
-            case DST_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
-                break;
-            case DST_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
-                break;
-            case DST_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
-                break;
-            case LIGHTEN:
-                break;
-            case MULTIPLY:
-                break;
-            case SCREEN:
-                break;
-            case SRC:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
-                break;
-            case SRC_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
-                break;
-            case SRC_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
-                break;
-            case SRC_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
-                break;
-            case SRC_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-                break;
-            case XOR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
-                break;
-        }
-    }
-
-
-    // --------------------
-    // OVERRIDEN ENUMS
-    // This is needed since we rename Canvas into _Original_Canvas
-    // --------------------
-
-    public enum EdgeType {
-        BW(0),  //!< treat edges by just rounding to nearest pixel boundary
-        AA(1);  //!< treat edges by rounding-out, since they may be antialiased
-
-        EdgeType(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-
-    // --------------------
-    // OVERRIDEN METHODS
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap)
-     */
-    @Override
-    public void setBitmap(Bitmap bitmap) {
-        mBufferedImage = Bitmap_Delegate.getImage(bitmap);
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#translate(float, float)
-     */
-    @Override
-    public void translate(float dx, float dy) {
-        getGraphics2d().translate(dx, dy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save()
-     */
-    @Override
-    public int save() {
-        // get the current save count
-        int count = mGraphicsStack.size();
-
-        // create a new graphics and add it to the stack
-        Graphics2D g = (Graphics2D)getGraphics2d().create();
-        mGraphicsStack.push(g);
-
-        // return the old save count
-        return count;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save(int)
-     */
-    @Override
-    public int save(int saveFlags) {
-        // For now we ignore saveFlags
-        return save();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restore()
-     */
-    @Override
-    public void restore() {
-        mGraphicsStack.pop();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restoreToCount(int)
-     */
-    @Override
-    public void restoreToCount(int saveCount) {
-        while (mGraphicsStack.size() > saveCount) {
-            mGraphicsStack.pop();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getSaveCount()
-     */
-    @Override
-    public int getSaveCount() {
-        return mGraphicsStack.size();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom, Op op) {
-        return clipRect(left, top, right, bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom) {
-        getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(int, int, int, int)
-     */
-    @Override
-    public boolean clipRect(int left, int top, int right, int bottom) {
-        getGraphics2d().clipRect(left, top, right-left, bottom-top);
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(Rect rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect)
-     */
-    @Override
-    public boolean clipRect(Rect rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(RectF rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF)
-     */
-    @Override
-    public boolean clipRect(RectF rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    public boolean quickReject(RectF rect, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(RectF rect, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(Path path, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(Path path, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Retrieve the clip bounds, returning true if they are non-empty.
-     *
-     * @param bounds Return the clip bounds here. If it is null, ignore it but
-     *               still return true if the current clip is non-empty.
-     * @return true if the current clip is non-empty.
-     */
-    @Override
-    public boolean getClipBounds(Rect bounds) {
-        Rectangle rect = getGraphics2d().getClipBounds();
-        if (rect != null) {
-            bounds.left = rect.x;
-            bounds.top = rect.y;
-            bounds.right = rect.x + rect.width;
-            bounds.bottom = rect.y + rect.height;
-            return true;
-        }
-        return false;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode)
-     */
-    @Override
-    public void drawColor(int color, PorterDuff.Mode mode) {
-        Graphics2D g = getGraphics2d();
-
-        // save old color
-        Color c = g.getColor();
-
-        Composite composite = g.getComposite();
-
-        // get the alpha from the color
-        int alpha = color >>> 24;
-        float falpha = alpha / 255.f;
-
-        setModeInGraphics(mode, g, falpha);
-
-        g.setColor(new Color(color));
-
-        g.fillRect(0, 0, getWidth(), getHeight());
-
-        g.setComposite(composite);
-
-        // restore color
-        g.setColor(c);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int)
-     */
-    @Override
-    public void drawColor(int color) {
-        drawColor(color, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawARGB(int, int, int, int)
-     */
-    @Override
-    public void drawARGB(int a, int r, int g, int b) {
-        drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRGB(int, int, int)
-     */
-    @Override
-    public void drawRGB(int r, int g, int b) {
-        drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getWidth()
-     */
-    @Override
-    public int getWidth() {
-        return mBufferedImage.getWidth();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getHeight()
-     */
-    @Override
-    public int getHeight() {
-        return mBufferedImage.getHeight();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPaint(android.graphics.Paint)
-     */
-    @Override
-    public void drawPaint(Paint paint) {
-        drawColor(paint.getColor());
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
-        drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                (int)left, (int)top,
-                (int)left+bitmap.getWidth(), (int)top+bitmap.getHeight(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        boolean needsRestore = false;
-        if (matrix.isIdentity() == false) {
-            // create a new graphics and apply the matrix to it
-            save(); // this creates a new Graphics2D, and stores it for children call to use
-            needsRestore = true;
-            Graphics2D g = getGraphics2d(); // get the newly create Graphics2D
-
-            // get the Graphics2D current matrix
-            AffineTransform currentTx = g.getTransform();
-            // get the AffineTransform from the matrix
-            AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
-
-            // combine them so that the matrix is applied after.
-            currentTx.preConcatenate(matrixTx);
-
-            // give it to the graphics as a new matrix replacing all previous transform
-            g.setTransform(currentTx);
-        }
-
-        // draw the bitmap
-        drawBitmap(bitmap, 0, 0, paint);
-
-        if (needsRestore) {
-            // remove the new graphics
-            restore();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
-            int height, boolean hasAlpha, Paint paint) {
-        throw new UnsupportedOperationException();
-    }
-
-    private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft,
-            int dtop, int dright, int dbottom, Paint paint) {
-        BufferedImage image = Bitmap_Delegate.getImage(bitmap);
-
-        Graphics2D g = getGraphics2d();
-
-        Composite c = null;
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g = (Graphics2D)g.create();
-                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-            }
-
-            if (paint.getAlpha() != 0xFF) {
-                c = g.getComposite();
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-                        paint.getAlpha()/255.f));
-            }
-        }
-
-        g.drawImage(image, dleft, dtop, dright, dbottom,
-                sleft, stop, sright, sbottom, null);
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g.dispose();
-            }
-            if (c != null) {
-                g.setComposite(c);
-            }
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float, float, float)
-     */
-    @Override
-    public void rotate(float degrees, float px, float py) {
-        if (degrees != 0) {
-            Graphics2D g = getGraphics2d();
-            g.translate(px, py);
-            g.rotate(Math.toRadians(degrees));
-            g.translate(-px, -py);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float)
-     */
-    @Override
-    public void rotate(float degrees) {
-        getGraphics2d().rotate(Math.toRadians(degrees));
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float, float, float)
-     */
-    @Override
-    public void scale(float sx, float sy, float px, float py) {
-        Graphics2D g = getGraphics2d();
-        g.translate(px, py);
-        g.scale(sx, sy);
-        g.translate(-px, -py);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float)
-     */
-    @Override
-    public void scale(float sx, float sy) {
-        getGraphics2d().scale(sx, sy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
-        // WARNING: the logic in this method is similar to Paint.measureText.
-        // Any change to this method should be reflected in Paint.measureText
-        Graphics2D g = getGraphics2d();
-
-        g = (Graphics2D)g.create();
-        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-
-        // set the color. because this only handles RGB, the alpha channel is handled
-        // as a composite.
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-
-
-        // Paint.TextAlign indicates how the text is positioned relative to X.
-        // LEFT is the default and there's nothing to do.
-        if (paint.getTextAlign() != Align.LEFT) {
-            float m = paint.measureText(text, index, count);
-            if (paint.getTextAlign() == Align.CENTER) {
-                x -= m / 2;
-            } else if (paint.getTextAlign() == Align.RIGHT) {
-                x -= m;
-            }
-        }
-
-        List<FontInfo> fonts = paint.getFonts();
-        try {
-            if (fonts.size() > 0) {
-                FontInfo mainFont = fonts.get(0);
-                int i = index;
-                int lastIndex = index + count;
-                while (i < lastIndex) {
-                    // always start with the main font.
-                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                    if (upTo == -1) {
-                        // draw all the rest and exit.
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
-                        return;
-                    } else if (upTo > 0) {
-                        // draw what's possible
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
-
-                        // compute the width that was drawn to increase x
-                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-
-                        // move index to the first non displayed char.
-                        i = upTo;
-
-                        // don't call continue at this point. Since it is certain the main font
-                        // cannot display the font a index upTo (now ==i), we move on to the
-                        // fallback fonts directly.
-                    }
-
-                    // no char supported, attempt to read the next char(s) with the
-                    // fallback font. In this case we only test the first character
-                    // and then go back to test with the main font.
-                    // Special test for 2-char characters.
-                    boolean foundFont = false;
-                    for (int f = 1 ; f < fonts.size() ; f++) {
-                        FontInfo fontInfo = fonts.get(f);
-
-                        // need to check that the font can display the character. We test
-                        // differently if the char is a high surrogate.
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                        if (upTo == -1) {
-                            // draw that char
-                            g.setFont(fontInfo.mFont);
-                            g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                            // update x
-                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
-
-                            // update the index in the text, and move on
-                            i += charCount;
-                            foundFont = true;
-                            break;
-
-                        }
-                    }
-
-                    // in case no font can display the char, display it with the main font.
-                    // (it'll put a square probably)
-                    if (foundFont == false) {
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                        // measure it to advance x
-                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
-
-                        // and move to the next chars.
-                        i += charCount;
-                    }
-                }
-            }
-        } finally {
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toString().toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), 0, text.length(), x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(RectF rect, Paint paint) {
-        doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
-        doDrawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(Rect r, Paint paint) {
-        doDrawRect(r.left, r.top, r.width(), r.height(), paint);
-    }
-
-    private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
-        if (width > 0 && height > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRect(left, top, width, height);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRect(left, top, width, height);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        if (rect.width() > 0 && rect.height() > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-
-            int arcWidth = (int)(rx * 2);
-            int arcHeight = (int)(ry * 2);
-
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLine(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, int offset, int count, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        for (int i = 0 ; i < count ; i += 4) {
-            g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
-                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, Paint paint) {
-        drawLines(pts, 0, pts.length, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        int size = (int)(radius * 2);
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawOval(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawOval(RectF oval, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint)
-     */
-    @Override
-    public void drawPath(Path path, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fill(path.getAwtShape());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.draw(path.getAwtShape());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void setMatrix(Matrix matrix) {
-        // get the new current graphics
-        Graphics2D g = getGraphics2d();
-
-        // and apply the matrix
-        g.setTransform(Matrix_Delegate.getAffineTransform(matrix));
-
-        if (mLogger != null && Matrix_Delegate.hasPerspective(matrix)) {
-            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
-     */
-    @Override
-    public void concat(Matrix matrix) {
-        // get the current top graphics2D object.
-        Graphics2D g = getGraphics2d();
-
-        // get its current matrix
-        AffineTransform currentTx = g.getTransform();
-        // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
-
-        // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
-    }
-
-
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipPath(Path path, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path)
-     */
-    @Override
-    public boolean clipPath(Path path) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRegion(Region region, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region)
-     */
-    @Override
-    public boolean clipRegion(Region region) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
-            Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
-            int vertOffset, int[] colors, int colorOffset, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.Rect)
-     */
-    @Override
-    public void drawPicture(Picture picture, Rect dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.RectF)
-     */
-    @Override
-    public void drawPicture(Picture picture, RectF dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture)
-     */
-    @Override
-    public void drawPicture(Picture picture) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoint(float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoint(float x, float y, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoint(x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, offset, count, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(char[], int, int, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, index, count, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(java.lang.String, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(String text, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(char[] text, int index, int count, Path path, float offset,
-            float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, index, count, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
-            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
-            int indexOffset, int indexCount, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset,
-                indices, indexOffset, indexCount, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getDrawFilter()
-     */
-    @Override
-    public DrawFilter getDrawFilter() {
-        // TODO Auto-generated method stub
-        return super.getDrawFilter();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix()
-     */
-    @Override
-    public Matrix getMatrix() {
-        // TODO Auto-generated method stub
-        return super.getMatrix();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void getMatrix(Matrix ctm) {
-        // TODO Auto-generated method stub
-        super.getMatrix(ctm);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#isOpaque()
-     */
-    @Override
-    public boolean isOpaque() {
-        // TODO Auto-generated method stub
-        return super.isOpaque();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(float, float, float, float, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(left, top, right, bottom, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(bounds, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(android.graphics.RectF, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(bounds, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter)
-     */
-    @Override
-    public void setDrawFilter(DrawFilter filter) {
-        // TODO Auto-generated method stub
-        super.setDrawFilter(filter);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#skew(float, float)
-     */
-    @Override
-    public void skew(float sx, float sy) {
-        // TODO Auto-generated method stub
-        super.skew(sx, sy);
-    }
-
-
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
new file mode 100644
index 0000000..6627d37
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -0,0 +1,559 @@
+/*
+ * 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.api.ILayoutLog;
+import com.android.layoutlib.bridge.DelegateManager;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.Stack;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Canvas
+ *
+ * Through the layoutlib_create tool, the original native methods of Canvas 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 Canvas class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Canvas_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Canvas_Delegate> sManager =
+            new DelegateManager<Canvas_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private BufferedImage mBufferedImage;
+    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
+    private ILayoutLog mLogger;
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the native delegate associated to a given {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(Canvas canvas) {
+        return sManager.getDelegate(canvas.mNativeCanvas);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(int native_canvas) {
+        return sManager.getDelegate(native_canvas);
+    }
+
+    /**
+     * Sets the layoutlib logger into the canvas.
+     * @param logger
+     */
+    public void setLogger(ILayoutLog logger) {
+        mLogger = logger;
+    }
+
+    /**
+     * Returns the current {@link Graphics2D} used to draw.
+     */
+    public Graphics2D getGraphics2d() {
+        return mGraphicsStack.peek();
+    }
+
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    public void dispose() {
+
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static boolean isOpaque(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int getWidth(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int getHeight(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void skew(Canvas thisCanvas, float sx, float sy) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
+            float bottom) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
+            int bottom) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int save(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void restore(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int getSaveCount(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
+            Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count,
+            Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void freeCaches() {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int initRaster(int nativeBitmapOrZero) {
+        if (nativeBitmapOrZero > 0) {
+            // get the Bitmap from the int
+            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
+
+            // create a new Canvas_Delegate with the given bitmap and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage());
+
+            return sManager.addDelegate(newDelegate);
+        } else {
+            // create a new Canvas_Delegate and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate();
+
+            return sManager.addDelegate(newDelegate);
+        }
+    }
+
+    /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.setBitmap(bitmapDelegate.getImage());
+    }
+
+    /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
+                                               int paint, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayer(int nativeCanvas, float l,
+                                               float t, float r, float b,
+                                               int paint, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
+                                                    RectF bounds, int alpha,
+                                                    int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
+                                                    float t, float r, float b,
+                                                    int alpha, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_concat(int nCanvas, int nMatrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipRect(int nCanvas,
+                                                  float left, float top,
+                                                  float right, float bottom,
+                                                  int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipPath(int nativeCanvas,
+                                                  int nativePath,
+                                                  int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipRegion(int nativeCanvas,
+                                                    int nativeRegion,
+                                                    int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeSetDrawFilter(int nativeCanvas,
+                                                   int nativeFilter) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_getClipBounds(int nativeCanvas,
+                                                       Rect bounds) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_getCTM(int canvas, int matrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     RectF rect,
+                                                     int native_edgeType) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     int path,
+                                                     int native_edgeType) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     float left, float top,
+                                                     float right, float bottom,
+                                                     int native_edgeType) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g,
+                                              int b) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r,
+                                               int g, int b) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawColor(int nativeCanvas, int color) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawColor(int nativeCanvas, int color,
+                                                int mode) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawLine(int nativeCanvas, float startX,
+                                               float startY, float stopX,
+                                               float stopY, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
+                                               int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawRect(int nativeCanvas, float left,
+                                               float top, float right,
+                                               float bottom, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
+                                               int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawCircle(int nativeCanvas, float cx,
+                                                 float cy, float radius,
+                                                 int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawArc(int nativeCanvas, RectF oval,
+                                              float startAngle, float sweep,
+                                              boolean useCenter, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawRoundRect(int nativeCanvas,
+                                                    RectF rect, float rx,
+                                                    float ry, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPath(int nativeCanvas, int path,
+                                               int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 float left, float top,
+                                                 int nativePaintOrZero,
+                                                 int canvasDensity,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 Rect src, RectF dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
+                                                 Rect src, Rect dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
+                                                int offset, int stride, float x,
+                                                 float y, int width, int height,
+                                                 boolean hasAlpha,
+                                                 int nativePaintOrZero) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
+                                                      int nMatrix, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
+                                                    int meshWidth, int meshHeight,
+                                                    float[] verts, int vertOffset,
+                                                    int[] colors, int colorOffset, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
+                   float[] verts, int vertOffset, float[] texs, int texOffset,
+                   int[] colors, int colorOffset, short[] indices,
+                   int indexOffset, int indexCount, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawText(int nativeCanvas, char[] text,
+                                               int index, int count, float x,
+                                               float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawText(int nativeCanvas, String text,
+                                               int start, int end, float x,
+                                               float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
+            int start, int end, int contextStart, int contextEnd,
+            float x, float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
+            int start, int count, int contextStart, int contextCount,
+            float x, float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  char[] text, int index,
+                                                  int count, float[] pos,
+                                                  int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  String text, float[] pos,
+                                                  int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     char[] text, int index,
+                                                     int count, int path,
+                                                     float hOffset,
+                                                     float vOffset, int bidiFlags,
+                                                     int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     String text, int path,
+                                                     float hOffset,
+                                                     float vOffset,
+                                                     int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPicture(int nativeCanvas,
+                                                  int nativePicture) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void finalizer(int nativeCanvas) {
+        sManager.removeDelegate(nativeCanvas);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Canvas_Delegate(BufferedImage image) {
+        setBitmap(image);
+    }
+
+    private Canvas_Delegate() {
+    }
+
+    private void setBitmap(BufferedImage image) {
+        mBufferedImage = image;
+        mGraphicsStack.push(mBufferedImage.createGraphics());
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
deleted file mode 100644
index 2de21c1..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ /dev/null
@@ -1,1211 +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 android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.SpannedString;
-import android.text.TextUtils;
-
-import java.awt.BasicStroke;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A paint implementation overridden by the LayoutLib bridge.
- */
-public class Paint extends _Original_Paint {
-
-    private int mColor = 0xFFFFFFFF;
-    private float mStrokeWidth = 1.f;
-    private float mTextSize = 20;
-    private float mScaleX = 1;
-    private float mSkewX = 0;
-    private Align mAlign = Align.LEFT;
-    private Style mStyle = Style.FILL;
-    private float mStrokeMiter = 4.0f;
-    private Cap mCap = Cap.BUTT;
-    private Join mJoin = Join.MITER;
-    private int mFlags = 0;
-
-    /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
-     */
-    public static final class FontInfo {
-        Font mFont;
-        java.awt.FontMetrics mMetrics;
-    }
-
-    private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
-
-    public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
-    public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
-    public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
-    public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
-    public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
-    public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
-    public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
-    public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
-    public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
-
-    public static class FontMetrics extends _Original_Paint.FontMetrics {
-    }
-
-    public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
-    }
-
-    /**
-     * The Style specifies if the primitive being drawn is filled,
-     * stroked, or both (in the same color). The default is FILL.
-     */
-    public enum Style {
-        /**
-         * Geometry and text drawn with this style will be filled, ignoring all
-         * stroke-related settings in the paint.
-         */
-        FILL            (0),
-        /**
-         * Geometry and text drawn with this style will be stroked, respecting
-         * the stroke-related fields on the paint.
-         */
-        STROKE          (1),
-        /**
-         * Geometry and text drawn with this style will be both filled and
-         * stroked at the same time, respecting the stroke-related fields on
-         * the paint.
-         */
-        FILL_AND_STROKE (2);
-
-        Style(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * The Cap specifies the treatment for the beginning and ending of
-     * stroked lines and paths. The default is BUTT.
-     */
-    public enum Cap {
-        /**
-         * The stroke ends with the path, and does not project beyond it.
-         */
-        BUTT    (0),
-        /**
-         * The stroke projects out as a square, with the center at the end
-         * of the path.
-         */
-        ROUND   (1),
-        /**
-         * The stroke projects out as a semicircle, with the center at the
-         * end of the path.
-         */
-        SQUARE  (2);
-
-        private Cap(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaCap() {
-            switch (this) {
-                case BUTT:
-                    return BasicStroke.CAP_BUTT;
-                case ROUND:
-                    return BasicStroke.CAP_ROUND;
-                default:
-                case SQUARE:
-                    return BasicStroke.CAP_SQUARE;
-            }
-        }
-    }
-
-    /**
-     * The Join specifies the treatment where lines and curve segments
-     * join on a stroked path. The default is MITER.
-     */
-    public enum Join {
-        /**
-         * The outer edges of a join meet at a sharp angle
-         */
-        MITER   (0),
-        /**
-         * The outer edges of a join meet in a circular arc.
-         */
-        ROUND   (1),
-        /**
-         * The outer edges of a join meet with a straight line
-         */
-        BEVEL   (2);
-
-        private Join(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaJoin() {
-            switch (this) {
-                default:
-                case MITER:
-                    return BasicStroke.JOIN_MITER;
-                case ROUND:
-                    return BasicStroke.JOIN_ROUND;
-                case BEVEL:
-                    return BasicStroke.JOIN_BEVEL;
-            }
-        }
-    }
-
-    /**
-     * Align specifies how drawText aligns its text relative to the
-     * [x,y] coordinates. The default is LEFT.
-     */
-    public enum Align {
-        /**
-         * The text is drawn to the right of the x,y origin
-         */
-        LEFT    (0),
-        /**
-         * The text is drawn centered horizontally on the x,y origin
-         */
-        CENTER  (1),
-        /**
-         * The text is drawn to the left of the x,y origin
-         */
-        RIGHT   (2);
-
-        private Align(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    public Paint() {
-        this(0);
-    }
-
-    /*
-     * Do not remove or com.android.layoutlib.bridge.TestClassReplacement fails.
-     */
-    @Override
-    public void finalize() { }
-
-    public Paint(int flags) {
-        setFlags(flags | DEFAULT_PAINT_FLAGS);
-        initFont();
-    }
-
-    public Paint(Paint paint) {
-        set(paint);
-        initFont();
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-    }
-
-    /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
-     */
-    public List<FontInfo> getFonts() {
-        return mFonts;
-    }
-
-    private void initFont() {
-        mTypeface = Typeface.DEFAULT;
-        updateFontObject();
-    }
-
-    /**
-     * Update the {@link Font} object from the typeface, text size and scaling
-     */
-    @SuppressWarnings("deprecation")
-    private void updateFontObject() {
-        if (mTypeface != null) {
-            // Get the fonts from the TypeFace object.
-            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
-
-            // create new font objects as well as FontMetrics, based on the current text size
-            // and skew info.
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
-            for (Font font : fonts) {
-                FontInfo info = new FontInfo();
-                info.mFont = font.deriveFont(mTextSize);
-                if (mScaleX != 1.0 || mSkewX != 0) {
-                    // TODO: support skew
-                    info.mFont = info.mFont.deriveFont(new AffineTransform(
-                            mScaleX, mSkewX, 0, 0, 1, 0));
-                }
-                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
-
-                infoList.add(info);
-            }
-
-            mFonts = Collections.unmodifiableList(infoList);
-        }
-    }
-
-    //----------------------------------------
-
-    public void set(Paint src) {
-        if (this != src) {
-            mColor = src.mColor;
-            mTextSize = src.mTextSize;
-            mScaleX = src.mScaleX;
-            mSkewX = src.mSkewX;
-            mAlign = src.mAlign;
-            mStyle = src.mStyle;
-            mFlags = src.mFlags;
-
-            updateFontObject();
-
-            super.set(src);
-        }
-    }
-
-    @Override
-    public void setCompatibilityScaling(float factor) {
-        super.setCompatibilityScaling(factor);
-    }
-
-    @Override
-    public int getFlags() {
-        return mFlags;
-    }
-
-    @Override
-    public void setFlags(int flags) {
-        mFlags = flags;
-    }
-
-    @Override
-    public boolean isAntiAlias() {
-        return super.isAntiAlias();
-    }
-
-    @Override
-    public boolean isDither() {
-        return super.isDither();
-    }
-
-    @Override
-    public boolean isLinearText() {
-        return super.isLinearText();
-    }
-
-    @Override
-    public boolean isStrikeThruText() {
-        return super.isStrikeThruText();
-    }
-
-    @Override
-    public boolean isUnderlineText() {
-        return super.isUnderlineText();
-    }
-
-    @Override
-    public boolean isFakeBoldText() {
-        return super.isFakeBoldText();
-    }
-
-    @Override
-    public boolean isSubpixelText() {
-        return super.isSubpixelText();
-    }
-
-    @Override
-    public boolean isFilterBitmap() {
-        return super.isFilterBitmap();
-    }
-
-    /**
-     * Return the font's recommended interline spacing, given the Paint's
-     * settings for typeface, textSize, etc. If metrics is not null, return the
-     * fontmetric values in it.
-     *
-     * @param metrics If this object is not null, its fields are filled with
-     *                the appropriate values given the paint's text attributes.
-     * @return the font's recommended interline spacing.
-     */
-    public float getFontMetrics(FontMetrics metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    public int getFontMetricsInt(FontMetricsInt metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
-     */
-    public FontMetrics getFontMetrics() {
-        FontMetrics fm = new FontMetrics();
-        getFontMetrics(fm);
-        return fm;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
-     */
-    public FontMetricsInt getFontMetricsInt() {
-        FontMetricsInt fm = new FontMetricsInt();
-        getFontMetricsInt(fm);
-        return fm;
-    }
-
-
-
-    @Override
-    public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public Typeface setTypeface(Typeface typeface) {
-        if (typeface != null) {
-            mTypeface = typeface;
-        } else {
-            mTypeface = Typeface.DEFAULT;
-        }
-
-        updateFontObject();
-
-        return typeface;
-    }
-
-    @Override
-    public Typeface getTypeface() {
-        return super.getTypeface();
-    }
-
-    @Override
-    public int getColor() {
-        return mColor;
-    }
-
-    @Override
-    public void setColor(int color) {
-        mColor = color;
-    }
-
-    @Override
-    public void setARGB(int a, int r, int g, int b) {
-        super.setARGB(a, r, g, b);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
-    }
-
-    @Override
-    public int getAlpha() {
-        return mColor >>> 24;
-    }
-
-    /**
-     * Set or clear the shader object.
-     * <p />
-     * Pass null to clear any previous shader.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param shader May be null. the new shader to be installed in the paint
-     * @return       shader
-     */
-    @Override
-    public Shader setShader(Shader shader) {
-        return mShader = shader;
-    }
-
-    @Override
-    public Shader getShader() {
-        return super.getShader();
-    }
-
-    /**
-     * Set or clear the paint's colorfilter, returning the parameter.
-     *
-     * @param filter May be null. The new filter to be installed in the paint
-     * @return       filter
-     */
-    @Override
-    public ColorFilter setColorFilter(ColorFilter filter) {
-        mColorFilter = filter;
-        return filter;
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return super.getColorFilter();
-    }
-
-    /**
-     * Set or clear the xfermode object.
-     * <p />
-     * Pass null to clear any previous xfermode.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param xfermode May be null. The xfermode to be installed in the paint
-     * @return         xfermode
-     */
-    @Override
-    public Xfermode setXfermode(Xfermode xfermode) {
-        return mXfermode = xfermode;
-    }
-
-    @Override
-    public Xfermode getXfermode() {
-        return super.getXfermode();
-    }
-
-    @Override
-    public Rasterizer setRasterizer(Rasterizer rasterizer) {
-        mRasterizer = rasterizer;
-        return rasterizer;
-    }
-
-    @Override
-    public Rasterizer getRasterizer() {
-        return super.getRasterizer();
-    }
-
-    @Override
-    public void setShadowLayer(float radius, float dx, float dy, int color) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void clearShadowLayer() {
-        super.clearShadowLayer();
-    }
-
-    public void setTextAlign(Align align) {
-        mAlign = align;
-    }
-
-    @Override
-    public void setTextAlign(android.graphics._Original_Paint.Align align) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Align getTextAlign() {
-        return mAlign;
-    }
-
-    public void setStyle(Style style) {
-        mStyle = style;
-    }
-
-    @Override
-    public void setStyle(android.graphics._Original_Paint.Style style) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Style getStyle() {
-        return mStyle;
-    }
-
-    @Override
-    public void setDither(boolean dither) {
-        mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
-    }
-
-    @Override
-    public void setAntiAlias(boolean aa) {
-        mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
-    }
-
-    @Override
-    public void setFakeBoldText(boolean flag) {
-        mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
-    }
-
-    @Override
-    public void setLinearText(boolean flag) {
-        mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG;
-    }
-
-    @Override
-    public void setSubpixelText(boolean flag) {
-        mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG;
-    }
-
-    @Override
-    public void setUnderlineText(boolean flag) {
-        mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG;
-    }
-
-    @Override
-    public void setStrikeThruText(boolean flag) {
-        mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG;
-    }
-
-    @Override
-    public void setFilterBitmap(boolean flag) {
-        mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG;
-    }
-
-    @Override
-    public float getStrokeWidth() {
-        return mStrokeWidth;
-    }
-
-    @Override
-    public void setStrokeWidth(float width) {
-        mStrokeWidth = width;
-    }
-
-    @Override
-    public float getStrokeMiter() {
-        return mStrokeMiter;
-    }
-
-    @Override
-    public void setStrokeMiter(float miter) {
-        mStrokeMiter = miter;
-    }
-
-    @Override
-    public void setStrokeCap(android.graphics._Original_Paint.Cap cap) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeCap(Cap cap) {
-        mCap = cap;
-    }
-
-    public Cap getStrokeCap() {
-        return mCap;
-    }
-
-    @Override
-    public void setStrokeJoin(android.graphics._Original_Paint.Join join) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeJoin(Join join) {
-        mJoin = join;
-    }
-
-    public Join getStrokeJoin() {
-        return mJoin;
-    }
-
-    @Override
-    public boolean getFillPath(Path src, Path dst) {
-        return super.getFillPath(src, dst);
-    }
-
-    @Override
-    public PathEffect setPathEffect(PathEffect effect) {
-        mPathEffect = effect;
-        return effect;
-    }
-
-    @Override
-    public PathEffect getPathEffect() {
-        return super.getPathEffect();
-    }
-
-    @Override
-    public MaskFilter setMaskFilter(MaskFilter maskfilter) {
-        mMaskFilter = maskfilter;
-        return maskfilter;
-    }
-
-    @Override
-    public MaskFilter getMaskFilter() {
-        return super.getMaskFilter();
-    }
-
-    /**
-     * Return the paint's text size.
-     *
-     * @return the paint's text size.
-     */
-    @Override
-    public float getTextSize() {
-        return mTextSize;
-    }
-
-    /**
-     * Set the paint's text size. This value must be > 0
-     *
-     * @param textSize set the paint's text size.
-     */
-    @Override
-    public void setTextSize(float textSize) {
-        mTextSize = textSize;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal scale factor for text. The default value
-     * is 1.0.
-     *
-     * @return the paint's scale factor in X for drawing/measuring text
-     */
-    @Override
-    public float getTextScaleX() {
-        return mScaleX;
-    }
-
-    /**
-     * Set the paint's horizontal scale factor for text. The default value
-     * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
-     * stretch the text narrower.
-     *
-     * @param scaleX set the paint's scale in X for drawing/measuring text.
-     */
-    @Override
-    public void setTextScaleX(float scaleX) {
-        mScaleX = scaleX;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal skew factor for text. The default value
-     * is 0.
-     *
-     * @return         the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public float getTextSkewX() {
-        return mSkewX;
-    }
-
-    /**
-     * Set the paint's horizontal skew factor for text. The default value
-     * is 0. For approximating oblique text, use values around -0.25.
-     *
-     * @param skewX set the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public void setTextSkewX(float skewX) {
-        mSkewX = skewX;
-
-        updateFontObject();
-    }
-
-    @Override
-    public float getFontSpacing() {
-        return super.getFontSpacing();
-    }
-
-    /**
-     * Return the distance above (negative) the baseline (ascent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance above (negative) the baseline (ascent) based on the
-     *         current typeface and text size.
-     */
-    @Override
-    public float ascent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            // Android expects negative ascent so we invert the value from Java.
-            return - javaMetrics.getAscent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the distance below (positive) the baseline (descent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance below (positive) the baseline (descent) based on
-     *         the current typeface and text size.
-     */
-    @Override
-    public float descent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            return javaMetrics.getDescent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param index The index of the first character to start measuring
-     * @param count THe number of characters to measure, beginning with start
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(char[] text, int index, int count) {
-        // WARNING: the logic in this method is similar to Canvas.drawText.
-        // Any change to this method should be reflected in Canvas.drawText
-        if (mFonts.size() > 0) {
-            FontInfo mainFont = mFonts.get(0);
-            int i = index;
-            int lastIndex = index + count;
-            float total = 0f;
-            while (i < lastIndex) {
-                // always start with the main font.
-                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                if (upTo == -1) {
-                    // shortcut to exit
-                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
-                } else if (upTo > 0) {
-                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-                    i = upTo;
-                    // don't call continue at this point. Since it is certain the main font
-                    // cannot display the font a index upTo (now ==i), we move on to the
-                    // fallback fonts directly.
-                }
-
-                // no char supported, attempt to read the next char(s) with the
-                // fallback font. In this case we only test the first character
-                // and then go back to test with the main font.
-                // Special test for 2-char characters.
-                boolean foundFont = false;
-                for (int f = 1 ; f < mFonts.size() ; f++) {
-                    FontInfo fontInfo = mFonts.get(f);
-
-                    // need to check that the font can display the character. We test
-                    // differently if the char is a high surrogate.
-                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                    if (upTo == -1) {
-                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
-                        i += charCount;
-                        foundFont = true;
-                        break;
-
-                    }
-                }
-
-                // in case no font can display the char, measure it with the main font.
-                if (foundFont == false) {
-                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    total += mainFont.mMetrics.charsWidth(text, i, size);
-                    i += size;
-                }
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param start The index of the first character to start measuring
-     * @param end   1 beyond the index of the last character to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text, int start, int end) {
-        return measureText(text.toCharArray(), start, end - start);
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text) {
-        return measureText(text.toCharArray(), 0, text.length());
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.measureText with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public float measureText(CharSequence text, int start, int end) {
-        if (text instanceof String) {
-            return measureText((String)text, start, end);
-        }
-        if (text instanceof SpannedString ||
-            text instanceof SpannableString) {
-            return measureText(text.toString(), start, end);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).measureText(start, end, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        float result = measureText(buf, 0, end - start);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param index The offset into text to begin measuring at
-     * @param count The number of maximum number of entries to measure. If count
-     *              is negative, then the characters before index are measured
-     *              in reverse order. This allows for measuring the end of
-     *              string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(char[] text, int index, int count,
-                                float maxWidth, float[] measuredWidth) {
-        int inc = count > 0 ? 1 : -1;
-
-        int measureIndex = 0;
-        float measureAcc = 0;
-        for (int i = index ; i != index + count ; i += inc, measureIndex++) {
-            int start, end;
-            if (i < index) {
-                start = i;
-                end = index;
-            } else {
-                start = index;
-                end = i;
-            }
-
-            // measure from start to end
-            float res = measureText(text, start, end - start + 1);
-
-            if (measuredWidth != null) {
-                measuredWidth[measureIndex] = res;
-            }
-
-            measureAcc += res;
-            if (res > maxWidth) {
-                // we should not return this char index, but since it's 0-based and we need
-                // to return a count, we simply return measureIndex;
-                return measureIndex;
-            }
-
-        }
-
-        return measureIndex;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param measureForwards If true, measure forwards, starting at index.
-     *                        Otherwise, measure backwards, starting with the
-     *                        last character in the string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(String text, boolean measureForwards,
-                                float maxWidth, float[] measuredWidth) {
-        return breakText(text,
-                0 /* start */, text.length() /* end */,
-                measureForwards, maxWidth, measuredWidth);
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param start The offset into text to begin measuring at
-     * @param end   The end of the text slice to measure.
-     * @param measureForwards If true, measure forwards, starting at start.
-     *                        Otherwise, measure backwards, starting with end.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(end - start).
-     */
-    @Override
-    public int breakText(CharSequence text, int start, int end, boolean measureForwards,
-            float maxWidth, float[] measuredWidth) {
-        char[] buf = new char[end - start];
-        int result;
-
-        TextUtils.getChars(text, start, end, buf, 0);
-
-        if (measureForwards) {
-            result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
-        } else {
-            result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
-        }
-
-        return result;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text     The text to measure
-     * @param index    The index of the first char to to measure
-     * @param count    The number of chars starting with index to measure
-     * @param widths   array to receive the advance widths of the characters.
-     *                 Must be at least a large as count.
-     * @return         the actual number of widths returned.
-     */
-    @Override
-    public int getTextWidths(char[] text, int index, int count,
-                             float[] widths) {
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length
-                    || count > widths.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-
-            // FIXME: handle multi-char characters.
-            // Need to figure out if the lengths of the width array takes into account
-            // multi-char characters.
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        widths[i] = info.mMetrics.charWidth(c);
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // we stop there.
-                    return i;
-                }
-            }
-
-            return count;
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text   The text to measure
-     * @param start  The index of the first char to to measure
-     * @param end    The end of the text slice to measure
-     * @param widths array to receive the advance widths of the characters.
-     *               Must be at least a large as the text.
-     * @return       the number of unichars in the specified text.
-     */
-    @Override
-    public int getTextWidths(String text, int start, int end, float[] widths) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (end - start > widths.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        return getTextWidths(text.toCharArray(), start, end - start, widths);
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
-        if (text instanceof String) {
-            return getTextWidths((String)text, start, end, widths);
-        }
-        if (text instanceof SpannedString || text instanceof SpannableString) {
-            return getTextWidths(text.toString(), start, end, widths);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        int result = getTextWidths(buf, 0, end - start, widths);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    @Override
-    public int getTextWidths(String text, float[] widths) {
-        return super.getTextWidths(text, widths);
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting in
-     * the paint.
-     *
-     * @param text     The text to retrieve the path from
-     * @param index    The index of the first character in text
-     * @param count    The number of characterss starting with index
-     * @param x        The x coordinate of the text's origin
-     * @param y        The y coordinate of the text's origin
-     * @param path     The path to receive the data describing the text. Must
-     *                 be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(char[] text, int index, int count,
-                            float x, float y, Path path) {
-
-        // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
-
-        if ((index | count) < 0 || index + count > text.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
-
-        throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting
-     * in the paint.
-     *
-     * @param text  The text to retrieve the path from
-     * @param start The first character in the text
-     * @param end   1 past the last charcter in the text
-     * @param x     The x coordinate of the text's origin
-     * @param y     The y coordinate of the text's origin
-     * @param path  The path to receive the data describing the text. Must
-     *              be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(String text, int start, int end,
-                            float x, float y, Path path) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        getTextPath(text.toCharArray(), start, end - start, x, y, path);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  String to measure and return its bounds
-     * @param start Index of the first char in the string to measure
-     * @param end   1 past the last char in the string measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(String text, int start, int end, Rect bounds) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (bounds == null) {
-            throw new NullPointerException("need bounds Rect");
-        }
-
-        getTextBounds(text.toCharArray(), start, end - start, bounds);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  Array of chars to measure and return their unioned bounds
-     * @param index Index of the first char in the array to measure
-     * @param count The number of chars, beginning at index, to measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(char[] text, int index, int count, Rect bounds) {
-        // FIXME
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            if (bounds == null) {
-                throw new NullPointerException("need bounds Rect");
-            }
-
-            FontInfo mainInfo = mFonts.get(0);
-
-            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext);
-            bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
-        }
-    }
-
-    public static void finalizer(int foo) {
-        // pass
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
new file mode 100644
index 0000000..e8079ed
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -0,0 +1,750 @@
+/*
+ * 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.Paint.FontMetrics;
+import android.graphics.Paint.FontMetricsInt;
+
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Paint
+ *
+ * Through the layoutlib_create tool, the original native methods of Paint 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 Paint class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Paint_Delegate {
+
+    /**
+     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     */
+    public static final class FontInfo {
+        Font mFont;
+        java.awt.FontMetrics mMetrics;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Paint_Delegate> sManager =
+            new DelegateManager<Paint_Delegate>();
+
+    // ---- delegate helper data ----
+    private List<FontInfo> mFonts;
+    private final FontRenderContext mFontContext = new FontRenderContext(
+            new AffineTransform(), true, true);
+
+    // ---- delegate data ----
+    private int mFlags;
+    private int mColor;
+    private int mStyle;
+    private int mCap;
+    private int mJoin;
+    private int mAlign;
+    private int mTypeface;
+    private float mStrokeWidth;
+    private float mStrokeMiter;
+    private float mTextSize;
+    private float mTextScaleX;
+    private float mTextSkewX;
+
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the list of {@link Font} objects. The first item is the main font, the rest
+     * are fall backs for characters not present in the main font.
+     */
+    public List<FontInfo> getFonts() {
+        return mFonts;
+    }
+
+
+    // ---- native methods ----
+
+    /*package*/ static int getFlags(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mFlags;
+    }
+
+    /*package*/ static void setFlags(Paint thisPaint, int flags) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mFlags = flags;
+    }
+
+    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
+        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
+    }
+
+    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
+        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
+    }
+
+    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
+        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
+    }
+
+    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
+        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
+    }
+
+    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
+        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
+    }
+
+    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
+        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
+    }
+
+    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
+        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
+    }
+
+    /*package*/ static int getColor(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mColor;
+    }
+
+    /*package*/ static void setColor(Paint thisPaint, int color) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mColor = color;
+    }
+
+    /*package*/ static int getAlpha(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mColor >>> 24;
+    }
+
+    /*package*/ static void setAlpha(Paint thisPaint, int a) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
+    }
+
+    /*package*/ static float getStrokeWidth(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mStrokeWidth;
+    }
+
+    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStrokeWidth = width;
+    }
+
+    /*package*/ static float getStrokeMiter(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mStrokeMiter;
+    }
+
+    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStrokeMiter = miter;
+    }
+
+    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
+            int color) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float getTextSize(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextSize;
+    }
+
+    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextSize = textSize;
+    }
+
+    /*package*/ static float getTextScaleX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextScaleX;
+    }
+
+    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextScaleX = scaleX;
+    }
+
+    /*package*/ static float getTextSkewX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextSkewX;
+    }
+
+    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextSkewX = skewX;
+    }
+
+    /*package*/ static float ascent(Paint thisPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float descent(Paint thisPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
+            int count) {
+        // WARNING: the logic in this method is similar to Canvas.drawText.
+        // Any change to this method should be reflected in Canvas.drawText
+
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            FontInfo mainFont = delegate.mFonts.get(0);
+            int i = index;
+            int lastIndex = index + count;
+            float total = 0f;
+            while (i < lastIndex) {
+                // always start with the main font.
+                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                if (upTo == -1) {
+                    // shortcut to exit
+                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
+                } else if (upTo > 0) {
+                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+                    i = upTo;
+                    // don't call continue at this point. Since it is certain the main font
+                    // cannot display the font a index upTo (now ==i), we move on to the
+                    // fallback fonts directly.
+                }
+
+                // no char supported, attempt to read the next char(s) with the
+                // fallback font. In this case we only test the first character
+                // and then go back to test with the main font.
+                // Special test for 2-char characters.
+                boolean foundFont = false;
+                for (int f = 1 ; f < delegate.mFonts.size() ; f++) {
+                    FontInfo fontInfo = delegate.mFonts.get(f);
+
+                    // need to check that the font can display the character. We test
+                    // differently if the char is a high surrogate.
+                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                    if (upTo == -1) {
+                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
+                        i += charCount;
+                        foundFont = true;
+                        break;
+
+                    }
+                }
+
+                // in case no font can display the char, measure it with the main font.
+                if (foundFont == false) {
+                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    total += mainFont.mMetrics.charsWidth(text, i, size);
+                    i += size;
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
+        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, String text) {
+        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
+    }
+
+    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
+            float maxWidth, float[] measuredWidth) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+            float maxWidth, float[] measuredWidth) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static int native_init() {
+        Paint_Delegate newDelegate = new Paint_Delegate();
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static int native_initWithPaint(int paint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(paint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static void native_reset(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.reset();
+    }
+
+    /*package*/ static void native_set(int native_dst, int native_src) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
+        if (delegate_dst == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
+        if (delegate_src == null) {
+            assert false;
+            return;
+        }
+
+        delegate_dst.set(delegate_src);
+    }
+
+    /*package*/ static int native_getStyle(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mStyle;
+    }
+
+    /*package*/ static void native_setStyle(int native_object, int style) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStyle = style;
+    }
+
+    /*package*/ static int native_getStrokeCap(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mCap;
+    }
+
+    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mCap = cap;
+    }
+
+    /*package*/ static int native_getStrokeJoin(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mJoin;
+    }
+
+    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mJoin = join;
+    }
+
+    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setShader(int native_object, int shader) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setColorFilter(int native_object, int filter) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setPathEffect(int native_object, int effect) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setTypeface(int native_object, int typeface) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mTypeface = typeface;
+    }
+
+    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static int native_getTextAlign(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mAlign;
+    }
+
+    /*package*/ static void native_setTextAlign(int native_object, int align) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mAlign = align;
+    }
+
+    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
+            int count, float[] widths) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
+            int end, float[] widths) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            char[] text, int index, int count, int contextIndex, int contextCount,
+            int flags, float[] advances, int advancesIndex) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
+            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
+            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+                char[] text, int index, int count, float x, float y, int path) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+            String text, int start, int end, float x, float y, int path) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
+            int end, Rect bounds) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
+            int count, Rect bounds) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void finalizer(int nativePaint) {
+        sManager.removeDelegate(nativePaint);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Paint_Delegate() {
+        reset();
+
+        mTypeface = Typeface.sDefaults[0].native_instance;
+        updateFontObject();
+    }
+
+    private Paint_Delegate(Paint_Delegate paint) {
+        set(paint);
+        updateFontObject();
+    }
+
+    private void set(Paint_Delegate paint) {
+        mFlags = paint.mFlags;
+        mColor = paint.mColor;
+        mStyle = paint.mStyle;
+        mCap = paint.mCap;
+        mJoin = paint.mJoin;
+        mAlign = paint.mAlign;
+        mTypeface = paint.mTypeface;
+        mStrokeWidth = paint.mStrokeWidth;
+        mStrokeMiter = paint.mStrokeMiter;
+        mTextSize = paint.mTextSize;
+        mTextScaleX = paint.mTextScaleX;
+        mTextSkewX = paint.mTextSkewX;
+    }
+
+    private void reset() {
+        mFlags = Paint.DEFAULT_PAINT_FLAGS;
+        mColor = 0;
+        mStyle = 0;
+        mCap = 0;
+        mJoin = 0;
+        mAlign = 0;
+        mTypeface = 0;
+        mStrokeWidth = 1.f;
+        mStrokeMiter = 2.f;
+        mTextSize = 20.f;
+        mTextScaleX = 1.f;
+        mTextSkewX = 0.f;
+    }
+
+    /**
+     * Update the {@link Font} object from the typeface, text size and scaling
+     */
+    private void updateFontObject() {
+        if (mTypeface != 0) {
+            // Get the fonts from the TypeFace object.
+            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
+
+            // create new font objects as well as FontMetrics, based on the current text size
+            // and skew info.
+            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
+            for (Font font : fonts) {
+                FontInfo info = new FontInfo();
+                info.mFont = font.deriveFont(mTextSize);
+                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
+                    // TODO: support skew
+                    info.mFont = info.mFont.deriveFont(new AffineTransform(
+                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
+                }
+                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
+
+                infoList.add(info);
+            }
+
+            mFonts = Collections.unmodifiableList(infoList);
+        }
+    }
+
+    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        if (flagValue) {
+            delegate.mFlags |= flagMask;
+        } else {
+            delegate.mFlags &= ~flagMask;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 248bdab..7e90e7d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -72,7 +72,11 @@
     }
 
     public static List<Font> getFonts(Typeface typeface) {
-        Typeface_Delegate delegate = sManager.getDelegate(typeface.native_instance);
+        return getFonts(typeface.native_instance);
+    }
+
+    public static List<Font> getFonts(int native_int) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_int);
         if (delegate == null) {
             assert false;
             return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 9eb83c8..cdb4148 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,6 +24,7 @@
 import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IStyleResourceValue;
 import com.android.layoutlib.api.IXmlPullParser;
+import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
 import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
 import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
 import com.android.ninepatch.NinePatch;
@@ -33,7 +34,9 @@
 import android.content.ClipData;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Typeface_Delegate;
@@ -64,6 +67,7 @@
 import android.widget.TabHost;
 import android.widget.TabWidget;
 
+import java.awt.image.BufferedImage;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -450,13 +454,28 @@
             view.layout(0, screenOffset, screenWidth, screenHeight);
 
             // draw the views
-            Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger);
+            // create the BufferedImage into which the layout will be rendered.
+            BufferedImage image = new BufferedImage(screenWidth, screenHeight - screenOffset,
+                    BufferedImage.TYPE_INT_ARGB);
+
+            // create an Android bitmap around the BufferedImage
+            Bitmap bitmap = Bitmap_Delegate.createBitmap(image, Density.getEnum(density));
+
+            // create a Canvas around the Android bitmap
+            Canvas canvas = new Canvas(bitmap);
+
+            // to set the logger, get the native delegate
+            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+            canvasDelegate.setLogger(logger);
+
 
             root.draw(canvas);
-            canvas.dispose();
+            canvasDelegate.dispose();
 
-            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
-                    canvas.getImage());
+            return new LayoutResult(
+                    visit(((ViewGroup)view).getChildAt(0), context),
+                    image);
+
         } catch (PostInflateException e) {
             return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n"
                     + e.getMessage());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
index abbf2f0..2c92567 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
@@ -19,6 +19,7 @@
 import com.android.ninepatch.NinePatch;
 
 import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
 import android.graphics.ColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -80,7 +81,8 @@
     @Override
     public void draw(Canvas canvas) {
         Rect r = getBounds();
-        m9Patch.draw(canvas.getGraphics2d(), r.left, r.top, r.width(), r.height());
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+        m9Patch.draw(canvasDelegate.getGraphics2d(), r.left, r.top, r.width(), r.height());
 
         return;
     }
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
index 6e14e82..ba3c51a 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
@@ -17,8 +17,6 @@
 package com.android.layoutlib.bridge;
 
 import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics._Original_Paint;
 import android.text.TextPaint;
 
 import junit.framework.TestCase;
@@ -58,14 +56,6 @@
         }
     }
 
-    public void testPaint() {
-        _Original_Paint o = new _Original_Paint();
-        assertNotNull(o);
-
-        Paint p = new Paint();
-        assertNotNull(p);
-    }
-
     public void textTextPaint() {
         TextPaint p = new TextPaint();
         assertNotNull(p);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
index 7c1eecd..a86b5c9 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
@@ -87,7 +87,11 @@
 
             try {
                 // try to load the method with the given parameter types.
-                delegateClass.getDeclaredMethod(originalMethod.getName(), parameters);
+                Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
+                        parameters);
+
+                // check that the method is static
+                assertTrue((delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
             } catch (NoSuchMethodException e) {
                 // compute a full class name that's long but not too long.
                 StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
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 0ecb474..c845cc4 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
@@ -104,7 +104,9 @@
      */
     private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
         "android.graphics.Bitmap",
+        "android.graphics.Canvas",
         "android.graphics.Matrix",
+        "android.graphics.Paint",
         "android.graphics.Typeface",
     };
 
@@ -126,11 +128,9 @@
         new String[] {
             "android.graphics.BitmapFactory",       "android.graphics._Original_BitmapFactory",
             "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
-            "android.graphics.Canvas",              "android.graphics._Original_Canvas",
             "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
             "android.graphics.DashPathEffect",       "android.graphics._Original_DashPathEffect",
             "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
-            "android.graphics.Paint",               "android.graphics._Original_Paint",
             "android.graphics.Path",                "android.graphics._Original_Path",
             "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",
             "android.graphics.RadialGradient",      "android.graphics._Original_RadialGradient",
@@ -150,13 +150,6 @@
      */
     private final static String[] DELETE_RETURNS =
         new String[] {
-            "android.graphics.Paint",       // class to delete methods from
-            "android.graphics.Paint$Align", // list of type identifying methods to delete
-            "android.graphics.Paint$Style",
-            "android.graphics.Paint$Join",
-            "android.graphics.Paint$Cap",
-            "android.graphics.Paint$FontMetrics",
-            "android.graphics.Paint$FontMetricsInt",
             null };                         // separator, for next class/methods list.
 }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index 21d6682..c7968a4 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -169,7 +169,7 @@
         // Construct the descriptor of the delegate. For a static method, it's the same
         // however for an instance method we need to pass the 'this' reference first
         String desc = mDesc;
-        if (!mIsStatic && argTypes.length > 0) {
+        if (!mIsStatic) {
             Type[] argTypes2 = new Type[argTypes.length + 1];
 
             argTypes2[0] = Type.getObjectType(mClassName);