auto import from //branches/cupcake/...@137197
diff --git a/api/current.xml b/api/current.xml
index 55c7756..96998ed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27761,6 +27761,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_USER_PRESENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.USER_PRESENT&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_VIEW"
  type="java.lang.String"
  transient="false"
@@ -108917,6 +108928,21 @@
 <parameter name="what" type="java.lang.Object">
 </parameter>
 </method>
+<method name="isSelectingMetaTracker"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="what" type="java.lang.Object">
+</parameter>
+</method>
 <method name="onKeyDown"
  return="boolean"
  abstract="false"
@@ -123194,6 +123220,19 @@
  visibility="public"
 >
 </method>
+<method name="checkInputConnectionProxy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="clearAnimation"
  return="void"
  abstract="false"
@@ -135200,7 +135239,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="5"
+ value="6"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135211,7 +135250,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="1"
+ value="2"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135222,7 +135261,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="4"
+ value="5"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135233,7 +135272,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="0"
+ value="1"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135244,7 +135283,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="2"
+ value="3"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135255,7 +135294,18 @@
  type="int"
  transient="false"
  volatile="false"
- value="3"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IME_ACTION_UNSPECIFIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -135284,7 +135334,7 @@
  visibility="public"
 >
 </field>
-<field name="IME_NORMAL"
+<field name="IME_NULL"
  type="int"
  transient="false"
  volatile="false"
@@ -135295,17 +135345,6 @@
  visibility="public"
 >
 </field>
-<field name="IME_UNDEFINED"
- type="int"
- transient="false"
- volatile="false"
- value="-2147483648"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="actionId"
  type="int"
  transient="false"
@@ -135501,6 +135540,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_SELECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_SINGLE_LINE"
  type="int"
  transient="false"
@@ -136071,6 +136121,271 @@
 >
 </field>
 </interface>
+<class name="InputConnectionWrapper"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.inputmethod.InputConnection">
+</implements>
+<constructor name="InputConnectionWrapper"
+ type="android.view.inputmethod.InputConnectionWrapper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="android.view.inputmethod.InputConnection">
+</parameter>
+</constructor>
+<method name="beginBatchEdit"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="clearMetaKeyStates"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int">
+</parameter>
+</method>
+<method name="commitCompletion"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="android.view.inputmethod.CompletionInfo">
+</parameter>
+</method>
+<method name="commitText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="newCursorPosition" type="int">
+</parameter>
+</method>
+<method name="deleteSurroundingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="leftLength" type="int">
+</parameter>
+<parameter name="rightLength" type="int">
+</parameter>
+</method>
+<method name="endBatchEdit"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="finishComposingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCursorCapsMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reqModes" type="int">
+</parameter>
+</method>
+<method name="getExtractedText"
+ return="android.view.inputmethod.ExtractedText"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.view.inputmethod.ExtractedTextRequest">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="getTextAfterCursor"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="n" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="getTextBeforeCursor"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="n" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="performContextMenuAction"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="performEditorAction"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="editorAction" type="int">
+</parameter>
+</method>
+<method name="performPrivateCommand"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="action" type="java.lang.String">
+</parameter>
+<parameter name="data" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="reportFullscreenMode"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+<method name="sendKeyEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="setComposingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="newCursorPosition" type="int">
+</parameter>
+</method>
+<method name="setSelection"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
+</class>
 <interface name="InputMethod"
  abstract="true"
  static="false"
@@ -155853,7 +156168,7 @@
 <parameter name="depth" type="int">
 </parameter>
 </method>
-<method name="didTouchFocusSelectAll"
+<method name="didTouchFocusSelect"
  return="boolean"
  abstract="false"
  native="false"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e1c1f64..824fd9b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1042,6 +1042,14 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
+
+    /**
+     * Broadcast Action: Sent when the user is present after device wakes up (e.g when the 
+     * keyguard is gone).
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USER_PRESENT= "android.intent.action.USER_PRESENT";
+
     /**
      * Broadcast Action: The current time has changed.  Sent every
      * minute.  You can <em>not</em> receive this through components declared
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ae8b08..2dcb483 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -260,8 +260,9 @@
         boolean assetError = true;
         try {
             assmgr = new AssetManager();
-            if(assmgr.addAssetPath(mArchiveSourcePath) != 0) {
-                parser = assmgr.openXmlResourceParser("AndroidManifest.xml");
+            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
+            if(cookie != 0) {
+                parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
                 assetError = false;
             } else {
                 Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index fadcb35..1c91736 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -567,8 +567,8 @@
 
     /**
      * Add an additional set of assets to the asset manager.  This can be
-     * either a directory or ZIP file.  Not for use by applications.  A
-     * zero return value indicates failure.
+     * either a directory or ZIP file.  Not for use by applications.  Returns
+     * the cookie of the added asset, or 0 on failure.
      * {@hide}
      */
     public native final int addAssetPath(String path);
diff --git a/core/java/android/emoji/EmojiFactory.java b/core/java/android/emoji/EmojiFactory.java
new file mode 100644
index 0000000..389bd07
--- /dev/null
+++ b/core/java/android/emoji/EmojiFactory.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2009 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.emoji;
+
+import android.graphics.Bitmap;
+
+import java.lang.ref.WeakReference;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A class for the factories which produce Emoji (pictgram) images.
+ * This is intended to be used by IME, Email app, etc.
+ * There's no plan to make this public for now.
+ * @hide
+ */
+public final class EmojiFactory {
+    // private static final String LOG_TAG = "EmojiFactory";
+    
+    private int sCacheSize = 100;
+    
+    // HashMap for caching Bitmap object. In order not to make an cache object
+    // blow up, we use LinkedHashMap with size limit.
+    private class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
+        public CustomLinkedHashMap() {
+            // These magic numbers are gotten from the source code of
+            // LinkedHashMap.java and HashMap.java.
+            super(16, 0.75f, true);
+        }
+        
+        /*
+         * If size() becomes more than sCacheSize, least recently used cache
+         * is erased. 
+         * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
+         */
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+            return size() > sCacheSize;
+        }
+    }
+    
+    // A pointer to native EmojiFactory object.
+    private int mNativeEmojiFactory;
+    private String mName;
+    // Cache.
+    private Map<Integer, WeakReference<Bitmap>> mCache;
+    
+    /**
+     * @noinspection UnusedDeclaration
+     */
+    /*
+     * Private constructor that must received an already allocated native
+     * EmojiFactory int (pointer).
+     *
+     * This can be called from JNI code.
+     */
+    private EmojiFactory(int nativeEmojiFactory, String name) {
+        mNativeEmojiFactory = nativeEmojiFactory;
+        mName = name;
+        mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>();
+    }
+    
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nativeDestructor(mNativeEmojiFactory);
+        } finally {
+            super.finalize();
+        }
+    }
+    
+    public String name() {
+        return mName;
+    }
+    
+    /**
+     * Returns Bitmap object corresponding to the AndroidPua.
+     * 
+     * Note that each Bitmap is cached by this class, which means that, if you modify a
+     * Bitmap object (using setPos() method), all same emoji Bitmap will be modified.
+     * If it is unacceptable, please copy the object before modifying it.
+     *  
+     * @param pua A unicode codepoint.
+     * @return Bitmap object when this factory knows the Bitmap relevant to the codepoint.
+     * Otherwise null is returned.  
+     */
+    public synchronized Bitmap getBitmapFromAndroidPua(int pua) {
+        WeakReference<Bitmap> cache = mCache.get(pua);
+        if (cache == null) {
+            Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
+            // There is no need to cache returned null, since in most cases it means there
+            // is no map from the AndroidPua to a specific image. In other words, it usually does
+            // not include the cost of creating Bitmap object.
+            if (ret != null) {
+               mCache.put(pua, new WeakReference<Bitmap>(ret));
+            }
+            return ret;
+        } else {
+            Bitmap tmp = cache.get();
+            if (tmp == null) {
+                Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
+                mCache.put(pua, new WeakReference<Bitmap>(ret));
+                return ret;
+            } else {
+                return tmp;
+            }
+        }
+    }
+
+    /**
+     * Returns Bitmap object corresponding to the vendor specified sjis.
+     * 
+     * See comments in getBitmapFromAndroidPua().
+     * 
+     * @param sjis sjis code specific to each career(vendor)
+     * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
+     * null is returned.
+     */
+    public synchronized Bitmap getBitmapFromVendorSpecificSjis(char sjis) {
+        return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificSjis(sjis));
+    }
+
+    /**
+     * Returns Bitmap object corresponding to the vendor specific Unicode.
+     * 
+     * See comments in getBitmapFromAndroidPua().
+     * 
+     * @param vsp vendor specific PUA.
+     * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
+     * null is returned.
+     */
+    public synchronized Bitmap getBitmapFromVendorSpecificPua(int vsp) {
+        return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificPua(vsp));
+    }
+    
+    /**
+     * Returns Unicode PUA for Android corresponding to the vendor specific sjis.
+     * 
+     * @param sjis vendor specific sjis
+     * @return Unicode PUA for Android, or -1 if there's no map for the sjis.
+     */
+    public int getAndroidPuaFromVendorSpecificSjis(char sjis) {
+        return nativeGetAndroidPuaFromVendorSpecificSjis(mNativeEmojiFactory, sjis);
+    }
+    
+    /**
+     * Returns vendor specific sjis corresponding to the Unicode AndroidPua.
+     * 
+     * @param pua Unicode PUA for Android,
+     * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
+     */
+    public int getVendorSpecificSjisFromAndroidPua(int pua) {
+        return nativeGetVendorSpecificSjisFromAndroidPua(mNativeEmojiFactory, pua);
+    }
+    
+    /**
+     * Returns Unicode PUA for Android corresponding to the vendor specific Unicode.
+     * 
+     * @param vsp vendor specific PUA.
+     * @return Unicode PUA for Android, or -1 if there's no map for the
+     * Unicode.
+     */
+    public int getAndroidPuaFromVendorSpecificPua(int vsp) {
+        return nativeGetAndroidPuaFromVendorSpecificPua(mNativeEmojiFactory, vsp);
+    }
+
+    public String getAndroidPuaFromVendorSpecificPua(String vspString) {
+        if (vspString == null) {
+            return null;
+        }
+        int minVsp = nativeGetMinimumVendorSpecificPua(mNativeEmojiFactory);
+        int maxVsp = nativeGetMaximumVendorSpecificPua(mNativeEmojiFactory);
+        int len = vspString.length();
+        int[] codePoints = new int[vspString.codePointCount(0, len)];
+
+        int new_len = 0;
+        for (int i = 0; i < len; i = vspString.offsetByCodePoints(i, 1), new_len++) {
+            int codePoint = vspString.codePointAt(i);
+            if (minVsp <= codePoint && codePoint <= maxVsp) {
+                int newCodePoint = getAndroidPuaFromVendorSpecificPua(codePoint);
+                if (newCodePoint > 0) {
+                    codePoints[new_len] = newCodePoint;
+                    continue;
+                }
+            }
+            codePoints[new_len] = codePoint;
+        }
+        return new String(codePoints, 0, new_len);
+    }
+    
+    /**
+     * Returns vendor specific Unicode corresponding to the Unicode AndroidPua.
+     * 
+     * @param pua Unicode PUA for Android,
+     * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
+     */
+    public int getVendorSpecificPuaFromAndroidPua(int pua) {
+        return nativeGetVendorSpecificPuaFromAndroidPua(mNativeEmojiFactory, pua);
+    }
+
+    public String getVendorSpecificPuaFromAndroidPua(String puaString) {
+        if (puaString == null) {
+            return null;
+        }
+        int minVsp = nativeGetMinimumAndroidPua(mNativeEmojiFactory);
+        int maxVsp = nativeGetMaximumAndroidPua(mNativeEmojiFactory);
+        int len = puaString.length();
+        int[] codePoints = new int[puaString.codePointCount(0, len)];
+
+        int new_len = 0;
+        for (int i = 0; i < len; i = puaString.offsetByCodePoints(i, 1), new_len++) {
+            int codePoint = puaString.codePointAt(i);
+            if (minVsp <= codePoint && codePoint <= maxVsp) {
+                int newCodePoint = getVendorSpecificPuaFromAndroidPua(codePoint);
+                if (newCodePoint > 0) {
+                    codePoints[new_len] = newCodePoint;
+                    continue;
+                }
+            }
+            codePoints[new_len] = codePoint;
+        }
+        return new String(codePoints, 0, new_len);
+    }
+
+    /**
+     * Constructs an instance of EmojiFactory corresponding to the name.
+     *  
+     * @param class_name Name of the factory. This must include complete package name.
+     * @return A concrete EmojiFactory instance corresponding to factory_name.
+     * If factory_name is invalid, null is returned. 
+     */
+    public static native EmojiFactory newInstance(String class_name);
+    
+    /**
+     * Constructs an instance of available EmojiFactory.
+     * 
+     * @return A concrete EmojiFactory instance. If there are several available
+     * EmojiFactory class, preferred one is chosen by the system. If there isn't, null
+     * is returned. 
+     */
+    public static native EmojiFactory newAvailableInstance();
+    
+    // native methods
+    
+    private native void nativeDestructor(int factory);
+    private native Bitmap nativeGetBitmapFromAndroidPua(int nativeEmojiFactory, int AndroidPua);
+    private native int nativeGetAndroidPuaFromVendorSpecificSjis(int nativeEmojiFactory,
+            char sjis);
+    private native int nativeGetVendorSpecificSjisFromAndroidPua(int nativeEmojiFactory,
+            int pua);
+    private native int nativeGetAndroidPuaFromVendorSpecificPua(int nativeEmojiFactory,
+            int vsp);
+    private native int nativeGetVendorSpecificPuaFromAndroidPua(int nativeEmojiFactory,
+            int pua);
+    private native int nativeGetMaximumVendorSpecificPua(int nativeEmojiFactory);
+    private native int nativeGetMinimumVendorSpecificPua(int nativeEmojiFactory);
+    private native int nativeGetMaximumAndroidPua(int nativeEmojiFactory);
+    private native int nativeGetMinimumAndroidPua(int nativeEmojiFactory);
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f1e613e..34d5695 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1377,9 +1377,11 @@
         if (mExtractedToken != token) {
             return;
         }
-        if (mExtractEditText != null && text != null) {
-            mExtractedText = text;
-            mExtractEditText.setExtractedText(text);
+        if (text != null) {
+            if (mExtractEditText != null) {
+                mExtractedText = text;
+                mExtractEditText.setExtractedText(text);
+            }
         }
     }
     
diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java
index 1454089..d11a9c5 100644
--- a/core/java/android/provider/Checkin.java
+++ b/core/java/android/provider/Checkin.java
@@ -132,8 +132,6 @@
             BROWSER_SNAP_CENTER,
             BROWSER_TEXT_SIZE_CHANGE,
             BROWSER_ZOOM_OVERVIEW,
-            BROWSER_ZOOM_RING,
-            BROWSER_ZOOM_RING_DRAG,
             CRASHES_REPORTED,
             CRASHES_TRUNCATED,
             ELAPSED_REALTIME_SEC,
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
index 87153cf..6f5513a 100644
--- a/core/java/android/server/BluetoothDeviceService.java
+++ b/core/java/android/server/BluetoothDeviceService.java
@@ -237,7 +237,28 @@
         public void run() {
             boolean res = (enableNative() == 0);
             if (res) {
-                mEventLoop.start();
+                int retryCount = 2;
+                boolean running = false;
+                while ((retryCount-- > 0) && !running) {
+                    mEventLoop.start();
+                    // it may take a momement for the other thread to do its 
+                    // thing.  Check periodically for a while.
+                    int pollCount = 5;
+                    while ((pollCount-- > 0) && !running) {
+                        if (mEventLoop.isEventLoopRunning()) {
+                            running = true;
+                            break;
+                        }
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {}
+                    }
+                }
+                if (!running) {
+                    log("bt EnableThread giving up");
+                    res = false;
+                    disableNative();
+                }
             }
 
             if (mEnableCallback != null) {
@@ -254,14 +275,20 @@
                     persistBluetoothOnSetting(true);
                 }
                 mIsDiscovering = false;
-                Intent intent = new Intent(BluetoothIntent.ENABLED_ACTION);
                 mBondState.loadBondState();
-                mContext.sendBroadcast(intent, BLUETOOTH_PERM);
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(REGISTER_SDP_RECORDS), 3000);
 
                 // Update mode
                 mEventLoop.onModeChanged(getModeNative());
             }
+            Intent intent = null;
+            if (res) {
+                intent = new Intent(BluetoothIntent.ENABLED_ACTION);
+            } else {
+                intent = new Intent(BluetoothIntent.DISABLED_ACTION);
+            }
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
             mEnableThread = null;
         }
     }
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 8b09583..e4ebcca 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -44,6 +44,7 @@
 
     private int mNativeData;
     private Thread mThread;
+    private boolean mStarted;
     private boolean mInterrupted;
     private HashMap<String, Integer> mPasskeyAgentRequestData;
     private HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
@@ -122,14 +123,18 @@
                 public void run() {
                     try {
                         if (setUpEventLoopNative()) {
+                            mStarted = true;
                             while (!mInterrupted) {
                                 waitForAndDispatchEvent(0);
                                 sleep(500);
                             }
-                            tearDownEventLoopNative();
                         }
+                        // tear down even in the error case to clean
+                        // up anything we started to setup
+                        tearDownEventLoopNative();
                     } catch (InterruptedException e) { }
                     if (DBG) log("Event Loop thread finished");
+                    mThread = null;
                 }
             };
         if (DBG) log("Starting Event Loop thread");
@@ -152,7 +157,7 @@
     }
 
     public synchronized boolean isEventLoopRunning() {
-        return mThread != null;
+        return mThread != null && mStarted;
     }
 
     /*package*/ void onModeChanged(String bluezMode) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 8aa49af..17c7a6c 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -204,7 +204,7 @@
                                 MotionEvent event) {
         boolean handled = Touch.onTouchEvent(widget, buffer, event);
 
-        if (widget.isFocused() && !widget.didTouchFocusSelectAll()) {
+        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
             if (event.getAction() == MotionEvent.ACTION_UP) {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 39ad976..61ec67f 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -159,13 +159,21 @@
 
     /**
      * Returns true if this object is one that this class would use to
-     * keep track of meta state in the specified text.
+     * keep track of any meta state in the specified text.
      */
     public static boolean isMetaTracker(CharSequence text, Object what) {
         return what == CAP || what == ALT || what == SYM ||
                what == SELECTING;
     }
 
+    /**
+     * Returns true if this object is one that this class would use to
+     * keep track of the selecting meta state in the specified text.
+     */
+    public static boolean isSelectingMetaTracker(CharSequence text, Object what) {
+        return what == SELECTING;
+    }
+
     private static void adjust(Spannable content, Object what) {
         int current = content.getSpanFlags(what);
 
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 88ff3c5..f7ac522 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -498,7 +498,6 @@
             if (mIsDoubleTapping) {
                 // Finally, give the up event of the double-tap
                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
-                mIsDoubleTapping = false;
             } else if (mInLongPress) {
                 mHandler.removeMessages(TAP);
                 mInLongPress = false;
@@ -520,6 +519,7 @@
             mPreviousUpEvent = MotionEvent.obtain(ev);
             mVelocityTracker.recycle();
             mVelocityTracker = null;
+            mIsDoubleTapping = false;
             mHandler.removeMessages(SHOW_PRESS);
             mHandler.removeMessages(LONG_PRESS);
             break;
@@ -529,6 +529,7 @@
             mHandler.removeMessages(TAP);
             mVelocityTracker.recycle();
             mVelocityTracker = null;
+            mIsDoubleTapping = false;
             mStillDown = false;
             if (mInLongPress) {
                 mInLongPress = false;
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index cc3563c..841066c 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -26,9 +26,6 @@
 
     public static final int LONG_PRESS = 0;
     
-    /** @hide pending API council */
-    public static final int ZOOM_RING_TICK = 1;
-    
     /**
      * Flag for {@link View#performHapticFeedback(int, int)
      * View.performHapticFeedback(int, int)}: Ignore the setting in the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3e762f5..d78320a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3663,8 +3663,15 @@
      * would make sense to automatically display a soft input window for
      * it.  Subclasses should override this if they implement
      * {@link #onCreateInputConnection(EditorInfo)} to return true if
-     * a call on that method would return a non-null InputConnection.  The
-     * default implementation always returns false.
+     * a call on that method would return a non-null InputConnection, and
+     * they are really a first-class editor that the user would normally
+     * start typing on when the go into a window containing your view.
+     * 
+     * <p>The default implementation always returns false.  This does
+     * <em>not</em> mean that its {@link #onCreateInputConnection(EditorInfo)}
+     * will not be called or the user can not otherwise perform edits on your
+     * view; it is just a hint to the system that this is not the primary
+     * purpose of this view.
      * 
      * @return Returns true if this view is a text editor, else false.
      */
@@ -3689,6 +3696,20 @@
     }
 
     /**
+     * Called by the {@link android.view.inputmethod.InputMethodManager}
+     * when a view who is not the current
+     * input connection target is trying to make a call on the manager.  The
+     * default implementation returns false; you can override this to return
+     * true for certain views if you are performing InputConnection proxying
+     * to them.
+     * @param view The View that is making the InputMethodManager call.
+     * @return Return true to allow the call, false to reject.
+     */
+    public boolean checkInputConnectionProxy(View view) {
+        return false;
+    }
+    
+    /**
      * Show the context menu for this view. It is not safe to hold on to the
      * menu after returning from this method.
      *
@@ -5016,7 +5037,7 @@
             (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ?
                     getVerticalScrollbarWidth() : 0;
 
-        scrollBar.setBounds(scrollX + (mPaddingLeft & inside) + getScrollBarPaddingLeft(), top,
+        scrollBar.setBounds(scrollX + (mPaddingLeft & inside), top,
                 scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size);
         scrollBar.setParameters(
                 computeHorizontalScrollRange(),
@@ -6503,32 +6524,6 @@
         return mBGDrawable;
     }
 
-    private int getScrollBarPaddingLeft() {
-        // TODO: Deal with RTL languages
-        return 0;
-    }
-
-    /*
-     * Returns the pixels occupied by the vertical scrollbar, if not overlaid
-     */
-    private int getScrollBarPaddingRight() {
-        // TODO: Deal with RTL languages
-        if ((mViewFlags & SCROLLBARS_VERTICAL) == 0) {
-            return 0;
-        }
-        return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getVerticalScrollbarWidth();
-    }
-
-    /*
-     * Returns the pixels occupied by the horizontal scrollbar, if not overlaid
-     */
-    private int getScrollBarPaddingBottom() {
-        if ((mViewFlags & SCROLLBARS_HORIZONTAL) == 0) {
-            return 0;
-        }
-        return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getHorizontalScrollbarHeight();
-    }
-
     /**
      * Sets the padding. The view may add on the space required to display
      * the scrollbars, depending on the style and visibility of the scrollbars.
@@ -6552,7 +6547,22 @@
         mUserPaddingRight = right;
         mUserPaddingBottom = bottom;
 
-        if (mPaddingLeft != left + getScrollBarPaddingLeft()) {
+        final int viewFlags = mViewFlags;
+        
+        // Common case is there are no scroll bars.
+        if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
+            // TODO: Deal with RTL languages to adjust left padding instead of right.
+            if ((viewFlags & SCROLLBARS_VERTICAL) != 0) {
+                right += (viewFlags & SCROLLBARS_INSET_MASK) == 0
+                        ? 0 : getVerticalScrollbarWidth();
+            }
+            if ((viewFlags & SCROLLBARS_HORIZONTAL) == 0) {
+                bottom += (viewFlags & SCROLLBARS_INSET_MASK) == 0
+                        ? 0 : getHorizontalScrollbarHeight();
+            }
+        }
+        
+        if (mPaddingLeft != left) {
             changed = true;
             mPaddingLeft = left;
         }
@@ -6560,13 +6570,13 @@
             changed = true;
             mPaddingTop = top;
         }
-        if (mPaddingRight != right + getScrollBarPaddingRight()) {
+        if (mPaddingRight != right) {
             changed = true;
-            mPaddingRight = right + getScrollBarPaddingRight();
+            mPaddingRight = right;
         }
-        if (mPaddingBottom != bottom + getScrollBarPaddingBottom()) {
+        if (mPaddingBottom != bottom) {
             changed = true;
-            mPaddingBottom = bottom + getScrollBarPaddingBottom();
+            mPaddingBottom = bottom;
         }
 
         if (changed) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 52b4107..deca910 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -386,7 +386,14 @@
             // anyway.
             return true;
         }
-        Selection.setSelection(content, start, end);
+        if (start == end && MetaKeyKeyListener.getMetaState(content,
+                MetaKeyKeyListener.META_SELECTING) != 0) {
+            // If we are in selection mode, then we want to extend the
+            // selection instead of replacing it.
+            Selection.extendSelection(content, start);
+        } else {
+            Selection.setSelection(content, start, end);
+        }
         return true;
     }
 
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 1c0d42a..b00e565 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -33,43 +33,49 @@
     public static final int IME_MASK_ACTION = 0x000000ff;
     
     /**
-     * Bits of {@link #IME_MASK_ACTION}: there is no special action
-     * associated with this editor.
+     * Bits of {@link #IME_MASK_ACTION}: no specific action has been
+     * associated with this editor, let the editor come up with its own if
+     * it can.
      */
-    public static final int IME_ACTION_NONE = 0x00000000;
+    public static final int IME_ACTION_UNSPECIFIED = 0x00000000;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: there is no available action.
+     */
+    public static final int IME_ACTION_NONE = 0x00000001;
     
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go"
      * operation to take the user to the target of the text they typed.
      * Typically used, for example, when entering a URL.
      */
-    public static final int IME_ACTION_GO = 0x00000001;
+    public static final int IME_ACTION_GO = 0x00000002;
     
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search"
      * operation, taking the user to the results of searching for the text
      * the have typed (in whatever context is appropriate).
      */
-    public static final int IME_ACTION_SEARCH = 0x00000002;
+    public static final int IME_ACTION_SEARCH = 0x00000003;
     
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send"
      * operation, delivering the text to its target.  This is typically used
      * when composing a message.
      */
-    public static final int IME_ACTION_SEND = 0x00000003;
+    public static final int IME_ACTION_SEND = 0x00000004;
     
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next"
      * operation, taking the user to the next field that will accept text.
      */
-    public static final int IME_ACTION_NEXT = 0x00000004;
+    public static final int IME_ACTION_NEXT = 0x00000005;
     
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "done"
      * operation, typically meaning the IME will be closed.
      */
-    public static final int IME_ACTION_DONE = 0x00000005;
+    public static final int IME_ACTION_DONE = 0x00000006;
     
     /**
      * Flag of {@link #imeOptions}: used in conjunction with
@@ -82,21 +88,15 @@
     public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000;
     
     /**
-     * Generic non-special type for {@link #imeOptions}.
+     * Generic unspecified type for {@link #imeOptions}.
      */
-    public static final int IME_NORMAL = 0x00000000;
-    
-    /**
-     * Special code for when the ime option has been undefined.  This is not
-     * used with the EditorInfo structure, but can be used elsewhere.
-     */
-    public static final int IME_UNDEFINED = 0x80000000;
+    public static final int IME_NULL = 0x00000000;
     
     /**
      * Extended type information for the editor, to help the IME better
      * integrate with it.
      */
-    public int imeOptions = IME_NORMAL;
+    public int imeOptions = IME_NULL;
     
     /**
      * A string supplying additional information options that are
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index e5d3cae..c2851d6 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -55,6 +55,11 @@
     public static final int FLAG_SINGLE_LINE = 0x0001;
     
     /**
+     * Bit for {@link #flags}: set if the editor is currently in selection mode.
+     */
+    public static final int FLAG_SELECTING = 0x0002;
+    
+    /**
      * Additional bit flags of information about the edited text.
      */
     public int flags;
@@ -72,7 +77,7 @@
         dest.writeInt(partialEndOffset);
         dest.writeInt(selectionStart);
         dest.writeInt(selectionEnd);
-        dest.writeInt(flags);
+        dest.writeInt(this.flags);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 32cce35..8b6831e 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -17,7 +17,6 @@
 package android.view.inputmethod;
 
 import android.os.Bundle;
-import android.text.Spanned;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
@@ -135,7 +134,7 @@
      * @return Returns true on success, false if the input connection is no longer
      * valid.
      */
-    boolean deleteSurroundingText(int leftLength, int rightLength);
+    public boolean deleteSurroundingText(int leftLength, int rightLength);
 
     /**
      * Set composing text around the current cursor position with the given text,
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
new file mode 100644
index 0000000..e3d5e62
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007-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.view.inputmethod;
+
+import android.os.Bundle;
+import android.view.KeyEvent;
+
+/**
+ * <p>Wrapper class for proxying calls to another InputConnection.  Subclass
+ * and have fun!
+ */
+public class InputConnectionWrapper implements InputConnection {
+    private final InputConnection mTarget;
+    
+    public InputConnectionWrapper(InputConnection target) {
+        mTarget = target;
+    }
+    
+    public CharSequence getTextBeforeCursor(int n, int flags) {
+        return mTarget.getTextBeforeCursor(n, flags);
+    }
+    
+    public CharSequence getTextAfterCursor(int n, int flags) {
+        return mTarget.getTextAfterCursor(n, flags);
+    }
+
+    public int getCursorCapsMode(int reqModes) {
+        return mTarget.getCursorCapsMode(reqModes);
+    }
+    
+    public ExtractedText getExtractedText(ExtractedTextRequest request,
+            int flags) {
+        return mTarget.getExtractedText(request, flags);
+    }
+
+    public boolean deleteSurroundingText(int leftLength, int rightLength) {
+        return mTarget.deleteSurroundingText(leftLength, rightLength);
+    }
+
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        return mTarget.setComposingText(text, newCursorPosition);
+    }
+
+    public boolean finishComposingText() {
+        return mTarget.finishComposingText();
+    }
+    
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        return mTarget.commitText(text, newCursorPosition);
+    }
+
+    public boolean commitCompletion(CompletionInfo text) {
+        return mTarget.commitCompletion(text);
+    }
+
+    public boolean setSelection(int start, int end) {
+        return mTarget.setSelection(start, end);
+    }
+    
+    public boolean performEditorAction(int editorAction) {
+        return mTarget.performEditorAction(editorAction);
+    }
+    
+    public boolean performContextMenuAction(int id) {
+        return mTarget.performContextMenuAction(id);
+    }
+    
+    public boolean beginBatchEdit() {
+        return mTarget.beginBatchEdit();
+    }
+    
+    public boolean endBatchEdit() {
+        return mTarget.endBatchEdit();
+    }
+    
+    public boolean sendKeyEvent(KeyEvent event) {
+        return mTarget.sendKeyEvent(event);
+    }
+
+    public boolean clearMetaKeyStates(int states) {
+        return mTarget.clearMetaKeyStates(states);
+    }
+    
+    public boolean reportFullscreenMode(boolean enabled) {
+        return mTarget.reportFullscreenMode(enabled);
+    }
+    
+    public boolean performPrivateCommand(String action, Bundle data) {
+        return mTarget.performPrivateCommand(action, data);
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 916ffea..7f2142e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -529,7 +529,10 @@
     public boolean isActive(View view) {
         checkFocus();
         synchronized (mH) {
-            return mServedView == view && mCurrentTextBoxAttribute != null;
+            return (mServedView == view
+                    || (mServedView != null
+                            && mServedView.checkInputConnectionProxy(view)))
+                    && mCurrentTextBoxAttribute != null;
         }
     }
     
@@ -620,7 +623,8 @@
     public void displayCompletions(View view, CompletionInfo[] completions) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view) {
+            if (mServedView != view && (mServedView == null
+                            || !mServedView.checkInputConnectionProxy(view))) {
                 return;
             }
             
@@ -637,7 +641,8 @@
     public void updateExtractedText(View view, int token, ExtractedText text) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view) {
+            if (mServedView != view && (mServedView == null
+                    || !mServedView.checkInputConnectionProxy(view))) {
                 return;
             }
             
@@ -730,7 +735,8 @@
             ResultReceiver resultReceiver) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view) {
+            if (mServedView != view && (mServedView == null
+                    || !mServedView.checkInputConnectionProxy(view))) {
                 return false;
             }
 
@@ -871,7 +877,8 @@
     public void restartInput(View view) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view) {
+            if (mServedView != view && (mServedView == null
+                    || !mServedView.checkInputConnectionProxy(view))) {
                 return;
             }
             
@@ -1032,7 +1039,7 @@
             if (DEBUG) Log.v(TAG, "focusOut: " + view
                     + " mServedView=" + mServedView
                     + " winFocus=" + view.hasWindowFocus());
-            if (mServedView == view) {
+            if (mServedView != view) {
                 // The following code would auto-hide the IME if we end up
                 // with no more views with focus.  This can happen, however,
                 // whenever we go into touch mode, so it ends up hiding
@@ -1129,8 +1136,9 @@
             try {
                 final boolean isTextEditor = focusedView != null &&
                         focusedView.onCheckIsTextEditor();
-                mService.windowGainedFocus(mClient, focusedView != null,
-                        isTextEditor, softInputMode, first, windowFlags);
+                mService.windowGainedFocus(mClient, rootView.getWindowToken(),
+                        focusedView != null, isTextEditor, softInputMode, first,
+                        windowFlags);
             } catch (RemoteException e) {
             }
         }
@@ -1150,8 +1158,9 @@
             int candidatesStart, int candidatesEnd) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view || mCurrentTextBoxAttribute == null
-                    || mCurMethod == null) {
+            if ((mServedView != view && (mServedView == null
+                        || !mServedView.checkInputConnectionProxy(view)))
+                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
             
@@ -1189,8 +1198,9 @@
     public void updateCursor(View view, int left, int top, int right, int bottom) {
         checkFocus();
         synchronized (mH) {
-            if (mServedView != view || mCurrentTextBoxAttribute == null
-                    || mCurMethod == null) {
+            if ((mServedView != view && (mServedView == null
+                        || !mServedView.checkInputConnectionProxy(view)))
+                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
             
@@ -1223,7 +1233,8 @@
     public void sendAppPrivateCommand(View view, String action, Bundle data) {
         checkFocus();
         synchronized (mH) {
-            if ((view != null && mServedView != view)
+            if ((mServedView != view && (mServedView == null
+                        || !mServedView.checkInputConnectionProxy(view)))
                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java
index 39806dc..efc131f 100644
--- a/core/java/android/webkit/TextDialog.java
+++ b/core/java/android/webkit/TextDialog.java
@@ -32,8 +32,6 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.method.MovementMethod;
-import android.text.method.PasswordTransformationMethod;
-import android.text.method.TextKeyListener;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.KeyCharacterMap;
@@ -475,15 +473,10 @@
      * @param   inPassword  True if the textfield is a password field.
      */
     /* package */ void setInPassword(boolean inPassword) {
-        PasswordTransformationMethod method;
         if (inPassword) {
-            method = PasswordTransformationMethod.getInstance();
-            setInputType(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.
+            setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
                     TYPE_TEXT_VARIATION_PASSWORD);
-        } else {
-            method = null;
         }
-        setTransformationMethod(method);
     }
 
     /* package */ void setMaxLength(int maxLength) {
@@ -545,20 +538,13 @@
      * removing the password input type.
      */
     public void setSingleLine(boolean single) {
-        if (mSingle != single) {
-            TextKeyListener.Capitalize cap;
-            int inputType = EditorInfo.TYPE_CLASS_TEXT;
-            if (single) {
-                cap = TextKeyListener.Capitalize.NONE;
-            } else {
-                cap = TextKeyListener.Capitalize.SENTENCES;
-                inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
-            }
-            setKeyListener(TextKeyListener.getInstance(!single, cap));
-            mSingle = single;
-            setHorizontallyScrolling(single);
-            setInputType(inputType);
+        int inputType = EditorInfo.TYPE_CLASS_TEXT;
+        if (!single) {
+            inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
         }
+        mSingle = single;
+        setHorizontallyScrolling(single);
+        setInputType(inputType);
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 91795a3..dc39b90 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -73,7 +73,6 @@
 import android.widget.Toast;
 import android.widget.ZoomButtonsController;
 import android.widget.ZoomControls;
-import android.widget.ZoomRingController;
 import android.widget.FrameLayout;
 import android.widget.AdapterView.OnItemClickListener;
 
@@ -239,8 +238,8 @@
      */
     VelocityTracker mVelocityTracker;
 
-    private static boolean mShowZoomRingTutorial = true;
-    private static final int ZOOM_RING_TUTORIAL_DURATION = 3000;
+    private static boolean mShowZoomTutorial = true;
+    private static final int ZOOM_TUTORIAL_DURATION = 3000;
 
     /**
      * Touch mode
@@ -265,11 +264,6 @@
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
 
-    // Whether we are in the drag tap mode, which exists starting at the second
-    // tap's down, through its move, and includes its up. These events should be
-    // given to the method on the zoom controller.
-    private boolean mInZoomTapDragMode = false;
-
     // Whether to prevent drag during touch. The initial value depends on
     // mForwardTouchEvents. If WebCore wants touch events, we assume it will
     // take control of touch events unless it says no for touch down event.
@@ -361,7 +355,7 @@
     private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
     private static final int SWITCH_TO_ENTER = 7;
     private static final int RESUME_WEBCORE_UPDATE = 8;
-    private static final int DISMISS_ZOOM_RING_TUTORIAL = 9;
+    private static final int DISMISS_ZOOM_TUTORIAL = 9;
 
     //! arg1=x, arg2=y
     static final int SCROLL_TO_MSG_ID               = 10;
@@ -417,11 +411,6 @@
     // width which view is considered to be fully zoomed out
     static final int ZOOM_OUT_WIDTH = 1024;
 
-    private static final float MAX_ZOOM_RING_ANGLE = (float) (Math.PI * 2 / 3);
-    private static final int ZOOM_RING_STEPS = 4;
-    private static final float ZOOM_RING_ANGLE_UNIT = MAX_ZOOM_RING_ANGLE
-            / ZOOM_RING_STEPS;
-
     private static final float DEFAULT_MAX_ZOOM_SCALE = 2;
     private static final float DEFAULT_MIN_ZOOM_SCALE = (float) 1/3;
     // scale limit, which can be set through viewport meta tag in the web page
@@ -560,146 +549,52 @@
     }
 
     private ZoomButtonsController mZoomButtonsController; 
+    private ImageView mZoomOverviewButton;
 
-    private ZoomRingController mZoomRingController;
-    private ImageView mZoomRingOverview;
-    private Animation mZoomRingOverviewExitAnimation;
-
-    // These keep track of the center point of the zoom ring.  They are used to
+    // These keep track of the center point of the zoom.  They are used to
     // determine the point around which we should zoom.
     private float mZoomCenterX;
     private float mZoomCenterY;
 
-    private ZoomRingController.OnZoomListener mZoomListener =
-            new ZoomRingController.OnZoomListener() {
-
-        private float mClockwiseBound;
-        private float mCounterClockwiseBound;
-        private float mStartScale;
+    private ZoomButtonsController.OnZoomListener mZoomListener =
+            new ZoomButtonsController.OnZoomListener() {
 
         public void onCenter(int x, int y) {
             // Don't translate when the control is invoked, hence we do nothing
             // in this callback
         }
 
-        public void onBeginPan() {
-            setZoomOverviewVisible(false);
+        public void onOverview() {
+            mZoomButtonsController.setVisible(false);
+            zoomScrollOut();
             if (mLogEvent) {
                 Checkin.updateStats(mContext.getContentResolver(),
-                        Checkin.Stats.Tag.BROWSER_ZOOM_RING_DRAG, 1, 0.0);
+                        Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
             }
         }
 
-        public boolean onPan(int deltaX, int deltaY) {
-            return pinScrollBy(deltaX, deltaY, false, 0);
-        }
-
-        public void onEndPan() {
-        }
-
         public void onVisibilityChanged(boolean visible) {
             if (visible) {
                 switchOutDrawHistory();
-                if (mMaxZoomScale - 1 > ZOOM_RING_STEPS * 0.01f) {
-                    mClockwiseBound = (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE);
-                } else {
-                    mClockwiseBound = (float) (2 * Math.PI);
-                }
-                mZoomRingController.setThumbClockwiseBound(mClockwiseBound);
-                if (1 - mMinZoomScale > ZOOM_RING_STEPS * 0.01f) {
-                    mCounterClockwiseBound = MAX_ZOOM_RING_ANGLE;
-                } else {
-                    mCounterClockwiseBound = 0;
-                }
-                mZoomRingController
-                        .setThumbCounterclockwiseBound(mCounterClockwiseBound);
-                float angle = 0f;
-                if (mActualScale > 1 && mClockwiseBound < (float) (2 * Math.PI)) {
-                    angle = -(float) Math.round(ZOOM_RING_STEPS
-                            * (mActualScale - 1) / (mMaxZoomScale - 1))
-                            / ZOOM_RING_STEPS;
-                } else if (mActualScale < 1 && mCounterClockwiseBound > 0) {
-                    angle = (float) Math.round(ZOOM_RING_STEPS
-                            * (1 - mActualScale) / (1 - mMinZoomScale))
-                            / ZOOM_RING_STEPS;
-                }
-                mZoomRingController.setThumbAngle(angle * MAX_ZOOM_RING_ANGLE);
-
-                // Don't show a thumb if the user cannot zoom
-                mZoomRingController.setThumbVisible(mMinZoomScale != mMaxZoomScale);
-                
-                // Show the zoom overview tab on the ring
-                setZoomOverviewVisible(true);
-                if (mLogEvent) {
-                    Checkin.updateStats(mContext.getContentResolver(),
-                            Checkin.Stats.Tag.BROWSER_ZOOM_RING, 1, 0.0);
-                }
+                mZoomButtonsController.setOverviewVisible(true);
+                updateButtonsEnabled();
             }
         }
 
-        public void onBeginDrag() {
-            mPreviewZoomOnly = true;
-            mStartScale = mActualScale;
-            setZoomOverviewVisible(false);
-        }
-        
-        public void onEndDrag() {
-            mPreviewZoomOnly = false;
-            if (mLogEvent) {
-                EventLog.writeEvent(EVENT_LOG_ZOOM_LEVEL_CHANGE,
-                        (int) mStartScale * 100, (int) mActualScale * 100,
-                        System.currentTimeMillis());
-            }
-            setNewZoomScale(mActualScale, true);
+        private void updateButtonsEnabled() {
+            mZoomButtonsController.setZoomInEnabled(mActualScale < mMaxZoomScale);
+            mZoomButtonsController.setZoomOutEnabled(mActualScale > mMinZoomScale);
         }
 
-        public boolean onDragZoom(int deltaZoomLevel, int centerX,
-                int centerY, float startAngle, float curAngle) {
-            if (deltaZoomLevel < 0
-                    && Math.abs(mActualScale - mMinZoomScale) < 0.01f
-                    || deltaZoomLevel > 0
-                    &&  Math.abs(mActualScale - mMaxZoomScale) < 0.01f
-                    || deltaZoomLevel == 0) {
-                return false;
-            }
-            mZoomCenterX = (float) centerX;
-            mZoomCenterY = (float) centerY;
-
-            float scale = 1.0f;
-            // curAngle is [0, 2 * Math.PI)
-            if (curAngle < (float) Math.PI) {
-                if (curAngle >= mCounterClockwiseBound) {
-                    scale = mMinZoomScale;
-                } else {
-                    scale = 1 - (float) Math.round(curAngle
-                            / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
-                            * (1 - mMinZoomScale);
-                }
-            } else {
-                if (curAngle <= mClockwiseBound) {
-                    scale = mMaxZoomScale;
-                } else {
-                    scale = 1 + (float) Math.round(
-                            ((float) 2 * Math.PI - curAngle)
-                            / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
-                            * (mMaxZoomScale - 1);
-                }
-            }
-            zoomWithPreview(scale);
-            return true;
-        }
-
-        public void onSimpleZoom(boolean zoomIn, int centerX, int centerY) {
-            mZoomCenterX = (float) centerX;
-            mZoomCenterY = (float) centerY;
-            
+        public void onZoom(boolean zoomIn) {
             if (zoomIn) {
                 zoomIn();
             } else {
                 zoomOut();
             }
+            
+            updateButtonsEnabled();
         }
-        
     };
     
     /**
@@ -738,34 +633,8 @@
         mFocusData.mX = 0;
         mFocusData.mY = 0;
         mScroller = new Scroller(context);
-        mZoomRingController = new ZoomRingController(context, this);
-        mZoomRingController.setCallback(mZoomListener);
-        mZoomRingController.setTrackDrawable(
-                com.android.internal.R.drawable.zoom_ring_track_absolute);
-        float density = context.getResources().getDisplayMetrics().density;
-        mZoomRingController.setPannerAcceleration((int) (160 * density));
-        mZoomRingController.setPannerStartAcceleratingDuration((int) (700 * density));
-        createZoomRingOverviewTab();
         mZoomButtonsController = new ZoomButtonsController(context, this);
-        mZoomButtonsController.setOverviewVisible(true);
-        mZoomButtonsController.setCallback(new ZoomButtonsController.OnZoomListener() {
-            public void onCenter(int x, int y) {
-                mZoomListener.onCenter(x, y);
-            }
-
-            public void onOverview() {
-                mZoomButtonsController.setVisible(false);
-                zoomScrollOut();
-            }
-
-            public void onVisibilityChanged(boolean visible) {
-                mZoomListener.onVisibilityChanged(visible);
-            }
-
-            public void onZoom(boolean zoomIn) {
-                mZoomListener.onSimpleZoom(zoomIn, getWidth() / 2, getHeight() / 2);
-            }
-        });
+        mZoomButtonsController.setCallback(mZoomListener);
     }
 
     private void init() {
@@ -783,67 +652,6 @@
         mDoubleTapSlopSquare = doubleTapslop * doubleTapslop;
     }
 
-    private void createZoomRingOverviewTab() {
-        Context context = getContext();
-        
-        mZoomRingOverviewExitAnimation = AnimationUtils.loadAnimation(context,
-                com.android.internal.R.anim.fade_out);
-        
-        mZoomRingOverview = new ImageView(context);
-        mZoomRingOverview.setBackgroundResource(
-                com.android.internal.R.drawable.zoom_ring_overview_tab);
-        mZoomRingOverview.setImageResource(com.android.internal.R.drawable.btn_zoom_page);
-        
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                Gravity.CENTER);
-        // TODO: magic constant that's based on the zoom ring radius + some offset
-        lp.topMargin = 200;
-        mZoomRingOverview.setLayoutParams(lp);
-        mZoomRingOverview.setOnClickListener(new View.OnClickListener() {
-            public void onClick(View v) {
-                // Hide the zoom ring
-                mZoomRingController.setVisible(false);
-                if (mLogEvent) {
-                    Checkin.updateStats(mContext.getContentResolver(),
-                            Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
-                }
-                zoomScrollOut();
-            }});
-        
-        // Measure the overview View to figure out its height
-        mZoomRingOverview.forceLayout();
-        mZoomRingOverview.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-        
-        ViewGroup container = mZoomRingController.getContainer();
-        // Find the index of the zoom ring in the container
-        View zoomRing = container.findViewById(mZoomRingController.getZoomRingId());
-        int zoomRingIndex;
-        for (zoomRingIndex = container.getChildCount() - 1; zoomRingIndex >= 0; zoomRingIndex--) {
-            if (container.getChildAt(zoomRingIndex) == zoomRing) break;
-        }
-        // Add the overview tab below the zoom ring (so we don't steal its events)
-        container.addView(mZoomRingOverview, zoomRingIndex);
-        // Since we use margins to adjust the vertical placement of the tab, the widget
-        // ends up getting clipped off. Ensure the container is big enough for
-        // us.
-        int myHeight = mZoomRingOverview.getMeasuredHeight() + lp.topMargin / 2;
-        // Multiplied by 2 b/c the zoom ring needs to be centered on the screen
-        container.setMinimumHeight(myHeight * 2);
-    }
-    
-    private void setZoomOverviewVisible(boolean visible) {
-        int newVisibility = visible ? View.VISIBLE : View.INVISIBLE;
-        if (mZoomRingOverview.getVisibility() == newVisibility) return;
-            
-        if (!visible) {
-            mZoomRingOverview.startAnimation(mZoomRingOverviewExitAnimation);
-        }
-        mZoomRingOverview.setVisibility(newVisibility);
-    }
-    
     /* package */ boolean onSavePassword(String schemePlusHost, String username,
             String password, final Message resumeMsg) {
        boolean rVal = false;
@@ -2988,6 +2796,7 @@
      *  @param  end     End of selection to delete.
      */
     /* package */ void deleteSelection(int start, int end) {
+        mTextGeneration++;
         mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
                 new WebViewCore.FocusData(mFocusData));
     }
@@ -3452,8 +3261,7 @@
             p.setOnHierarchyChangeListener(null);
         }
 
-        // Clean up the zoom ring
-        mZoomRingController.setVisible(false);
+        // Clean up the zoom controller
         mZoomButtonsController.setVisible(false);
     }
     
@@ -3565,9 +3373,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int ow, int oh) {
         super.onSizeChanged(w, h, ow, oh);
-        // Center zooming to the center of the screen.  This is appropriate for
-        // this case of zooming, and it also sets us up properly if we remove
-        // the new zoom ring controller
+        // Center zooming to the center of the screen.
         mZoomCenterX = getViewWidth() * .5f;
         mZoomCenterY = getViewHeight() * .5f;
 
@@ -3641,13 +3447,13 @@
             return false;
         }
 
-        if (mShowZoomRingTutorial && getSettings().supportZoom()
-                && (mMaxZoomScale - mMinZoomScale) > ZOOM_RING_STEPS * 0.01f) {
-            ZoomRingController.showZoomTutorialOnce(mContext);
-            mShowZoomRingTutorial = false;
+        if (mShowZoomTutorial && getSettings().supportZoom()
+                && (mMaxZoomScale != mMinZoomScale)) {
+            ZoomButtonsController.showZoomTutorialOnce(mContext);
+            mShowZoomTutorial = false;
             mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                    .obtainMessage(DISMISS_ZOOM_RING_TUTORIAL),
-                    ZOOM_RING_TUTORIAL_DURATION);
+                    .obtainMessage(DISMISS_ZOOM_TUTORIAL),
+                    ZOOM_TUTORIAL_DURATION);
         }
 
         if (LOGV_ENABLED) {
@@ -3655,15 +3461,6 @@
                     + mTouchMode);
         }
 
-        if ((mZoomRingController.isVisible() || mZoomButtonsController.isVisible())
-                && mInZoomTapDragMode) {
-            if (ev.getAction() == MotionEvent.ACTION_UP) {
-                // Just released the second tap, no longer in tap-drag mode
-                mInZoomTapDragMode = false;
-            }
-            return mZoomRingController.handleDoubleTapEvent(ev);
-        }
-
         int action = ev.getAction();
         float x = ev.getX();
         float y = ev.getY();
@@ -3689,7 +3486,7 @@
             WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
             ted.mAction = action;
             ted.mX = viewToContent((int) x + mScrollX);
-            ted.mY = viewToContent((int) y + mScrollY);;
+            ted.mY = viewToContent((int) y + mScrollY);
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mLastSentTouchTime = eventTime;
         }
@@ -3721,7 +3518,7 @@
                     nativeMoveSelection(viewToContent(mSelectX)
                             , viewToContent(mSelectY), false);
                     mTouchSelection = mExtendSelection = true;
-                } else if (!ZoomRingController.useOldZoom(mContext) &&
+                } else if (!ZoomButtonsController.useOldZoom(mContext) &&
                         mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP) &&
                         (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare)) {
                     // Found doubletap, invoke the zoom controller
@@ -3732,13 +3529,11 @@
                         mTextEntry.updateCachedTextfield();
                     }
                     nativeClearFocus(contentX, contentY);
-                    mInZoomTapDragMode = true;
                     if (mLogEvent) {
                         EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
                                 (eventTime - mLastTouchUpTime), eventTime);
                     }
-                    return mZoomRingController.handleDoubleTapEvent(ev) ||
-                            mZoomButtonsController.handleDoubleTapEvent(ev);
+                    return mZoomButtonsController.handleDoubleTapEvent(ev);
                 } else {
                     mTouchMode = TOUCH_INIT_MODE;
                     mPreventDrag = mForwardTouchEvents;
@@ -3747,9 +3542,8 @@
                                 (eventTime - mLastTouchUpTime), eventTime);
                     }
                 }
-                // don't trigger the link if zoom ring is visible
-                if (mTouchMode == TOUCH_INIT_MODE
-                        && !mZoomRingController.isVisible()) {
+                // Trigger the link
+                if (mTouchMode == TOUCH_INIT_MODE) {
                     mPrivateHandler.sendMessageDelayed(mPrivateHandler
                             .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
                 }
@@ -3885,7 +3679,7 @@
                     mUserScroll = true;
                 }
 
-                if (ZoomRingController.useOldZoom(mContext)) {
+                if (ZoomButtonsController.useOldZoom(mContext)) {
                     boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
                     boolean showMagnify = canZoomScrollOut();
                     if (mZoomControls != null && (showPlusMinus || showMagnify)) {
@@ -3909,15 +3703,6 @@
                 mLastTouchUpTime = eventTime;
                 switch (mTouchMode) {
                     case TOUCH_INIT_MODE: // tap
-                        if (mZoomRingController.isVisible()) {
-                            // don't trigger the link if zoom ring is visible,
-                            // but still allow the double tap
-                            mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                    .obtainMessage(RELEASE_SINGLE_TAP,
-                                            new Boolean(false)),
-                                    DOUBLE_TAP_TIMEOUT);
-                            break;
-                        }
                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                         if (getSettings().supportZoom()) {
                             mPrivateHandler.sendMessageDelayed(mPrivateHandler
@@ -4432,14 +4217,6 @@
     }
 
     /**
-     * @hide pending API council? Assuming we make ZoomRingController itself
-     *       public, which I think we will.
-     */
-    public ZoomRingController getZoomRingController() {
-        return mZoomRingController;    
-    }
-    
-    /**
      * Perform zoom in in the webview
      * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
      */
@@ -4482,9 +4259,6 @@
         });
         zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
             public void onClick(View v) {
-                // Hide the zoom ring
-                mZoomRingController.setVisible(false);
-                
                 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                 mPrivateHandler.postDelayed(mZoomControlRunnable,
                         ZOOM_CONTROLS_TIMEOUT);
@@ -4693,6 +4467,7 @@
         arg.put("replace", replace);
         arg.put("start", new Integer(newStart));
         arg.put("end", new Integer(newEnd));
+        mTextGeneration++;
         mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
     }
 
@@ -5006,8 +4781,8 @@
                     }
                     break;
 
-                case DISMISS_ZOOM_RING_TUTORIAL:
-                    mZoomRingController.finishZoomTutorial();
+                case DISMISS_ZOOM_TUTORIAL:
+                    mZoomButtonsController.finishZoomTutorial();
                     break;
 
                 default:
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d72570a..f517dc9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -41,6 +41,10 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
 import android.view.inputmethod.InputMethodManager;
 import android.view.ContextMenu.ContextMenuInfo;
 
@@ -899,6 +903,10 @@
     }
 
     private boolean acceptFilter() {
+        if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
+                ((Filterable) getAdapter()).getFilter() == null) {
+            return false;
+        }
         final Context context = mContext;
         final InputMethodManager inputManager = (InputMethodManager)
                 context.getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -1107,7 +1115,7 @@
         // filter popup needs to follow the widget.
         if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null &&
                 mPopup.isShowing()) {
-            positionPopup(true);
+            positionPopup();
         }
 
         return changed;
@@ -2767,20 +2775,20 @@
         // Make sure we have a window before showing the popup
         if (getWindowVisibility() == View.VISIBLE) {
             createTextFilter(true);
-            positionPopup(false);
+            positionPopup();
             // Make sure we get focus if we are showing the popup
             checkFocus();
         }
     }
 
-    private void positionPopup(boolean update) {
+    private void positionPopup() {
         int screenHeight = getResources().getDisplayMetrics().heightPixels;
         final int[] xy = new int[2];
         getLocationOnScreen(xy);
         // TODO: The 20 below should come from the theme and be expressed in dip
         // TODO: And the gravity should be defined in the theme as well
         final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20);
-        if (!update) {
+        if (!mPopup.isShowing()) {
             mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
                     xy[0], bottomGap);
         } else {
@@ -2849,8 +2857,7 @@
      * @return True if the text filter handled the event, false otherwise.
      */
     boolean sendToTextFilter(int keyCode, int count, KeyEvent event) {
-        if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
-                ((Filterable) getAdapter()).getFilter() == null) {
+        if (!acceptFilter()) {
             return false;
         }
 
@@ -2879,7 +2886,7 @@
             break;
         }
 
-        if (okToSend && acceptFilter()) {
+        if (okToSend) {
             createTextFilter(true);
 
             KeyEvent forwardEvent = event;
@@ -2906,6 +2913,27 @@
     }
 
     /**
+     * Return an InputConnection for editing of the filter text.
+     */
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        // XXX we need to have the text filter created, so we can get an
+        // InputConnection to proxy to.  Unfortunately this means we pretty
+        // much need to make it as soon as a list view gets focus.
+        createTextFilter(false);
+        return mTextFilter.onCreateInputConnection(outAttrs);
+    }
+    
+    /**
+     * For filtering we proxy an input connection to an internal text editor,
+     * and this allows the proxying to happen.
+     */
+    @Override
+    public boolean checkInputConnectionProxy(View view) {
+        return view == mTextFilter;
+    }
+    
+    /**
      * Creates the window for the text filter and populates it with an EditText field;
      *
      * @param animateEntrance true if the window should appear with an animation
@@ -2918,6 +2946,11 @@
                     c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             mTextFilter = (EditText) layoutInflater.inflate(
                     com.android.internal.R.layout.typing_filter, null);
+            // For some reason setting this as the "real" input type changes
+            // the text view in some way that it doesn't work, and I don't
+            // want to figure out why this is.
+            mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
+                    | EditorInfo.TYPE_TEXT_VARIATION_FILTER);
             mTextFilter.addTextChangedListener(this);
             p.setFocusable(false);
             p.setTouchable(false);
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 0a552e8..3368477 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -26,7 +26,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.util.TypedValue;
 import android.view.MotionEvent;
 
 /**
@@ -313,12 +312,17 @@
                 // Non-existent letter
                 while (section > 0) {
                     section--;
-                     prevIndex = mSectionIndexer.getPositionForSection(section);
-                     if (prevIndex != index) {
-                         prevSection = section;
-                         sectionIndex = section;
-                         break;
-                     }
+                    prevIndex = mSectionIndexer.getPositionForSection(section);
+                    if (prevIndex != index) {
+                        prevSection = section;
+                        sectionIndex = section;
+                        break;
+                    } else if (section == 0) {
+                        // When section reaches 0 here, sectionIndex must follow it.
+                        // Assuming mSectionIndexer.getPositionForSection(0) == 0.
+                        sectionIndex = 0;
+                        break;
+                    }
                 }
             }
             // Find the next index, in case the assumed next index is not
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 1d0fd5e..7e55c78 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -42,10 +42,12 @@
     private static final String THREAD_NAME = "Filter";
     private static final int FILTER_TOKEN = 0xD0D0F00D;
     private static final int FINISH_TOKEN = 0xDEADBEEF;
-    
+
     private Handler mThreadHandler;
     private Handler mResultHandler;
 
+    private final Object mLock = new Object();
+
     /**
      * <p>Creates a new asynchronous filter.</p>
      */
@@ -81,8 +83,7 @@
      * @see #publishResults(CharSequence, android.widget.Filter.FilterResults)
      */
     public final void filter(CharSequence constraint, FilterListener listener) {
-        synchronized (this) {
-
+        synchronized (mLock) {
             if (mThreadHandler == null) {
                 HandlerThread thread = new HandlerThread(THREAD_NAME);
                 thread.start();
@@ -221,7 +222,7 @@
                         message.sendToTarget();
                     }
 
-                    synchronized (this) {
+                    synchronized (mLock) {
                         if (mThreadHandler != null) {
                             Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN);
                             mThreadHandler.sendMessageDelayed(finishMessage, 3000);
@@ -229,7 +230,7 @@
                     }
                     break;
                 case FINISH_TOKEN:
-                    synchronized (this) {
+                    synchronized (mLock) {
                         if (mThreadHandler != null) {
                             mThreadHandler.getLooper().quit();
                             mThreadHandler = null;
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index f2cec92..227fb95 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -451,6 +451,7 @@
         public void onProgressChanged(SeekBar bar, int progress, boolean fromtouch) {
             if (fromtouch) {
                 mDragging = true;
+                duration = mPlayer.getDuration();
                 long newposition = (duration * progress) / 1000L;
                 mPlayer.seekTo( (int) newposition);
                 if (mCurrentTime != null)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3f4912f..8271a9a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -43,6 +43,7 @@
 import android.text.GraphicsOperations;
 import android.text.ClipboardManager;
 import android.text.InputFilter;
+import android.text.InputType;
 import android.text.Layout;
 import android.text.ParcelableSpan;
 import android.text.Selection;
@@ -235,7 +236,7 @@
     private CharWrapper mCharWrapper = null;
 
     private boolean mSelectionMoved = false;
-    private boolean mTouchFocusSelectedAll = false;
+    private boolean mTouchFocusSelected = false;
 
     private Marquee mMarquee;
     private boolean mRestartMarquee;
@@ -243,7 +244,7 @@
     private int mMarqueeRepeatLimit = 3;
 
     class InputContentType {
-        int imeOptions = EditorInfo.IME_UNDEFINED;
+        int imeOptions = EditorInfo.IME_NULL;
         String privateImeOptions;
         CharSequence imeActionLabel;
         int imeActionId;
@@ -261,6 +262,7 @@
         final ExtractedText mTmpExtracted = new ExtractedText();
         int mBatchEditNesting;
         boolean mCursorChanged;
+        boolean mSelectionModeChanged;
         boolean mContentChanged;
         int mChangedStart, mChangedEnd, mChangedDelta;
     }
@@ -287,8 +289,8 @@
          *
          * @param v The view that was clicked.
          * @param actionId Identifier of the action.  This will be either the
-         * identifier you supplied, or {@link EditorInfo#IME_UNDEFINED
-         * EditorInfo.IME_UNDEFINED} if being called due to the enter key
+         * identifier you supplied, or {@link EditorInfo#IME_NULL
+         * EditorInfo.IME_NULL} if being called due to the enter key
          * being pressed.
          * @param event If triggered by an enter key, this is the event;
          * otherwise, this is null.
@@ -1492,10 +1494,10 @@
 
     @Override
     public void setPadding(int left, int top, int right, int bottom) {
-        if (left != getPaddingLeft() ||
-            right != getPaddingRight() ||
-            top != getPaddingTop() ||
-            bottom != getPaddingBottom()) {
+        if (left != mPaddingLeft ||
+            right != mPaddingRight ||
+            top != mPaddingTop ||
+            bottom != mPaddingBottom) {
             nullLayouts();
         }
 
@@ -2951,7 +2953,7 @@
      */
     public int getImeOptions() {
         return mInputContentType != null
-                ? mInputContentType.imeOptions : EditorInfo.IME_UNDEFINED;
+                ? mInputContentType.imeOptions : EditorInfo.IME_NULL;
     }
 
     /**
@@ -3013,9 +3015,16 @@
      * Called when an attached input method calls
      * {@link InputConnection#performEditorAction(int)
      * InputConnection.performEditorAction()}
-     * for this text view.  The default implementation will call your click
-     * listener supplied to {@link #setOnEditorActionListener},
-     * or generate an enter key down/up pair to invoke the action if not.
+     * for this text view.  The default implementation will call your action
+     * listener supplied to {@link #setOnEditorActionListener}, or perform
+     * a standard operation for {@link EditorInfo#IME_ACTION_NEXT
+     * EditorInfo.IME_ACTION_NEXT} or {@link EditorInfo#IME_ACTION_DONE
+     * EditorInfo.IME_ACTION_DONE}.
+     * 
+     * <p>For backwards compatibility, if no IME options have been set and the
+     * text view would not normally advance focus on enter, then
+     * the NEXT and DONE actions received here will be turned into an enter
+     * key down/up pair to go through the normal key handling.
      * 
      * @param actionCode The code of the action being performed.
      * 
@@ -3052,6 +3061,7 @@
                 if (imm != null) {
                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
                 }
+                return;
             }
         }
         
@@ -3844,7 +3854,7 @@
             if (imm != null) {
                 if (imm.isActive(this)) {
                     boolean reported = false;
-                    if (ims.mContentChanged) {
+                    if (ims.mContentChanged || ims.mSelectionModeChanged) {
                         // We are in extract mode and the content has changed
                         // in some way... just report complete new text to the
                         // input method.
@@ -4197,7 +4207,7 @@
                         && mInputContentType.enterDown) {
                     mInputContentType.enterDown = false;
                     if (mInputContentType.onEditorActionListener.onEditorAction(
-                            this, EditorInfo.IME_UNDEFINED, event)) {
+                            this, EditorInfo.IME_NULL, event)) {
                         return true;
                     }
                 }
@@ -4272,17 +4282,18 @@
                 outAttrs.actionId = mInputContentType.imeActionId;
                 outAttrs.extras = mInputContentType.extras;
             } else {
-                outAttrs.imeOptions = EditorInfo.IME_UNDEFINED;
+                outAttrs.imeOptions = EditorInfo.IME_NULL;
             }
-            if (outAttrs.imeOptions == EditorInfo.IME_UNDEFINED) {
+            if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION)
+                    == EditorInfo.IME_ACTION_UNSPECIFIED) {
                 if (focusSearch(FOCUS_DOWN) != null) {
                     // An action has not been set, but the enter key will move to
                     // the next focus, so set the action to that.
-                    outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
+                    outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
                 } else {
                     // An action has not been set, and there is no focus to move
                     // to, so let's just supply a "done" action.
-                    outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
+                    outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
                 }
                 if (!shouldAdvanceFocusOnEnter()) {
                     outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
@@ -4307,51 +4318,64 @@
      */
     public boolean extractText(ExtractedTextRequest request,
             ExtractedText outText) {
-        return extractTextInternal(request, -1, -1, -1, outText);
+        return extractTextInternal(request, EXTRACT_UNKNOWN, EXTRACT_UNKNOWN,
+                EXTRACT_UNKNOWN, outText);
     }
     
+    static final int EXTRACT_NOTHING = -2;
+    static final int EXTRACT_UNKNOWN = -1;
+    
     boolean extractTextInternal(ExtractedTextRequest request,
             int partialStartOffset, int partialEndOffset, int delta,
             ExtractedText outText) {
         final CharSequence content = mText;
         if (content != null) {
-            final int N = content.length();
-            if (partialStartOffset < 0) {
-                outText.partialStartOffset = outText.partialEndOffset = -1;
-                partialStartOffset = 0;
-                partialEndOffset = N;
-            } else {
-                // Adjust offsets to ensure we contain full spans.
-                if (content instanceof Spanned) {
-                    Spanned spanned = (Spanned)content;
-                    Object[] spans = spanned.getSpans(partialStartOffset,
-                            partialEndOffset, ParcelableSpan.class);
-                    int i = spans.length;
-                    while (i > 0) {
-                        i--;
-                        int j = spanned.getSpanStart(spans[i]);
-                        if (j < partialStartOffset) partialStartOffset = j;
-                        j = spanned.getSpanEnd(spans[i]);
-                        if (j > partialEndOffset) partialEndOffset = j;
+            if (partialStartOffset != EXTRACT_NOTHING) {
+                final int N = content.length();
+                if (partialStartOffset < 0) {
+                    outText.partialStartOffset = outText.partialEndOffset = -1;
+                    partialStartOffset = 0;
+                    partialEndOffset = N;
+                } else {
+                    // Adjust offsets to ensure we contain full spans.
+                    if (content instanceof Spanned) {
+                        Spanned spanned = (Spanned)content;
+                        Object[] spans = spanned.getSpans(partialStartOffset,
+                                partialEndOffset, ParcelableSpan.class);
+                        int i = spans.length;
+                        while (i > 0) {
+                            i--;
+                            int j = spanned.getSpanStart(spans[i]);
+                            if (j < partialStartOffset) partialStartOffset = j;
+                            j = spanned.getSpanEnd(spans[i]);
+                            if (j > partialEndOffset) partialEndOffset = j;
+                        }
+                    }
+                    outText.partialStartOffset = partialStartOffset;
+                    outText.partialEndOffset = partialEndOffset;
+                    // Now use the delta to determine the actual amount of text
+                    // we need.
+                    partialEndOffset += delta;
+                    if (partialEndOffset > N) {
+                        partialEndOffset = N;
+                    } else if (partialEndOffset < 0) {
+                        partialEndOffset = 0;
                     }
                 }
-                outText.partialStartOffset = partialStartOffset;
-                outText.partialEndOffset = partialEndOffset;
-                // Now use the delta to determine the actual amount of text
-                // we need.
-                partialEndOffset += delta;
-                if (partialEndOffset > N) {
-                    partialEndOffset = N;
-                } else if (partialEndOffset < 0) {
-                    partialEndOffset = 0;
+                if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
+                    outText.text = content.subSequence(partialStartOffset,
+                            partialEndOffset);
+                } else {
+                    outText.text = TextUtils.substring(content, partialStartOffset,
+                            partialEndOffset);
                 }
             }
-            if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
-                outText.text = content.subSequence(partialStartOffset,
-                        partialEndOffset);
-            } else {
-                outText.text = TextUtils.substring(content, partialStartOffset,
-                        partialEndOffset);
+            outText.flags = 0;
+            if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
+                outText.flags |= ExtractedText.FLAG_SELECTING;
+            }
+            if (mSingleLine) {
+                outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
             }
             outText.startOffset = 0;
             outText.selectionStart = Selection.getSelectionStart(content);
@@ -4363,8 +4387,10 @@
     
     boolean reportExtractedText() {
         final InputMethodState ims = mInputMethodState;
-        if (ims != null && ims.mContentChanged) {
+        final boolean contentChanged = ims.mContentChanged;
+        if (ims != null && (contentChanged || ims.mSelectionModeChanged)) {
             ims.mContentChanged = false;
+            ims.mSelectionModeChanged = false;
             final ExtractedTextRequest req = mInputMethodState.mExtracting;
             if (req != null) {
                 InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4372,6 +4398,9 @@
                     if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
                             + ims.mChangedStart + " end=" + ims.mChangedEnd
                             + " delta=" + ims.mChangedDelta);
+                    if (ims.mChangedStart < 0 && !contentChanged) {
+                        ims.mChangedStart = EXTRACT_NOTHING;
+                    }
                     if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
                             ims.mChangedDelta, ims.mTmpExtracted)) {
                         if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
@@ -4408,19 +4437,21 @@
      */
     public void setExtractedText(ExtractedText text) {
         Editable content = getEditableText();
-        if (content == null) {
-            setText(text.text, TextView.BufferType.EDITABLE);
-        } else if (text.partialStartOffset < 0) {
-            removeParcelableSpans(content, 0, content.length());
-            content.replace(0, content.length(), text.text);
-        } else {
-            final int N = content.length();
-            int start = text.partialStartOffset;
-            if (start > N) start = N;
-            int end = text.partialEndOffset;
-            if (end > N) end = N;
-            removeParcelableSpans(content, start, end);
-            content.replace(start, end, text.text);
+        if (text.text != null) {
+            if (content == null) {
+                setText(text.text, TextView.BufferType.EDITABLE);
+            } else if (text.partialStartOffset < 0) {
+                removeParcelableSpans(content, 0, content.length());
+                content.replace(0, content.length(), text.text);
+            } else {
+                final int N = content.length();
+                int start = text.partialStartOffset;
+                if (start > N) start = N;
+                int end = text.partialEndOffset;
+                if (end > N) end = N;
+                removeParcelableSpans(content, start, end);
+                content.replace(start, end, text.text);
+            }
         }
         
         // Now set the selection position...  make sure it is in range, to
@@ -4436,6 +4467,13 @@
         if (end < 0) end = 0;
         else if (end > N) end = N;
         Selection.setSelection(sp, start, end);
+        
+        // Finally, update the selection mode.
+        if ((text.flags&ExtractedText.FLAG_SELECTING) != 0) {
+            MetaKeyKeyListener.startSelecting(this, sp);
+        } else {
+            MetaKeyKeyListener.stopSelecting(this, sp);
+        }
     }
     
     /**
@@ -4473,8 +4511,8 @@
                     ims.mChangedStart = 0;
                     ims.mChangedEnd = mText.length();
                 } else {
-                    ims.mChangedStart = -1;
-                    ims.mChangedEnd = -1;
+                    ims.mChangedStart = EXTRACT_UNKNOWN;
+                    ims.mChangedEnd = EXTRACT_UNKNOWN;
                     ims.mContentChanged = false;
                 }
                 onBeginBatchEdit();
@@ -4503,7 +4541,7 @@
     void finishBatchEdit(final InputMethodState ims) {
         onEndBatchEdit();
         
-        if (ims.mContentChanged) {
+        if (ims.mContentChanged || ims.mSelectionModeChanged) {
             updateAfterEdit();
             reportExtractedText();
         } else if (ims.mCursorChanged) {
@@ -5888,6 +5926,9 @@
 
         if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
             mHighlightPathBogus = true;
+            if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
+                ims.mSelectionModeChanged = true;
+            }
 
             if (Selection.getSelectionStart(buf) >= 0) {
                 if (ims == null || ims.mBatchEditNesting == 0) {
@@ -6026,7 +6067,6 @@
 
                 if (mSelectAllOnFocus) {
                     Selection.setSelection((Spannable) mText, 0, mText.length());
-                    mTouchFocusSelectedAll = true;
                 }
 
                 if (selMoved && selStart >= 0 && selEnd >= 0) {
@@ -6042,6 +6082,7 @@
 
                     Selection.setSelection((Spannable) mText, selStart, selEnd);
                 }
+                mTouchFocusSelected = true;
             }
 
             mFrozenWithFocus = false;
@@ -6149,7 +6190,7 @@
         if (action == MotionEvent.ACTION_DOWN) {
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
-            mTouchFocusSelectedAll = false;
+            mTouchFocusSelected = false;
         }
         
         final boolean superResult = super.onTouchEvent(event);
@@ -6218,10 +6259,11 @@
     /**
      * Returns true, only while processing a touch gesture, if the initial
      * touch down event caused focus to move to the text view and as a result
-     * it selected all of its text.
+     * its selection changed.  Only valid while processing the touch gesture
+     * of interest.
      */
-    public boolean didTouchFocusSelectAll() {
-        return mTouchFocusSelectedAll;
+    public boolean didTouchFocusSelect() {
+        return mTouchFocusSelected;
     }
     
     @Override
@@ -6351,6 +6393,11 @@
         return super.computeVerticalScrollRange();
     }
 
+    @Override
+    protected int computeVerticalScrollExtent() {
+        return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
+    }
+    
     public enum BufferType {
         NORMAL, SPANNABLE, EDITABLE,
     }
@@ -6497,6 +6544,26 @@
      * or null if there is no cursor or no word at the cursor.
      */
     private String getWordForDictionary() {
+        /*
+         * Quick return if the input type is one where adding words
+         * to the dictionary doesn't make any sense.
+         */
+        int klass = mInputType & InputType.TYPE_MASK_CLASS;
+        if (klass == InputType.TYPE_CLASS_NUMBER ||
+            klass == InputType.TYPE_CLASS_PHONE ||
+            klass == InputType.TYPE_CLASS_DATETIME) {
+            return null;
+        }
+
+        int variation = mInputType & InputType.TYPE_MASK_VARIATION;
+        if (variation == InputType.TYPE_TEXT_VARIATION_URI ||
+            variation == InputType.TYPE_TEXT_VARIATION_PASSWORD ||
+            variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
+            variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
+            variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+            return null;
+        }
+
         int end = getSelectionEnd();
 
         if (end < 0) {
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 1227afd..ec6f88b 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -46,8 +46,10 @@
  * such as scaling and tinting.
  */
 public class VideoView extends SurfaceView implements MediaPlayerControl {
+    private String TAG = "VideoView";
     // settable by the client
     private Uri         mUri;
+    private int         mDuration;
 
     // All the stuff we need for playing and showing a video
     private SurfaceHolder mSurfaceHolder = null;
@@ -184,6 +186,8 @@
             mMediaPlayer.setOnPreparedListener(mPreparedListener);
             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
             mIsPrepared = false;
+            Log.v(TAG, "reset duration to -1 in openVideo");
+            mDuration = -1;
             mMediaPlayer.setOnCompletionListener(mCompletionListener);
             mMediaPlayer.setOnErrorListener(mErrorListener);
             mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
@@ -195,10 +199,10 @@
             mMediaPlayer.prepareAsync();
             attachMediaController();
         } catch (IOException ex) {
-            Log.w("VideoView", "Unable to open content: " + mUri, ex);
+            Log.w(TAG, "Unable to open content: " + mUri, ex);
             return;
         } catch (IllegalArgumentException ex) {
-            Log.w("VideoView", "Unable to open content: " + mUri, ex);
+            Log.w(TAG, "Unable to open content: " + mUri, ex);
             return;
         }
     }
@@ -299,7 +303,7 @@
     private MediaPlayer.OnErrorListener mErrorListener =
         new MediaPlayer.OnErrorListener() {
         public boolean onError(MediaPlayer mp, int a, int b) {
-            Log.d("VideoView", "Error: " + a + "," + b);
+            Log.d(TAG, "Error: " + a + "," + b);
             if (mMediaController != null) {
                 mMediaController.hide();
             }
@@ -497,9 +501,14 @@
     
     public int getDuration() {
         if (mMediaPlayer != null && mIsPrepared) {
-            return mMediaPlayer.getDuration();
+            if (mDuration > 0) {
+                return mDuration;
+            }
+            mDuration = mMediaPlayer.getDuration();
+            return mDuration;
         }
-        return -1;
+        mDuration = -1;
+        return mDuration;
     }
     
     public int getCurrentPosition() {
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index ec45e23..1ba2dce 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -16,14 +16,19 @@
 
 package android.widget;
 
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -31,6 +36,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.Window;
 import android.view.WindowManager;
 import android.view.View.OnClickListener;
 import android.view.WindowManager.LayoutParams;
@@ -46,7 +52,7 @@
  *
  * @hide
  */
-public class ZoomButtonsController implements View.OnTouchListener {
+public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyListener {
 
     private static final String TAG = "ZoomButtonsController";
 
@@ -60,13 +66,13 @@
     private WindowManager mWindowManager;
 
     /**
-     * The view that is being zoomed by this zoom ring.
+     * The view that is being zoomed by this zoom controller.
      */
     private View mOwnerView;
 
     /**
      * The bounds of the owner view in global coordinates. This is recalculated
-     * each time the zoom ring is shown.
+     * each time the zoom controller is shown.
      */
     private Rect mOwnerViewBounds = new Rect();
 
@@ -89,11 +95,13 @@
      */
     private int[] mTouchTargetLocationInWindow = new int[2];
     /**
-     * If the zoom ring is dismissed but the user is still in a touch
+     * If the zoom controller is dismissed but the user is still in a touch
      * interaction, we set this to true. This will ignore all touch events until
      * up/cancel, and then set the owner's touch listener to null.
      */
     private boolean mReleaseTouchListenerOnUp;
+    
+    private boolean mIsSecondTapDown;
 
     private boolean mIsVisible;
 
@@ -122,10 +130,16 @@
         }
     };
 
+    /**
+     * The setting name that tracks whether we've shown the zoom tutorial.
+     */
+    private static final String SETTING_NAME_SHOWN_TUTORIAL = "shown_zoom_tutorial";
+    private static Dialog sTutorialDialog;
+
     /** When configuration changes, this is called after the UI thread is idle. */
     private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
-    /** Used to delay the zoom ring dismissal. */
-    private static final int MSG_DISMISS_ZOOM_RING = 3;
+    /** Used to delay the zoom controller dismissal. */
+    private static final int MSG_DISMISS_ZOOM_CONTROLS = 3;
     /**
      * If setVisible(true) is called and the owner view's window token is null,
      * we delay the setVisible(true) call until it is not null.
@@ -140,7 +154,7 @@
                     onPostConfigurationChanged();
                     break;
 
-                case MSG_DISMISS_ZOOM_RING:
+                case MSG_DISMISS_ZOOM_CONTROLS:
                     setVisible(false);
                     break;
                     
@@ -148,7 +162,7 @@
                     if (mOwnerView.getWindowToken() == null) {
                         // Doh, it is still null, throw an exception
                         throw new IllegalArgumentException(
-                                "Cannot make the zoom ring visible if the owner view is " +
+                                "Cannot make the zoom controller visible if the owner view is " +
                                 "not attached to a window.");
                     }
                     setVisible(true);
@@ -165,11 +179,24 @@
 
         mContainer = createContainer();
     }
-
+    
+    public void setZoomInEnabled(boolean enabled) {
+        mControls.setIsZoomInEnabled(enabled);
+    }
+    
+    public void setZoomOutEnabled(boolean enabled) {
+        mControls.setIsZoomOutEnabled(enabled);
+    }
+    
+    public void setZoomSpeed(long speed) {
+        mControls.setZoomSpeed(speed);
+    }
+    
     private FrameLayout createContainer() {
         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
         lp.gravity = Gravity.BOTTOM | Gravity.CENTER;
         lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
+                LayoutParams.FLAG_NOT_FOCUSABLE |
                 LayoutParams.FLAG_LAYOUT_NO_LIMITS;
         lp.height = LayoutParams.WRAP_CONTENT;
         lp.width = LayoutParams.FILL_PARENT;
@@ -182,6 +209,7 @@
         FrameLayout container = new FrameLayout(mContext);
         container.setLayoutParams(lp);
         container.setMeasureAllChildren(true);
+        container.setOnKeyListener(this);
         
         LayoutInflater inflater = (LayoutInflater) mContext
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -326,13 +354,13 @@
         return mContainer;
     }
 
-    public int getZoomRingId() {
+    public int getZoomControlsId() {
         return mControls.getId();
     }
 
     private void dismissControlsDelayed(int delay) {
-        mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
-        mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay);
+        mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS);
+        mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay);
     }
 
     /**
@@ -351,8 +379,21 @@
             int x = (int) event.getX();
             int y = (int) event.getY();
 
+            /*
+             * This class will consume all events in the second tap (down,
+             * move(s), up). But, the owner already got the second tap's down,
+             * so cancel that. Do this before setVisible, since that call
+             * will set us as a touch listener.
+             */
+            MotionEvent cancelEvent = MotionEvent.obtain(event.getDownTime(),
+                    SystemClock.elapsedRealtime(),
+                    MotionEvent.ACTION_CANCEL, 0, 0, 0);
+            mOwnerView.dispatchTouchEvent(cancelEvent);
+            cancelEvent.recycle();
+
             setVisible(true);
             centerPoint(x, y);
+            mIsSecondTapDown = true;
         }
 
         return true;
@@ -373,11 +414,23 @@
         }
     }
 
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
+        return false;
+    }
+
     public boolean onTouch(View v, MotionEvent event) {
         int action = event.getAction();
 
+        // Consume all events during the second-tap interaction (down, move, up/cancel)
+        boolean consumeEvent = mIsSecondTapDown;
+        if ((action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL)) {
+            // The second tap can no longer be down
+            mIsSecondTapDown = false;
+        }
+        
         if (mReleaseTouchListenerOnUp) {
-            // The ring was dismissed but we need to throw away all events until the up
+            // The controls were dismissed but we need to throw away all events until the up
             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                 mOwnerView.setOnTouchListener(null);
                 setTouchTargetView(null);
@@ -417,10 +470,10 @@
                     mOwnerViewBounds.top - targetViewRawY);
             boolean retValue = targetView.dispatchTouchEvent(containerEvent);
             containerEvent.recycle();
-            return retValue;
+            return retValue || consumeEvent;
 
         } else {
-            return false;
+            return consumeEvent;
         }
     }
 
@@ -465,10 +518,102 @@
         refreshPositioningVariables();
     }
 
-    public static boolean useThisZoom(Context context) {
-        return ZoomRingController.getZoomType(context) == 2;
+    /*
+     * This is static so Activities can call this instead of the Views
+     * (Activities usually do not have a reference to the ZoomButtonsController
+     * instance.)
+     */
+    /**
+     * Shows a "tutorial" (some text) to the user teaching her the new zoom
+     * invocation method. Must call from the main thread.
+     * <p>
+     * It checks the global system setting to ensure this has not been seen
+     * before. Furthermore, if the application does not have privilege to write
+     * to the system settings, it will store this bit locally in a shared
+     * preference.
+     *
+     * @hide This should only be used by our main apps--browser, maps, and
+     *       gallery
+     */
+    public static void showZoomTutorialOnce(Context context) {
+        ContentResolver cr = context.getContentResolver();
+        if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TUTORIAL, 0) == 1) {
+            return;
+        }
+
+        SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
+        if (sp.getInt(SETTING_NAME_SHOWN_TUTORIAL, 0) == 1) {
+            return;
+        }
+
+        if (sTutorialDialog != null && sTutorialDialog.isShowing()) {
+            sTutorialDialog.dismiss();
+        }
+
+        LayoutInflater layoutInflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        TextView textView = (TextView) layoutInflater.inflate(
+                com.android.internal.R.layout.alert_dialog_simple_text, null)
+                .findViewById(android.R.id.text1);
+        textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short);
+        
+        sTutorialDialog = new AlertDialog.Builder(context)
+                .setView(textView)
+                .setIcon(0)
+                .create();
+
+        Window window = sTutorialDialog.getWindow();
+        window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND |
+                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
+        sTutorialDialog.show();
     }
-    
+
+    /** @hide Should only be used by Android platform apps */
+    public static void finishZoomTutorial(Context context, boolean userNotified) {
+        if (sTutorialDialog == null) return;
+
+        sTutorialDialog.dismiss();
+        sTutorialDialog = null;
+
+        // Record that they have seen the tutorial
+        if (userNotified) {
+            try {
+                Settings.System.putInt(context.getContentResolver(), SETTING_NAME_SHOWN_TUTORIAL,
+                        1);
+            } catch (SecurityException e) {
+                /*
+                 * The app does not have permission to clear this global flag, make
+                 * sure the user does not see the message when he comes back to this
+                 * same app at least.
+                 */
+                SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
+                sp.edit().putInt(SETTING_NAME_SHOWN_TUTORIAL, 1).commit();
+            }
+        }
+    }
+
+    /** @hide Should only be used by Android platform apps */
+    public void finishZoomTutorial() {
+        finishZoomTutorial(mContext, true);
+    }
+
+    // Temporary methods for different zoom types
+    static int getZoomType(Context context) {
+        return Settings.System.getInt(context.getContentResolver(), "zoom", 1);
+    }
+
+    public static boolean useOldZoom(Context context) {
+        return getZoomType(context) == 0;
+    }
+
+    public static boolean useThisZoom(Context context) {
+        return getZoomType(context) == 2;
+    }
+
     public interface OnZoomListener {
         void onCenter(int x, int y);
         void onVisibilityChanged(boolean visible);
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index fdc05b6..e978db8 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -81,7 +81,7 @@
     }
     
     public void show() {
-        if (ZoomRingController.useOldZoom(mContext)) {
+        if (ZoomButtonsController.useOldZoom(mContext)) {
             fade(View.VISIBLE, 0.0f, 1.0f);
         }
     }
diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java
deleted file mode 100644
index 83a1225..0000000
--- a/core/java/android/widget/ZoomRing.java
+++ /dev/null
@@ -1,1274 +0,0 @@
-/*
- * Copyright (C) 2009 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.widget;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RotateDrawable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * A view that has a draggable thumb on a circle.
- * 
- * @hide
- */
-public class ZoomRing extends View {
-    private static final String TAG = "ZoomRing";
-
-    // TODO: Temporary until the trail is done
-    private static final boolean DRAW_TRAIL = false;
-
-    /**
-     * To avoid floating point calculations and int round-offs, we multiply
-     * radians by this value.
-     */
-    public static final int RADIAN_INT_MULTIPLIER = 10000;
-    /** The allowable margin of error when comparing two angles. */
-    public static final int RADIAN_INT_ERROR = 100;
-    public static final int PI_INT_MULTIPLIED = (int) (Math.PI * RADIAN_INT_MULTIPLIER);
-    public static final int TWO_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED * 2;
-    private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2;
-
-    private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
-    private final int mTouchSlop;
-
-    /** The slop when the user is grabbing the thumb. */
-    private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 8;
-    /** The slop until a user starts dragging the thumb. */
-    private static final int THUMB_DRAG_SLOP = PI_INT_MULTIPLIED / 12;
-
-    /** The distance (in px) from the center of the ring to the center of the thumb. */ 
-    private int mThumbDistance;
-
-    /** The angle on a unit circle that is considered to be the zoom ring's 0 degree. */
-    private int mZeroAngle = HALF_PI_INT_MULTIPLIED * 3;
-
-    /**
-     * The maximum delta angle that the thumb can move. The primary use is to
-     * ensure that when a user taps on the ring, the movement to reach that
-     * target angle is not ambiguous (for example, if the thumb is at 0 and he
-     * taps 180, should the thumb go clockwise or counterclockwise?
-     * <p>
-     * Includes error because we compare this to the result of
-     * getDelta(getClosestTickeAngle(..), oldAngle) which ends up having some
-     * rounding error.
-     */ 
-    private static final int MAX_ABS_JUMP_DELTA_ANGLE = (2 * PI_INT_MULTIPLIED / 3) +
-            RADIAN_INT_ERROR; 
-
-    /** The cached X of the zoom ring's center (in zoom ring coordinates). */
-    private int mCenterX;
-    /** The cached Y of the zoom ring's center (in zoom ring coordinates). */
-    private int mCenterY;
-
-    /** The angle of the thumb (in int radians) */
-    private int mThumbAngle;
-    /** The cached width/2 of the zoom ring. */
-    private int mThumbHalfWidth;
-    /** The cached height/2 of the zoom ring. */
-    private int mThumbHalfHeight;
-    
-    /**
-     * The bound for the thumb's movement when it is being dragged clockwise.
-     * Can be Integer.MIN_VALUE if there is no bound in this direction.
-     */
-    private int mThumbCwBound = Integer.MIN_VALUE;
-    /**
-     * The bound for the thumb's movement when it is being dragged
-     * counterclockwise. Can be Integer.MIN_VALUE if there is no bound in this
-     * direction.
-     */
-    private int mThumbCcwBound = Integer.MIN_VALUE;
-    
-    /**
-     * Whether to enforce the maximum absolute jump delta. See
-     * {@link #MAX_ABS_JUMP_DELTA_ANGLE}.
-     */
-    private boolean mEnforceMaxAbsJump = true;
-    
-    /** The inner radius of the track. */
-    private int mTrackInnerRadius;
-    /** Cached square of the inner radius of the track. */
-    private int mTrackInnerRadiusSquared;
-    /** The outer radius of the track. */
-    private int mTrackOuterRadius;
-    /** Cached square of the outer radius of the track. */
-    private int mTrackOuterRadiusSquared;
-
-    /** The raw X of where the widget previously was located. */
-    private int mPreviousWidgetDragX;
-    /** The raw Y of where the widget previously was located. */
-    private int mPreviousWidgetDragY;
-
-    /** Whether the thumb should be visible. */
-    private boolean mThumbVisible = true;
-    
-    /** The drawable for the thumb. */
-    private Drawable mThumbDrawable;
-    
-    /** Shown beneath the thumb if we can still zoom in. */
-    private Drawable mZoomInArrowDrawable;
-    /** Shown beneath the thumb if we can still zoom out. */
-    private Drawable mZoomOutArrowDrawable;
-    
-    /** @see #mThumbArrowsToDraw */
-    private static final int THUMB_ARROW_PLUS = 1 << 0;
-    /** @see #mThumbArrowsToDraw */
-    private static final int THUMB_ARROW_MINUS = 1 << 1;
-    /** Bitwise-OR of {@link #THUMB_ARROW_MINUS} and {@link #THUMB_ARROW_PLUS} */
-    private int mThumbArrowsToDraw;
-    
-    /** The duration for the thumb arrows fading out */
-    private static final int THUMB_ARROWS_FADE_DURATION = 300;
-    /** The time when the fade out started. */
-    private long mThumbArrowsFadeStartTime;
-    /** The current alpha for the thumb arrows. */
-    private int mThumbArrowsAlpha = 255;
-
-    /** The distance from the center to the zoom arrow hints (usually plus and minus). */
-    private int mZoomArrowHintDistance;
-    /** The offset angle from the thumb angle to draw the zoom arrow hints. */
-    private int mZoomArrowHintOffsetAngle = TWO_PI_INT_MULTIPLIED / 11;
-    /** Drawn (without rotation) on top of the arrow. */
-    private Drawable mZoomInArrowHintDrawable;
-    /** Drawn (without rotation) on top of the arrow. */
-    private Drawable mZoomOutArrowHintDrawable;
-    
-    /** Zoom ring is just chillin' */
-    private static final int MODE_IDLE = 0;
-    /**
-     * User has his finger down somewhere on the ring (besides the thumb) and we
-     * are waiting for him to move the slop amount before considering him in the
-     * drag thumb state.
-     */
-    private static final int MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP = 5;
-    /** User is dragging the thumb. */
-    private static final int MODE_DRAG_THUMB = 1;
-    /**
-     * User has his finger down, but we are waiting for him to pass the touch
-     * slop before going into the #MODE_MOVE_ZOOM_RING. This is a good time to
-     * show the movable hint.
-     */
-    private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4;
-    /** User is moving the zoom ring. */
-    private static final int MODE_MOVE_ZOOM_RING = 2;
-    /** User is dragging the thumb via tap-drag. */
-    private static final int MODE_TAP_DRAG = 3;
-    /** Ignore the touch interaction until the user touches the thumb again. */
-    private static final int MODE_IGNORE_UNTIL_TOUCHES_THUMB = 6;
-    /** The current mode of interaction. */
-    private int mMode;
-    /** Records the last mode the user was in. */
-    private int mPreviousMode;
-
-    /** The previous time of the up-touch on the center. */
-    private long mPreviousCenterUpTime;
-    /** The previous X of down-touch. */
-    private int mPreviousDownX;
-    /** The previous Y of down-touch. */
-    private int mPreviousDownY;
-
-    /** The angle where the user first grabbed the thumb. */
-    private int mInitialGrabThumbAngle;
-    
-    /** The callback. */
-    private OnZoomRingCallback mCallback;
-    /** The tick angle that we previously called back with. */
-    private int mPreviousCallbackTickAngle;
-    /** The delta angle between ticks.  A tick is a callback point. */
-    private int mTickDelta = Integer.MAX_VALUE;
-    /** If the user drags to within __% of a tick, snap to that tick. */
-    private int mFuzzyTickDelta = Integer.MAX_VALUE;
-    
-    /** The angle where the thumb is officially starting to be dragged. */
-    private int mThumbDragStartAngle;
-
-    /** The drawable for the zoom trail. */
-    private Drawable mTrail;
-    /** The accumulated angle for the trail. */
-    private double mAcculumalatedTrailAngle;
-    
-    /** The animation-step tracker for scrolling the thumb to a particular position. */
-    private Scroller mThumbScroller;
-
-    /** Whether to ever vibrate when passing a tick. */
-    private boolean mVibration = true;
-    
-    /** The drawable used to hint that this can pan its owner. */
-    private Drawable mPanningArrowsDrawable;
-    
-    private static final int MSG_THUMB_SCROLLER_STEP = 1;
-    private static final int MSG_THUMB_ARROWS_FADE_STEP = 2;
-    private Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_THUMB_SCROLLER_STEP:
-                    onThumbScrollerStep();
-                    break;
-                    
-                case MSG_THUMB_ARROWS_FADE_STEP:
-                    onThumbArrowsFadeStep();
-                    break;
-            }
-        }
-    };
-    
-    public ZoomRing(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
-        mTouchSlop = viewConfiguration.getScaledTouchSlop();
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomRing, defStyle, 0);
-        mThumbDistance = (int) a.getDimension(R.styleable.ZoomRing_thumbDistance, 0);
-        setTrackRadii(
-                (int) a.getDimension(R.styleable.ZoomRing_trackInnerRadius, 0),
-                (int) a.getDimension(R.styleable.ZoomRing_trackOuterRadius, Integer.MAX_VALUE));
-        mThumbDrawable = a.getDrawable(R.styleable.ZoomRing_thumbDrawable);
-        mZoomInArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowDrawable);
-        mZoomOutArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowDrawable);
-        mZoomInArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowHintDrawable);
-        mZoomOutArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowHintDrawable);
-        mZoomArrowHintDistance =
-                (int) a.getDimension(R.styleable.ZoomRing_zoomArrowHintDistance, 0);
-        mZoomArrowHintOffsetAngle =
-                (int) (a.getInteger(R.styleable.ZoomRing_zoomArrowHintOffsetAngle, 0)
-                        * TWO_PI_INT_MULTIPLIED / 360);
-        mPanningArrowsDrawable = a.getDrawable(R.styleable.ZoomRing_panningArrowsDrawable);
-        a.recycle();
-        
-        Resources res = context.getResources();
-        if (DRAW_TRAIL) {
-            // TODO get drawables from style instead
-            mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate();
-        }
-
-        mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2;
-        mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2;
-
-        setTickDelta(PI_INT_MULTIPLIED / 6);
-    }
-
-    public ZoomRing(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.zoomRingStyle);
-    }
-
-    public ZoomRing(Context context) {
-        this(context, null);
-    }
-
-    public void setTrackDrawable(Drawable drawable) {
-        setBackgroundDrawable(drawable);
-    }
-    
-    public void setCallback(OnZoomRingCallback callback) {
-        mCallback = callback;
-    }
-
-    /**
-     * Sets the distance between ticks.  This will be used as a callback threshold.
-     * 
-     * @param angle The angle between ticks.
-     */
-    public void setTickDelta(int angle) {
-        mTickDelta = angle;
-        mFuzzyTickDelta = (int) (angle * 0.65f);
-    }
-
-    public void setVibration(boolean vibration) {
-        mVibration = vibration;
-    }
-    
-    public void setThumbVisible(boolean thumbVisible) {
-        if (mThumbVisible != thumbVisible) {
-            mThumbVisible = thumbVisible;
-            invalidate();
-        }
-    }
-    
-    public Drawable getPanningArrowsDrawable() {
-        return mPanningArrowsDrawable;
-    }
-    
-    public void setTrackRadii(int innerRadius, int outerRadius) {
-        mTrackInnerRadius = innerRadius;
-        mTrackOuterRadius = outerRadius;
-        
-        mTrackInnerRadiusSquared = innerRadius * innerRadius;
-        if (mTrackInnerRadiusSquared < innerRadius) {
-            // Prevent overflow
-            mTrackInnerRadiusSquared = Integer.MAX_VALUE;
-        }
-
-        mTrackOuterRadiusSquared = outerRadius * outerRadius;
-        if (mTrackOuterRadiusSquared < outerRadius) {
-            // Prevent overflow
-            mTrackOuterRadiusSquared = Integer.MAX_VALUE;
-        }
-    }
-
-    public int getTrackInnerRadius() {
-        return mTrackInnerRadius;
-    }
-    
-    public int getTrackOuterRadius() {
-        return mTrackOuterRadius;
-    }
-    
-    public void setThumbClockwiseBound(int angle) {
-        if (angle < 0) {
-            mThumbCwBound = Integer.MIN_VALUE;
-        } else {
-            mThumbCwBound = getClosestTickAngle(angle);
-        }
-        updateEnforceMaxAbsJump();
-    }
-    
-    public void setThumbCounterclockwiseBound(int angle) {
-        if (angle < 0) {
-            mThumbCcwBound = Integer.MIN_VALUE;
-        } else {
-            mThumbCcwBound = getClosestTickAngle(angle);
-        }
-        updateEnforceMaxAbsJump();
-    }
-    
-    private void updateEnforceMaxAbsJump() {
-        // If there are bounds in both direction, there is no reason to restrict
-        // the amount that a user can absolute jump to
-        mEnforceMaxAbsJump =
-                mThumbCcwBound == Integer.MIN_VALUE || mThumbCwBound == Integer.MIN_VALUE;
-    }
-    
-    public int getThumbAngle() {
-        return mThumbAngle;
-    }
-    
-    public void setThumbAngle(int angle) {
-        angle = getValidAngle(angle);
-        mPreviousCallbackTickAngle = getClosestTickAngle(angle);
-        setThumbAngleAuto(angle, false, false);
-    }
-    
-    /**
-     * Sets the thumb angle. If already animating, will continue the animation,
-     * otherwise it will do a direct jump.
-     * 
-     * @param angle
-     * @param useDirection Whether to use the ccw parameter
-     * @param ccw Whether going counterclockwise (only used if useDirection is true)
-     */
-    private void setThumbAngleAuto(int angle, boolean useDirection, boolean ccw) {
-        if (mThumbScroller == null
-                || mThumbScroller.isFinished()
-                || Math.abs(getDelta(angle, getThumbScrollerAngle())) < THUMB_GRAB_SLOP) {
-            setThumbAngleInt(angle);
-        } else {
-            if (useDirection) {
-                setThumbAngleAnimated(angle, 0, ccw);
-            } else {
-                setThumbAngleAnimated(angle, 0);
-            }
-        }
-    }
-    
-    private void setThumbAngleInt(int angle) {
-        mThumbAngle = angle;
-        int unoffsetAngle = angle + mZeroAngle;
-        int thumbCenterX = (int) (Math.cos(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
-                mThumbDistance) + mCenterX;
-        int thumbCenterY = (int) (Math.sin(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
-                mThumbDistance) * -1 + mCenterY;
-
-        mThumbDrawable.setBounds(thumbCenterX - mThumbHalfWidth,
-                thumbCenterY - mThumbHalfHeight,
-                thumbCenterX + mThumbHalfWidth,
-                thumbCenterY + mThumbHalfHeight);
-
-        if (mThumbArrowsToDraw > 0) {
-            setThumbArrowsAngle(angle);
-        }
-        
-        if (DRAW_TRAIL) {
-            double degrees;
-            degrees = Math.min(359.0, Math.abs(mAcculumalatedTrailAngle));
-            int level = (int) (10000.0 * degrees / 360.0);
-
-            mTrail.setLevel((int) (10000.0 *
-                    (-Math.toDegrees(angle / (double) RADIAN_INT_MULTIPLIER) -
-                            degrees + 90) / 360.0));
-            ((RotateDrawable) mTrail).getDrawable().setLevel(level);
-        }
-
-        invalidate();
-    }
-    
-    /**
-     * 
-     * @param angle
-     * @param duration The animation duration, or 0 for the default duration.
-     */
-    public void setThumbAngleAnimated(int angle, int duration) {
-        // The angle when going from the current angle to the new angle
-        int deltaAngle = getDelta(mThumbAngle, angle);
-        setThumbAngleAnimated(angle, duration, deltaAngle > 0);
-    }
-    
-    public void setThumbAngleAnimated(int angle, int duration, boolean counterClockwise) {
-        if (mThumbScroller == null) {
-            mThumbScroller = new Scroller(mContext);
-        }
-        
-        int startAngle = mThumbAngle;
-        int endAngle = getValidAngle(angle);
-        int deltaAngle = getDelta(startAngle, endAngle, counterClockwise);
-        if (startAngle + deltaAngle < 0) {
-            // Keep our angles positive
-            startAngle += TWO_PI_INT_MULTIPLIED;
-        }
-        
-        if (!mThumbScroller.isFinished()) {
-            duration = mThumbScroller.getDuration() - mThumbScroller.timePassed();
-        } else if (duration == 0) {
-            duration = getAnimationDuration(deltaAngle);
-        }
-        mThumbScroller.startScroll(startAngle, 0, deltaAngle, 0, duration);
-        onThumbScrollerStep();
-    }
-    
-    private int getAnimationDuration(int deltaAngle) {
-        if (deltaAngle < 0) deltaAngle *= -1;
-        return 300 + deltaAngle * 300 / RADIAN_INT_MULTIPLIER; 
-    }
-    
-    private void onThumbScrollerStep() {
-        if (!mThumbScroller.computeScrollOffset()) return;
-        setThumbAngleInt(getThumbScrollerAngle());        
-        mHandler.sendEmptyMessage(MSG_THUMB_SCROLLER_STEP);
-    }
-
-    private int getThumbScrollerAngle() {
-        return mThumbScroller.getCurrX() % TWO_PI_INT_MULTIPLIED;
-    }
-    
-    public void resetThumbAngle() {
-        mPreviousCallbackTickAngle = 0;
-        setThumbAngleInt(0);
-    }
-    
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
-                resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right,
-            int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        // Cache the center point
-        mCenterX = (right - left) / 2;
-        mCenterY = (bottom - top) / 2;
-
-        // Done here since we now have center, which is needed to calculate some
-        // aux info for thumb angle
-        if (mThumbAngle == Integer.MIN_VALUE) {
-            resetThumbAngle();
-        }
-
-        if (DRAW_TRAIL) {
-            mTrail.setBounds(0, 0, right - left, bottom - top);
-        }
-        
-        // These drawables are the same size as the track
-        mZoomInArrowDrawable.setBounds(0, 0, right - left, bottom - top);
-        mZoomOutArrowDrawable.setBounds(0, 0, right - left, bottom - top);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return handleTouch(event.getAction(), event.getEventTime(),
-                (int) event.getX(), (int) event.getY(), (int) event.getRawX(),
-                (int) event.getRawY());
-    }
-
-    private void resetToIdle() {
-        setMode(MODE_IDLE);
-        mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE;
-        mAcculumalatedTrailAngle = 0.0;
-    }
-
-    public void setTapDragMode(boolean tapDragMode, int x, int y) {
-        resetToIdle();
-        if (tapDragMode) {
-            setMode(MODE_TAP_DRAG);
-            mCallback.onUserInteractionStarted();
-            onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY));
-        } else {
-            onTouchUp(SystemClock.elapsedRealtime(), true);
-        }
-    }
-
-    public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) {
-        // local{X,Y} will be where the center of the widget is (0,0)
-        int localX = x - mCenterX;
-        int localY = y - mCenterY;
-
-        /*
-         * If we are not drawing the thumb, there is no way for the user to be
-         * touching the thumb. Also, if this is the case, assume they are not
-         * touching the ring (so the user cannot absolute set the thumb, and
-         * there will be a larger touch region for going into the move-ring
-         * mode).
-         */
-        boolean isTouchingThumb = mThumbVisible;
-        boolean isTouchingRing = mThumbVisible;
-        
-        int touchAngle = getAngle(localX, localY);
-        
-        int radiusSquared = localX * localX + localY * localY;
-        if (radiusSquared < mTrackInnerRadiusSquared ||
-                radiusSquared > mTrackOuterRadiusSquared) {
-            // Out-of-bounds
-            isTouchingThumb = false;
-            isTouchingRing = false;
-        }
-
-        if (isTouchingThumb) {
-            int deltaThumbAndTouch = getDelta(mThumbAngle, touchAngle);
-            int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ?
-                    deltaThumbAndTouch : -deltaThumbAndTouch;
-            if (absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) {
-                // Didn't grab close enough to the thumb
-                isTouchingThumb = false;
-            }
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                if (!isTouchingRing &&
-                        (time - mPreviousCenterUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT)) {
-                    // Make sure the double-tap is in the center of the widget (and not on the ring)
-                    mCallback.onZoomRingDismissed();
-                    onTouchUp(time, isTouchingRing);
-                    
-                    // Dismissing, so halt here
-                    return true;
-                }
-
-                resetToIdle();
-                mCallback.onUserInteractionStarted();
-                mPreviousDownX = x;
-                mPreviousDownY = y;
-                // Fall through to code below switch (since the down is used for
-                // jumping to the touched tick)
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                // Fall through to code below switch
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                onTouchUp(time, isTouchingRing);
-                return true;
-
-            default:
-                return false;
-        }
-
-        if (mMode == MODE_IDLE) {
-            if (isTouchingThumb) {
-                // They grabbed the thumb
-                setMode(MODE_DRAG_THUMB);
-                onThumbDragStarted(touchAngle);
-                
-            } else if (isTouchingRing) {
-                // They tapped somewhere else on the ring
-                int tickAngle = getClosestTickAngle(touchAngle);
-                int deltaThumbAndTick = getDelta(mThumbAngle, tickAngle);
-                int boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick);
-
-                if (mEnforceMaxAbsJump) {
-                    // Enforcing the max jump
-                    if (deltaThumbAndTick > MAX_ABS_JUMP_DELTA_ANGLE ||
-                            deltaThumbAndTick < -MAX_ABS_JUMP_DELTA_ANGLE) {
-                        // Trying to jump too far, ignore this touch interaction                    
-                        setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
-                        return true;
-                    }
-
-                    if (boundAngle != Integer.MIN_VALUE) {
-                        // Cap the user's jump to the bound
-                        tickAngle = boundAngle;
-                    }
-                } else {
-                    // Not enforcing the max jump, but we have to make sure
-                    // we're getting to the tapped angle by going through the
-                    // in-bounds region
-                    if (boundAngle != Integer.MIN_VALUE) {
-                        // Going this direction hits a bound, let's go the opposite direction
-                        boolean oldDirectionIsCcw = deltaThumbAndTick > 0;
-                        deltaThumbAndTick = getDelta(mThumbAngle, tickAngle, !oldDirectionIsCcw);
-                        boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick);
-                        if (boundAngle != Integer.MIN_VALUE) {
-                            // Cannot get to the tapped location because it is out-of-bounds
-                            setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
-                            return true;
-                        }
-                    }
-                }
-
-                setMode(MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP);
-                mInitialGrabThumbAngle = touchAngle;
-                boolean ccw = deltaThumbAndTick > 0;
-                setThumbAngleAnimated(tickAngle, 0, ccw);
-                
-                /*
-                 * Our thumb scrolling animation takes us from mThumbAngle to
-                 * tickAngle, so manifest that as the user dragging the thumb
-                 * there.
-                 */
-                onThumbDragStarted(mThumbAngle);
-                // We know which direction we want to go
-                onThumbDragged(tickAngle, true, ccw);
-                
-            } else {
-                // They tapped somewhere else on the widget
-                setMode(MODE_WAITING_FOR_MOVE_ZOOM_RING);
-                mCallback.onZoomRingSetMovableHintVisible(true);
-            }
-
-        } else if (mMode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) {
-            int deltaDownAngle = getDelta(mInitialGrabThumbAngle, touchAngle);
-            if ((deltaDownAngle < -THUMB_DRAG_SLOP || deltaDownAngle > THUMB_DRAG_SLOP) &&
-                    isDeltaInBounds(mInitialGrabThumbAngle, deltaDownAngle)) {
-                setMode(MODE_DRAG_THUMB);
-                
-                // No need to call onThumbDragStarted, since that was done when they tapped-to-jump
-            }
-
-        } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
-            if (Math.abs(x - mPreviousDownX) > mTouchSlop ||
-                    Math.abs(y - mPreviousDownY) > mTouchSlop) {
-                /* Make sure the user has moved the slop amount before going into that mode. */
-                setMode(MODE_MOVE_ZOOM_RING);
-                mCallback.onZoomRingMovingStarted();
-                // Move the zoom ring so it is under the finger where the user first touched
-                mCallback.onZoomRingMoved(x - mPreviousDownX, y - mPreviousDownY, rawX, rawY);
-            }
-        } else if (mMode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) {
-            if (isTouchingThumb) {
-                // The user is back on the thumb, let's go back to the previous mode
-                setMode(mPreviousMode);
-            }
-        }
-
-        // Purposefully not an "else if"
-        if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) {
-            if (isTouchingRing) {
-                onThumbDragged(touchAngle, false, false);
-            }
-        } else if (mMode == MODE_MOVE_ZOOM_RING) {
-            onZoomRingMoved(rawX, rawY);
-        }
-
-        return true;
-    }
-    
-    private void onTouchUp(long time, boolean isTouchingRing) {
-        int mode = mMode;
-        if (mode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) {
-            // For cleaning up, pretend like the user was still in the previous mode
-            mode = mPreviousMode;
-        }
-        
-        if (mode == MODE_MOVE_ZOOM_RING || mode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
-            mCallback.onZoomRingSetMovableHintVisible(false);
-            if (mode == MODE_MOVE_ZOOM_RING) {
-                mCallback.onZoomRingMovingStopped();
-            }
-        } else if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG ||
-                mode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) {
-            onThumbDragStopped();
-            
-            if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG) {
-                // Animate back to a tick
-                setThumbAngleAnimated(mPreviousCallbackTickAngle, 0);
-            }
-        }
-        mCallback.onUserInteractionStopped();
-        
-        if (!isTouchingRing) {
-            mPreviousCenterUpTime = time;
-        }
-    }
-    
-    private void setMode(int mode) {
-        if (mode != mMode) {
-            mPreviousMode = mMode;
-            mMode = mode;
-        }
-    }
-
-    private boolean isDeltaInBounds(int startAngle, int deltaAngle) {
-        return getBoundIfExceeds(startAngle, deltaAngle) == Integer.MIN_VALUE;
-    }
-    
-    private int getBoundIfExceeds(int startAngle, int deltaAngle) {
-        if (deltaAngle > 0) {
-            // Counterclockwise movement
-            if (mThumbCcwBound != Integer.MIN_VALUE &&
-                    getDelta(startAngle, mThumbCcwBound, true) < deltaAngle) {
-                return mThumbCcwBound;
-            }
-        } else if (deltaAngle < 0) {
-            // Clockwise movement, both of these will be negative
-            int deltaThumbAndBound = getDelta(startAngle, mThumbCwBound, false);
-            if (mThumbCwBound != Integer.MIN_VALUE &&
-                    deltaThumbAndBound > deltaAngle) {
-                // Tapped outside of the bound in that direction                    
-                return mThumbCwBound;
-            }
-        }
-        
-        return Integer.MIN_VALUE;
-    }
-
-    private int getDelta(int startAngle, int endAngle, boolean useDirection, boolean ccw) {
-        return useDirection ? getDelta(startAngle, endAngle, ccw) : getDelta(startAngle, endAngle);
-    }
-    
-    /**
-     * Gets the smallest delta between two angles, and infers the direction
-     * based on the shortest path between the two angles. If going from
-     * startAngle to endAngle is counterclockwise, the result will be positive.
-     * If it is clockwise, the result will be negative.
-     * 
-     * @param startAngle The start angle.
-     * @param endAngle The end angle.
-     * @return The difference in angles.
-     */
-    private int getDelta(int startAngle, int endAngle) {
-        int largerAngle, smallerAngle;
-        if (endAngle > startAngle) {
-            largerAngle = endAngle;
-            smallerAngle = startAngle;
-        } else {
-            largerAngle = startAngle;
-            smallerAngle = endAngle;
-        }
-
-        int delta = largerAngle - smallerAngle;
-        if (delta <= PI_INT_MULTIPLIED) {
-            // If going clockwise, negate the delta
-            return startAngle == largerAngle ? -delta : delta; 
-        } else {
-            // The other direction is the delta we want (it includes the
-            // discontinuous 0-2PI angle)
-            delta = TWO_PI_INT_MULTIPLIED - delta;
-            // If going clockwise, negate the delta
-            return startAngle == smallerAngle ? -delta : delta; 
-        }
-    }
-
-    /**
-     * Gets the delta between two angles in the direction specified.
-     * 
-     * @param startAngle The start angle.
-     * @param endAngle The end angle.
-     * @param counterClockwise The direction to take when computing the delta.
-     * @return The difference in angles in the given direction.
-     */
-    private int getDelta(int startAngle, int endAngle, boolean counterClockwise) {
-        int delta = endAngle - startAngle;
-        
-        if (!counterClockwise && delta > 0) {
-            // Crossed the discontinuous 0/2PI angle, take the leftover slice of
-            // the pie and negate it
-            return -TWO_PI_INT_MULTIPLIED + delta;
-        } else if (counterClockwise && delta < 0) {
-            // Crossed the discontinuous 0/2PI angle, take the leftover slice of
-            // the pie (and ensure it is positive)
-            return TWO_PI_INT_MULTIPLIED + delta;
-        } else {
-            return delta;
-        }
-    }
-    
-    private void onThumbDragStarted(int startAngle) {
-        setThumbArrowsVisible(false);
-        mThumbDragStartAngle = startAngle;
-        mCallback.onZoomRingThumbDraggingStarted();
-    }
-    
-    private void onThumbDragged(int touchAngle, boolean useDirection, boolean ccw) {
-        boolean animateThumbToNewAngle = false;
-        
-        int totalDeltaAngle;
-        totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw);
-        if (totalDeltaAngle >= mFuzzyTickDelta
-                || totalDeltaAngle <= -mFuzzyTickDelta) {
-
-            if (!useDirection) {
-                // Set ccw to match the direction found by getDelta
-                ccw = totalDeltaAngle > 0;
-            }
-            
-            /*
-             * When the user slides the thumb through the tick that corresponds
-             * to a zoom bound, we don't want to abruptly stop there. Instead,
-             * let the user slide it to the next tick, and then animate it back
-             * to the original zoom bound tick. Because of this, we make sure
-             * the delta from the bound is more than halfway to the next tick.
-             * We make sure the bound is between the touch and the previous
-             * callback to ensure we just passed the bound.
-             */ 
-            int oldTouchAngle = touchAngle;
-            if (ccw && mThumbCcwBound != Integer.MIN_VALUE) {
-                int deltaCcwBoundAndTouch =
-                        getDelta(mThumbCcwBound, touchAngle, useDirection, true);
-                if (deltaCcwBoundAndTouch >= mTickDelta / 2) {
-                    // The touch has past a bound
-                    int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle,
-                            touchAngle, useDirection, true);
-                    if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) {
-                        // The bound is between the previous callback angle and the touch
-                        touchAngle = mThumbCcwBound;
-                        // We're moving the touch BACK to the bound, so opposite direction
-                        ccw = false;
-                    }
-                }
-            } else if (!ccw && mThumbCwBound != Integer.MIN_VALUE) {
-                // See block above for general comments
-                int deltaCwBoundAndTouch =
-                        getDelta(mThumbCwBound, touchAngle, useDirection, false);
-                if (deltaCwBoundAndTouch <= -mTickDelta / 2) {
-                    int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle,
-                            touchAngle, useDirection, false);
-                    /*
-                     * Both of these will be negative since we got delta in
-                     * clockwise direction, and we want the magnitude of
-                     * deltaPreviousCbAndTouch to be greater than the magnitude
-                     * of deltaCwBoundAndTouch
-                     */
-                    if (deltaPreviousCbAndTouch <= deltaCwBoundAndTouch) {
-                        touchAngle = mThumbCwBound;
-                        ccw = true;
-                    }
-                }
-            }
-            if (touchAngle != oldTouchAngle) {
-                // We bounded the touch angle
-                totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw);
-                animateThumbToNewAngle = true;
-                setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
-            }
-            
-            
-            // Prevent it from jumping too far
-            if (mEnforceMaxAbsJump) {
-                if (totalDeltaAngle <= -MAX_ABS_JUMP_DELTA_ANGLE) {
-                    totalDeltaAngle = -MAX_ABS_JUMP_DELTA_ANGLE;
-                    animateThumbToNewAngle = true;
-                } else if (totalDeltaAngle >= MAX_ABS_JUMP_DELTA_ANGLE) {
-                    totalDeltaAngle = MAX_ABS_JUMP_DELTA_ANGLE;
-                    animateThumbToNewAngle = true;
-                }
-            }
-
-            /*
-             * We need to cover the edge case of a user grabbing the thumb,
-             * going into the center of the widget, and then coming out from the
-             * center to an angle that's slightly below the angle he's trying to
-             * hit. If we do int division, we'll end up with one level lower
-             * than the one he was going for.
-             */
-            int deltaLevels = Math.round((float) totalDeltaAngle / mTickDelta); 
-            if (deltaLevels != 0) {
-                boolean canStillZoom = mCallback.onZoomRingThumbDragged(
-                        deltaLevels, mThumbDragStartAngle, touchAngle);
-                
-                if (mVibration) {
-                    // TODO: we're trying the haptics to see how it goes with
-                    // users, so we're ignoring the settings (for now)
-                    performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK,
-                            HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING |
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                }
-                
-                // Set the callback angle to the actual angle based on how many delta levels we gave
-                mPreviousCallbackTickAngle = getValidAngle(
-                        mPreviousCallbackTickAngle + (deltaLevels * mTickDelta));
-            }
-        }
-
-        if (DRAW_TRAIL) {
-            int deltaAngle = getDelta(mThumbAngle, touchAngle, useDirection, ccw);
-            mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER);
-        }
-            
-        if (animateThumbToNewAngle) {
-            if (useDirection) {
-                setThumbAngleAnimated(touchAngle, 0, ccw);
-            } else {
-                setThumbAngleAnimated(touchAngle, 0);                
-            }
-        } else {
-            setThumbAngleAuto(touchAngle, useDirection, ccw);
-        }
-    }
-//    private void onThumbDragged(int touchAngle, boolean useDirection, boolean ccw) {
-//        int deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw);
-//        
-//        if (!useDirection) {
-//            // Set ccw to match the direction found by getDelta
-//            ccw = deltaPrevCbAndTouch > 0;
-//            useDirection = true;            
-//        }
-//
-//        boolean animateThumbToNewAngle = false;
-//        boolean animationCcw = ccw;
-//
-//        if (deltaPrevCbAndTouch >= mFuzzyCallbackThreshold
-//                || deltaPrevCbAndTouch <= -mFuzzyCallbackThreshold) {
-//
-//            /*
-//             * When the user slides the thumb through the tick that corresponds
-//             * to a zoom bound, we don't want to abruptly stop there. Instead,
-//             * let the user slide it to the next tick, and then animate it back
-//             * to the original zoom bound tick. Because of this, we make sure
-//             * the delta from the bound is more than halfway to the next tick.
-//             * We make sure the bound is between the touch and the previous
-//             * callback to ensure we JUST passed the bound.
-//             */ 
-//            int oldTouchAngle = touchAngle;
-//            if (ccw && mThumbCcwBound != Integer.MIN_VALUE) {
-//                int deltaCcwBoundAndTouch =
-//                        getDelta(mThumbCcwBound, touchAngle, true, ccw);
-//                if (deltaCcwBoundAndTouch >= mCallbackThreshold / 2) {
-//                    // The touch has past far enough from the bound
-//                    int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle,
-//                            touchAngle, true, ccw);
-//                    if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) {
-//                        // The bound is between the previous callback angle and the touch
-//                        // Cap to the bound
-//                        touchAngle = mThumbCcwBound;
-//                        /*
-//                         * We're moving the touch BACK to the bound, so animate
-//                         * back in the opposite direction that passed the bound.
-//                         */
-//                        animationCcw = false;
-//                    }
-//                }
-//            } else if (!ccw && mThumbCwBound != Integer.MIN_VALUE) {
-//                // See block above for general comments
-//                int deltaCwBoundAndTouch =
-//                        getDelta(mThumbCwBound, touchAngle, true, ccw);
-//                if (deltaCwBoundAndTouch <= -mCallbackThreshold / 2) {
-//                    int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle,
-//                            touchAngle, true, ccw);
-//                    /*
-//                     * Both of these will be negative since we got delta in
-//                     * clockwise direction, and we want the magnitude of
-//                     * deltaPreviousCbAndTouch to be greater than the magnitude
-//                     * of deltaCwBoundAndTouch
-//                     */
-//                    if (deltaPreviousCbAndTouch <= deltaCwBoundAndTouch) {
-//                        touchAngle = mThumbCwBound;
-//                        animationCcw = true;
-//                    }
-//                }
-//            }
-//            if (touchAngle != oldTouchAngle) {
-//                // We bounded the touch angle
-//                deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, true, ccw);
-//                // Animate back to the bound
-//                animateThumbToNewAngle = true;
-//                // Disallow movement now
-//                setMode(MODE_IGNORE_UNTIL_UP);
-//            }
-//            
-//            
-//            /*
-//             * Prevent it from jumping too far (this could happen if the user
-//             * goes through the center)
-//             */
-//
-//            if (mEnforceMaxAbsJump) {
-//                if (deltaPrevCbAndTouch <= -MAX_ABS_JUMP_DELTA_ANGLE) {
-//                    deltaPrevCbAndTouch = -MAX_ABS_JUMP_DELTA_ANGLE;
-//                    animateThumbToNewAngle = true;
-//                } else if (deltaPrevCbAndTouch >= MAX_ABS_JUMP_DELTA_ANGLE) {
-//                    deltaPrevCbAndTouch = MAX_ABS_JUMP_DELTA_ANGLE;
-//                    animateThumbToNewAngle = true;
-//                }
-//            }
-//
-//            /*
-//             * We need to cover the edge case of a user grabbing the thumb,
-//             * going into the center of the widget, and then coming out from the
-//             * center to an angle that's slightly below the angle he's trying to
-//             * hit. If we do int division, we'll end up with one level lower
-//             * than the one he was going for.
-//             */
-//            int deltaLevels = Math.round((float) deltaPrevCbAndTouch / mCallbackThreshold); 
-//            if (deltaLevels != 0) {
-//                boolean canStillZoom = mCallback.onZoomRingThumbDragged(
-//                        deltaLevels, mThumbDragStartAngle, touchAngle);
-//                
-//                if (mVibration) {
-//                    // TODO: we're trying the haptics to see how it goes with
-//                    // users, so we're ignoring the settings (for now)
-//                    performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK,
-//                            HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING |
-//                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-//                
-//                }
-//                // Set the callback angle to the actual angle based on how many delta levels we gave
-//                mPreviousCallbackAngle = getValidAngle(
-//                        mPreviousCallbackAngle + (deltaLevels * mCallbackThreshold));
-//            }
-//        }
-//
-//        if (DRAW_TRAIL) {
-//            int deltaAngle = getDelta(mThumbAngle, touchAngle, true, ccw);
-//            mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER);
-//        }
-//            
-//        if (animateThumbToNewAngle) {
-//            setThumbAngleAnimated(touchAngle, 0, animationCcw);
-//        } else {
-//            /*
-//             * Use regular ccw here because animationCcw will never have been
-//             * changed if animateThumbToNewAngle is false
-//             */
-//            setThumbAngleAuto(touchAngle, true, ccw);
-//        }
-//    }
-    
-    private int getValidAngle(int invalidAngle) {
-        if (invalidAngle < 0) {
-            return (invalidAngle % TWO_PI_INT_MULTIPLIED) + TWO_PI_INT_MULTIPLIED;
-        } else if (invalidAngle >= TWO_PI_INT_MULTIPLIED) {
-            return invalidAngle % TWO_PI_INT_MULTIPLIED;
-        } else {
-            return invalidAngle;
-        }
-    }
-
-    private int getClosestTickAngle(int angle) {
-        int smallerAngleDistance = angle % mTickDelta;
-        int smallerAngle = angle - smallerAngleDistance;
-        if (smallerAngleDistance < mTickDelta / 2) {
-            // Closer to the smaller angle
-            return smallerAngle;
-        } else {
-            // Closer to the bigger angle (premodding)
-            return (smallerAngle + mTickDelta) % TWO_PI_INT_MULTIPLIED; 
-        }
-    }
-
-    private void onThumbDragStopped() {
-        mCallback.onZoomRingThumbDraggingStopped();
-    }
-
-    private void onZoomRingMoved(int rawX, int rawY) {
-        if (mPreviousWidgetDragX != Integer.MIN_VALUE) {
-            int deltaX = rawX - mPreviousWidgetDragX;
-            int deltaY = rawY - mPreviousWidgetDragY;
-
-            mCallback.onZoomRingMoved(deltaX, deltaY, rawX, rawY);
-        }
-
-        mPreviousWidgetDragX = rawX;
-        mPreviousWidgetDragY = rawY;
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-
-        if (!hasWindowFocus) {
-            mCallback.onZoomRingDismissed();
-        }
-    }
-    
-    private int getAngle(int localX, int localY) {
-        int radians = (int) (Math.atan2(localY, localX) * RADIAN_INT_MULTIPLIER);
-
-        // Convert from [-pi,pi] to {0,2pi]
-        if (radians < 0) {
-            radians = -radians;
-        } else if (radians > 0) {
-            radians = 2 * PI_INT_MULTIPLIED - radians;
-        } else {
-            radians = 0;
-        }
-
-        radians = radians - mZeroAngle;
-        return radians >= 0 ? radians : radians + 2 * PI_INT_MULTIPLIED;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mThumbVisible) {
-            if (DRAW_TRAIL) {
-                mTrail.draw(canvas);
-            }
-            if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) {
-                mZoomInArrowDrawable.draw(canvas);
-                mZoomInArrowHintDrawable.draw(canvas);
-            }
-            if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) {
-                mZoomOutArrowDrawable.draw(canvas);
-                mZoomOutArrowHintDrawable.draw(canvas);
-            }
-            mThumbDrawable.draw(canvas);
-        }
-    }
-    
-    private void setThumbArrowsAngle(int angle) {
-        int level = -angle * 10000 / ZoomRing.TWO_PI_INT_MULTIPLIED;
-        mZoomInArrowDrawable.setLevel(level);
-        mZoomOutArrowDrawable.setLevel(level);
-        
-        // Assume it is a square
-        int halfSideLength = mZoomInArrowHintDrawable.getIntrinsicHeight() / 2;
-        int unoffsetAngle = angle + mZeroAngle;
-        
-        int plusCenterX = (int) (Math.cos(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle)
-                / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX;
-        int plusCenterY = (int) (Math.sin(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle)
-                / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY;
-        mZoomInArrowHintDrawable.setBounds(plusCenterX - halfSideLength,
-                plusCenterY - halfSideLength,
-                plusCenterX + halfSideLength,
-                plusCenterY + halfSideLength);
-        
-        int minusCenterX = (int) (Math.cos(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle)
-                / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX;
-        int minusCenterY = (int) (Math.sin(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle)
-                / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY;
-        mZoomOutArrowHintDrawable.setBounds(minusCenterX - halfSideLength,
-                minusCenterY - halfSideLength,
-                minusCenterX + halfSideLength,
-                minusCenterY + halfSideLength);
-    }
-    
-    void setThumbArrowsVisible(boolean visible) {
-        if (visible) {
-            mThumbArrowsAlpha = 255;
-            int callbackAngle = mPreviousCallbackTickAngle;
-            if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR ||
-                    callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) {
-                mZoomInArrowDrawable.setAlpha(255);
-                mZoomInArrowHintDrawable.setAlpha(255);
-                mThumbArrowsToDraw |= THUMB_ARROW_PLUS;                
-            } else {
-                mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS;
-            }
-            if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR ||
-                    callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) {
-                mZoomOutArrowDrawable.setAlpha(255);
-                mZoomOutArrowHintDrawable.setAlpha(255);
-                mThumbArrowsToDraw |= THUMB_ARROW_MINUS;
-            } else {
-                mThumbArrowsToDraw &= ~THUMB_ARROW_MINUS;
-            }
-            invalidate();
-        } else if (mThumbArrowsAlpha == 255) {
-            // Only start fade if we're fully visible (otherwise another fade is happening already)
-            mThumbArrowsFadeStartTime = SystemClock.elapsedRealtime();
-            onThumbArrowsFadeStep();
-        }
-    }
-    
-    private void onThumbArrowsFadeStep() {
-        if (mThumbArrowsAlpha <= 0) {
-            mThumbArrowsToDraw = 0;
-            return;
-        }
-        
-        mThumbArrowsAlpha = (int)
-                (255 - (255 * (SystemClock.elapsedRealtime() - mThumbArrowsFadeStartTime)
-                        / THUMB_ARROWS_FADE_DURATION));
-        if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0;
-        if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) {
-            mZoomInArrowDrawable.setAlpha(mThumbArrowsAlpha);
-            mZoomInArrowHintDrawable.setAlpha(mThumbArrowsAlpha);
-            invalidateDrawable(mZoomInArrowHintDrawable);
-            invalidateDrawable(mZoomInArrowDrawable);
-        }
-        if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) {
-            mZoomOutArrowDrawable.setAlpha(mThumbArrowsAlpha);
-            mZoomOutArrowHintDrawable.setAlpha(mThumbArrowsAlpha);
-            invalidateDrawable(mZoomOutArrowHintDrawable);
-            invalidateDrawable(mZoomOutArrowDrawable);
-        }
-            
-        if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_STEP)) {
-            mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_STEP);
-        }
-    }
-    
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        
-        setThumbArrowsAngle(mThumbAngle);
-        setThumbArrowsVisible(true);
-    }
-
-    public interface OnZoomRingCallback {
-        void onZoomRingSetMovableHintVisible(boolean visible);
-        
-        void onZoomRingMovingStarted();
-        boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY);
-        void onZoomRingMovingStopped();
-        
-        void onZoomRingThumbDraggingStarted();
-        boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle);
-        void onZoomRingThumbDraggingStopped();
-        
-        void onZoomRingDismissed();
-        
-        void onUserInteractionStarted();
-        void onUserInteractionStopped();
-    }
-
-    private static void printAngle(String angleName, int angle) {
-        Log.d(TAG, angleName + ": " + (long) angle * 180 / PI_INT_MULTIPLIED);
-    }
-}
diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java
deleted file mode 100644
index 3bf3b22..0000000
--- a/core/java/android/widget/ZoomRingController.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * Copyright (C) 2009 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.widget;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.GestureDetector;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * A controller to simplify the use of the zoom ring widget.   
- * <p>
- * If you are using this with a custom View, please call
- * {@link #setVisible(boolean) setVisible(false)} from the
- * {@link View#onDetachedFromWindow}.
- *
- * @hide
- */
-public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
-        View.OnTouchListener, View.OnKeyListener {
-
-    // Temporary methods for different zoom types
-    static int getZoomType(Context context) {
-        return Settings.System.getInt(context.getContentResolver(), "zoom", 1);
-    }
-    public static boolean useOldZoom(Context context) {
-        return getZoomType(context) == 0;
-    }
-    private static boolean useThisZoom(Context context) {
-        return getZoomType(context) == 1;
-    }
-
-    /** The duration for the animation to re-center the zoom ring. */
-    private static final int RECENTERING_DURATION = 500;
-
-    /** The inactivity timeout for the zoom ring. */
-    private static final int INACTIVITY_TIMEOUT =
-            (int) ViewConfiguration.getZoomControlsTimeout();
-    
-    /**
-     * The delay when the user taps outside to dismiss the zoom ring. This is
-     * because the user can do a second-tap to recenter the owner view instead
-     * of dismissing the zoom ring.
-     */
-    private static final int OUTSIDE_TAP_DISMISS_DELAY =
-            ViewConfiguration.getDoubleTapTimeout() / 2;
-    
-    /**
-     * When the zoom ring is on the edge, this is the delay before we actually
-     * start panning the owner.
-     * @see #mInitiatePanGap
-     */
-    private static final int INITIATE_PAN_DELAY = 300;
-
-    /**
-     * While already panning, if the zoom ring remains this close to an edge,
-     * the owner will continue to be panned.
-     */
-    private int mPanGap;
-    
-    /** To begin a pan, the zoom ring must be this close to an edge. */
-    private int mInitiatePanGap;
-
-    /** Initialized from ViewConfiguration. */
-    private int mScaledTouchSlop;
-
-    /**
-     * The setting name that tracks whether we've shown the zoom ring toast.
-     */
-    private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast";
-
-    private Context mContext;
-    private WindowManager mWindowManager;
-
-    /**
-     * The view that is being zoomed by this zoom ring.
-     */
-    private View mOwnerView;
-
-    /**
-     * The bounds of the owner view in global coordinates. This is recalculated
-     * each time the zoom ring is shown.
-     */
-    private Rect mOwnerViewBounds = new Rect();
-
-    /**
-     * The container that is added as a window.
-     */
-    private FrameLayout mContainer;
-    private LayoutParams mContainerLayoutParams;
-
-    /**
-     * The view (or null) that should receive touch events. This will get set if
-     * the touch down hits the container. It will be reset on the touch up.
-     */
-    private View mTouchTargetView;
-    /**
-     * The {@link #mTouchTargetView}'s location in window, set on touch down.
-     */
-    private int[] mTouchTargetLocationInWindow = new int[2];
-    /**
-     * If the zoom ring is dismissed but the user is still in a touch
-     * interaction, we set this to true. This will ignore all touch events until
-     * up/cancel, and then set the owner's touch listener to null.
-     */
-    private boolean mReleaseTouchListenerOnUp;
-
-
-    /*
-     * Tap-drag is an interaction where the user first taps and then (quickly)
-     * does the clockwise or counter-clockwise drag. In reality, this is: (down,
-     * up, down, move in circles, up). This differs from the usual events of:
-     * (down, up, down, up, down, move in circles, up). While the only
-     * difference is the omission of an (up, down), for power-users this is a
-     * pretty big improvement as it now only requires them to focus on the
-     * screen once (for the first tap down) instead of twice (for the first tap
-     * down and then to grab the thumb).
-     */
-    /** The X where the tap-drag started. */
-    private int mTapDragStartX;
-    /** The Y where the tap-drag started. */
-    private int mTapDragStartY;
-
-    /** The controller is idle */
-    private static final int TOUCH_MODE_IDLE = 0;
-    /**
-     * In the middle of a second-tap interaction, waiting for either an up-touch
-     * or the user to start dragging to go into tap-drag mode.
-     */
-    private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2;
-    /** In the middle of a tap-drag. */
-    private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3;
-    private int mTouchMode;
-
-    /** Whether the zoom ring is visible. */
-    private boolean mIsZoomRingVisible;
-
-    private ZoomRing mZoomRing;
-    /** Cached width of the zoom ring. */
-    private int mZoomRingWidth;
-    /** Cached height of the zoom ring. */
-    private int mZoomRingHeight;
-
-    /** Invokes panning of owner view if the zoom ring is touching an edge. */
-    private Panner mPanner;
-    /** The time when the zoom ring first touched the edge. */
-    private long mTouchingEdgeStartTime;
-    /** Whether the user has already initiated the panning. */
-    private boolean mPanningInitiated;
-
-    /**
-     * When the finger moves the zoom ring to an edge, this is the horizontal
-     * accumulator for how much the finger has moved off of its original touch
-     * point on the zoom ring (OOB = out-of-bounds). If < 0, the finger has
-     * moved that many px to the left of its original touch point on the ring.
-     */
-    private int mMovingZoomRingOobX;
-    /** Vertical accumulator, see {@link #mMovingZoomRingOobX} */
-    private int mMovingZoomRingOobY;
-
-    /** Arrows that hint that the zoom ring is movable. */
-    private ImageView mPanningArrows;
-    /** The animation shown when the panning arrows are being shown. */
-    private Animation mPanningArrowsEnterAnimation;
-    /** The animation shown when the panning arrows are being hidden. */
-    private Animation mPanningArrowsExitAnimation;
-
-    /**
-     * Temporary rectangle, only use from the UI thread (and ideally don't rely
-     * on it being unused across many method calls.)
-     */
-    private Rect mTempRect = new Rect();
-
-    private OnZoomListener mCallback;
-
-    /**
-     * When the zoom ring is centered on screen, this will be the x value used
-     * for the container's layout params.
-     */
-    private int mCenteredContainerX = Integer.MIN_VALUE;
-
-    /**
-     * When the zoom ring is centered on screen, this will be the y value used
-     * for the container's layout params.
-     */
-    private int mCenteredContainerY = Integer.MIN_VALUE;
-
-    /**
-     * Scroller used to re-center the zoom ring if the user had dragged it to a
-     * corner and then double-taps any point on the owner view (the owner view
-     * will center the double-tapped point, but we should re-center the zoom
-     * ring).
-     * <p>
-     * The (x,y) of the scroller is the (x,y) of the container's layout params.
-     */
-    private Scroller mScroller;
-
-    /**
-     * When showing the zoom ring, we add the view as a new window. However,
-     * there is logic that needs to know the size of the zoom ring which is
-     * determined after it's laid out. Therefore, we must post this logic onto
-     * the UI thread so it will be exceuted AFTER the layout. This is the logic.
-     */
-    private Runnable mPostedVisibleInitializer;
-
-    /**
-     * Only touch from the main thread.
-     */
-    private static Dialog sTutorialDialog;
-    private static long sTutorialShowTime;
-    private static final int TUTORIAL_MIN_DISPLAY_TIME = 2000;
-
-    private IntentFilter mConfigurationChangedFilter =
-            new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
-
-    /** Listens for configuration changes so we can make sure we're still in a reasonable state. */
-    private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!mIsZoomRingVisible) return;
-
-            mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED);
-            mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED);
-        }
-    };
-
-    /** Keeps the scroller going (or starts it). */
-    private static final int MSG_SCROLLER_STEP = 1;
-    /** When configuration changes, this is called after the UI thread is idle. */
-    private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
-    /** Used to delay the zoom ring dismissal. */
-    private static final int MSG_DISMISS_ZOOM_RING = 3;
-
-    /**
-     * If setVisible(true) is called and the owner view's window token is null,
-     * we delay the setVisible(true) call until it is not null.
-     */
-    private static final int MSG_POST_SET_VISIBLE = 4;
-    
-    private Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SCROLLER_STEP:
-                    onScrollerStep();
-                    break;
-
-                case MSG_POST_CONFIGURATION_CHANGED:
-                    onPostConfigurationChanged();
-                    break;
-
-                case MSG_DISMISS_ZOOM_RING:
-                    setVisible(false);
-                    break;
-                    
-                case MSG_POST_SET_VISIBLE:
-                    if (mOwnerView.getWindowToken() == null) {
-                        // Doh, it is still null, throw an exception
-                        throw new IllegalArgumentException(
-                                "Cannot make the zoom ring visible if the owner view is " +
-                                "not attached to a window.");
-                    }
-                    setVisible(true);
-                    break;
-            }
-
-        }
-    };
-
-    public ZoomRingController(Context context, View ownerView) {
-        mContext = context;
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-
-        mPanner = new Panner();
-        mOwnerView = ownerView;
-
-        mZoomRing = new ZoomRing(context);
-        mZoomRing.setId(com.android.internal.R.id.zoomControls);
-        mZoomRing.setLayoutParams(new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                Gravity.CENTER));
-        mZoomRing.setCallback(this);
-
-        createPanningArrows();
-
-        mContainerLayoutParams = new LayoutParams();
-        mContainerLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
-        mContainerLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
-                LayoutParams.FLAG_NOT_FOCUSABLE |
-                LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-        mContainerLayoutParams.height = LayoutParams.WRAP_CONTENT;
-        mContainerLayoutParams.width = LayoutParams.WRAP_CONTENT;
-        mContainerLayoutParams.type = LayoutParams.TYPE_APPLICATION_PANEL;
-        mContainerLayoutParams.format = PixelFormat.TRANSPARENT;
-        mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_ZoomRing;
-
-        mContainer = new FrameLayout(context);
-        mContainer.setLayoutParams(mContainerLayoutParams);
-        mContainer.setMeasureAllChildren(true);
-
-        mContainer.addView(mZoomRing);
-        mContainer.addView(mPanningArrows);
-
-        mScroller = new Scroller(context, new DecelerateInterpolator());
-
-        ViewConfiguration vc = ViewConfiguration.get(context);
-        mScaledTouchSlop = vc.getScaledTouchSlop();
-        
-        float density = context.getResources().getDisplayMetrics().density;
-        mPanGap = (int) (20 * density);
-        mInitiatePanGap = (int) (10 * density);
-    }
-
-    private void createPanningArrows() {
-        mPanningArrows = new ImageView(mContext);
-        mPanningArrows.setImageDrawable(mZoomRing.getPanningArrowsDrawable());
-        mPanningArrows.setLayoutParams(new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                Gravity.CENTER));
-        mPanningArrows.setVisibility(View.INVISIBLE);
-
-        mPanningArrowsEnterAnimation = AnimationUtils.loadAnimation(mContext,
-                com.android.internal.R.anim.fade_in);
-        mPanningArrowsExitAnimation = AnimationUtils.loadAnimation(mContext,
-                com.android.internal.R.anim.fade_out);
-    }
-
-    /**
-     * Sets the angle (in radians) between ticks. This is also the angle a user
-     * must move the thumb in order for the client to get a callback. Once there
-     * is a callback, the accumulator resets. For example, if you set this to
-     * PI/6, it will give a callback every time the user moves PI/6 amount on
-     * the ring.
-     * 
-     * @param angle The angle for the callback threshold, in radians
-     */
-    public void setTickDelta(float angle) {
-        mZoomRing.setTickDelta((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER));
-    }
-
-    /**
-     * Sets a drawable for the zoom ring track.
-     *
-     * @param drawable The drawable to use for the track.
-     * @hide Need a better way of doing this, but this one-off for browser so it
-     *       can have its final look for the usability study
-     */
-    public void setTrackDrawable(int drawable) {
-        mZoomRing.setBackgroundResource(drawable);
-    }
-    
-    /**
-     * Sets the callback for the zoom ring controller.
-     * 
-     * @param callback The callback.
-     */
-    public void setCallback(OnZoomListener callback) {
-        mCallback = callback;
-    }
-
-    public void setVibration(boolean vibrate) {
-        mZoomRing.setVibration(vibrate);
-    }
-    
-    public void setThumbAngle(float angle) {
-        mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER));
-    }
-
-    public void setThumbAngleAnimated(float angle) {
-        mZoomRing.setThumbAngleAnimated((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER), 0);
-    }
-
-    public void setThumbVisible(boolean thumbVisible) {
-        mZoomRing.setThumbVisible(thumbVisible);
-    }
-    
-    public void setThumbClockwiseBound(float angle) {
-        mZoomRing.setThumbClockwiseBound(angle >= 0 ?
-                (int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER) :
-                Integer.MIN_VALUE);
-    }
-
-    public void setThumbCounterclockwiseBound(float angle) {
-        mZoomRing.setThumbCounterclockwiseBound(angle >= 0 ?
-                (int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER) :
-                Integer.MIN_VALUE);
-    }
-
-    public boolean isVisible() {
-        return mIsZoomRingVisible;
-    }
-
-    public void setVisible(boolean visible) {
-
-        if (!useThisZoom(mContext)) return;
-        
-        if (visible) {
-            if (mOwnerView.getWindowToken() == null) {
-                /*
-                 * We need a window token to show ourselves, maybe the owner's
-                 * window hasn't been created yet but it will have been by the
-                 * time the looper is idle, so post the setVisible(true) call.
-                 */
-                if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) {
-                    mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE);
-                }
-                return;
-            }
-            
-            dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
-        } else {
-            mPanner.stop();
-        }
-
-        if (mIsZoomRingVisible == visible) {
-            return;
-        }
-        mIsZoomRingVisible = visible;
-
-        if (visible) {
-            if (mContainerLayoutParams.token == null) {
-                mContainerLayoutParams.token = mOwnerView.getWindowToken();
-            }
-
-            mWindowManager.addView(mContainer, mContainerLayoutParams);
-
-            if (mPostedVisibleInitializer == null) {
-                mPostedVisibleInitializer = new Runnable() {
-                    public void run() {
-                        refreshPositioningVariables();
-                        resetZoomRing();
-                        refreshContainerLayout();
-
-                        if (mCallback != null) {
-                            mCallback.onVisibilityChanged(true);
-                        }
-                    }
-                };
-            }
-
-            mPanningArrows.setAnimation(null);
-
-            mHandler.post(mPostedVisibleInitializer);
-
-            // Handle configuration changes when visible
-            mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter);
-
-            // Steal key/touches events from the owner
-            mOwnerView.setOnKeyListener(this);
-            mOwnerView.setOnTouchListener(this);
-            mReleaseTouchListenerOnUp = false;
-
-        } else {
-            // Don't want to steal any more keys/touches
-            mOwnerView.setOnKeyListener(null);
-            if (mTouchTargetView != null) {
-                // We are still stealing the touch events for this touch
-                // sequence, so release the touch listener later
-                mReleaseTouchListenerOnUp = true;
-            } else {
-                mOwnerView.setOnTouchListener(null);
-            }
-
-            // No longer care about configuration changes
-            mContext.unregisterReceiver(mConfigurationChangedReceiver);
-
-            mWindowManager.removeView(mContainer);
-            mHandler.removeCallbacks(mPostedVisibleInitializer);
-
-            if (mCallback != null) {
-                mCallback.onVisibilityChanged(false);
-            }
-        }
-
-    }
-
-    private void refreshContainerLayout() {
-        if (mIsZoomRingVisible) {
-            mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
-        }
-    }
-
-    /**
-     * Returns the container of the zoom ring widget. The client can add views
-     * here to be shown alongside the zoom ring.  See {@link #getZoomRingId()}.
-     * <p>
-     * Notes:
-     * <ul>
-     * <li> The controller dispatches touch events differently than the regular view
-     * framework.
-     * <li> Please ensure you set your view to INVISIBLE not GONE when hiding it.
-     * </ul>
-     * 
-     * @return The layout used by the container.
-     */
-    public FrameLayout getContainer() {
-        return mContainer;
-    }
-
-    /**
-     * Returns the id of the zoom ring widget.
-     * 
-     * @return The id of the zoom ring widget.
-     */
-    public int getZoomRingId() {
-        return mZoomRing.getId();
-    }
-
-    private void dismissZoomRingDelayed(int delay) {
-        mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
-        mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay);
-    }
-
-    private void resetZoomRing() {
-        mScroller.abortAnimation();
-
-        mContainerLayoutParams.x = mCenteredContainerX;
-        mContainerLayoutParams.y = mCenteredContainerY;
-
-        // Reset the thumb
-        mZoomRing.resetThumbAngle();
-    }
-
-    /**
-     * Should be called by the client for each event belonging to the second tap
-     * (the down, move, up, and cancel events).
-     * <p>
-     * In most cases, the client can use a {@link GestureDetector} and forward events from
-     * {@link GestureDetector.OnDoubleTapListener#onDoubleTapEvent(MotionEvent)}.
-     * 
-     * @param event The event belonging to the second tap.
-     * @return Whether the event was consumed.
-     */
-    public boolean handleDoubleTapEvent(MotionEvent event) {
-        if (!useThisZoom(mContext)) return false;
-        
-        int action = event.getAction();
-
-        // TODO: make sure this works well with the
-        // ownerView.setOnTouchListener(this) instead of window receiving
-        // touches
-        if (action == MotionEvent.ACTION_DOWN) {
-            mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT;
-            int x = (int) event.getX();
-            int y = (int) event.getY();
-
-            refreshPositioningVariables();
-            setVisible(true);
-            centerPoint(x, y);
-            ensureZoomRingIsCentered();
-
-            // Tap drag mode stuff
-            mTapDragStartX = x;
-            mTapDragStartY = y;
-
-        } else if (action == MotionEvent.ACTION_CANCEL) {
-            mTouchMode = TOUCH_MODE_IDLE;
-
-        } else { // action is move or up
-            switch (mTouchMode) {
-                case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: {
-                    switch (action) {
-                        case MotionEvent.ACTION_MOVE:
-                            int x = (int) event.getX();
-                            int y = (int) event.getY();
-                            if (Math.abs(x - mTapDragStartX) > mScaledTouchSlop ||
-                                    Math.abs(y - mTapDragStartY) >
-                                    mScaledTouchSlop) {
-                                mZoomRing.setTapDragMode(true, x, y);
-                                mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG;
-                                setTouchTargetView(mZoomRing);
-                            }
-                            return true;
-
-                        case MotionEvent.ACTION_UP:
-                            mTouchMode = TOUCH_MODE_IDLE;
-                            break;
-                    }
-                    break;
-                }
-
-                case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: {
-                    switch (action) {
-                        case MotionEvent.ACTION_MOVE:
-                            giveTouchToZoomRing(event);
-                            return true;
-
-                        case MotionEvent.ACTION_UP:
-                            mTouchMode = TOUCH_MODE_IDLE;
-
-                            /*
-                             * This is a power-user feature that only shows the
-                             * zoom while the user is performing the tap-drag.
-                             * That means once it is released, the zoom ring
-                             * should disappear.
-                             */
-                            mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY());
-                            dismissZoomRingDelayed(0);
-                            break;
-                    }
-                    break;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    private void ensureZoomRingIsCentered() {
-        LayoutParams lp = mContainerLayoutParams;
-
-        if (lp.x != mCenteredContainerX || lp.y != mCenteredContainerY) {
-            int width = mContainer.getWidth();
-            int height = mContainer.getHeight();
-            mScroller.startScroll(lp.x, lp.y, mCenteredContainerX - lp.x,
-                    mCenteredContainerY - lp.y, RECENTERING_DURATION);
-            mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
-        }
-    }
-
-    private void refreshPositioningVariables() {
-        mZoomRingWidth = mZoomRing.getWidth();
-        mZoomRingHeight = mZoomRing.getHeight();
-
-        // Calculate the owner view's bounds
-        mOwnerView.getGlobalVisibleRect(mOwnerViewBounds);
-
-        // Get the center
-        Gravity.apply(Gravity.CENTER, mContainer.getWidth(), mContainer.getHeight(),
-                mOwnerViewBounds, mTempRect);
-        mCenteredContainerX = mTempRect.left;
-        mCenteredContainerY = mTempRect.top;
-    }
-
-    /**
-     * Centers the point (in owner view's coordinates).
-     */
-    private void centerPoint(int x, int y) {
-        if (mCallback != null) {
-            mCallback.onCenter(x, y);
-        }
-    }
-
-    private void giveTouchToZoomRing(MotionEvent event) {
-        int rawX = (int) event.getRawX();
-        int rawY = (int) event.getRawY();
-        int x = rawX - mContainerLayoutParams.x - mZoomRing.getLeft();
-        int y = rawY - mContainerLayoutParams.y - mZoomRing.getTop();
-        mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY);
-    }
-
-    /** @hide */
-    public void onZoomRingSetMovableHintVisible(boolean visible) {
-        setPanningArrowsVisible(visible);
-    }
-
-    /** @hide */
-    public void onUserInteractionStarted() {
-        mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
-    }
-
-    /** @hide */
-    public void onUserInteractionStopped() {
-        dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
-    }
-
-    /** @hide */
-    public void onZoomRingMovingStarted() {
-        mScroller.abortAnimation();
-        mTouchingEdgeStartTime = 0;
-        mMovingZoomRingOobX = 0;
-        mMovingZoomRingOobY = 0;
-        if (mCallback != null) {
-            mCallback.onBeginPan();
-        }
-    }
-
-    private void setPanningArrowsVisible(boolean visible) {
-        mPanningArrows.startAnimation(visible ? mPanningArrowsEnterAnimation
-                : mPanningArrowsExitAnimation);
-        mPanningArrows.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-    }
-
-    /** @hide */
-    public boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY) {
-
-        if (mMovingZoomRingOobX != 0) {
-            /*
-             * The finger has moved off the point where it originally touched
-             * the zidget.
-             */
-            boolean wasOobLeft = mMovingZoomRingOobX < 0;
-            mMovingZoomRingOobX += deltaX;
-            if ((wasOobLeft && mMovingZoomRingOobX > 0) ||
-                    (!wasOobLeft && mMovingZoomRingOobX < 0)) {
-                /*
-                 * Woot, the finger is back on the original point. Infact, it
-                 * went PAST its original point, so take the amount it passed
-                 * and use that as the delta to move the zoom ring.
-                 */ 
-                deltaX = mMovingZoomRingOobX;
-                // No longer out-of-bounds, reset
-                mMovingZoomRingOobX = 0;
-            } else {
-                // The finger is still not back, eat this movement
-                deltaX = 0;
-            }
-        }
-        
-        if (mMovingZoomRingOobY != 0) {
-            // See above for comments
-            boolean wasOobUp = mMovingZoomRingOobY < 0;
-            mMovingZoomRingOobY += deltaY;
-            if ((wasOobUp && mMovingZoomRingOobY > 0) || (!wasOobUp && mMovingZoomRingOobY < 0)) {
-                deltaY = mMovingZoomRingOobY;
-                mMovingZoomRingOobY = 0;
-            } else {
-                deltaY = 0;
-            }
-        }
-        
-        WindowManager.LayoutParams lp = mContainerLayoutParams;
-        Rect ownerBounds = mOwnerViewBounds;
-
-        int zoomRingLeft = mZoomRing.getLeft();
-        int zoomRingTop = mZoomRing.getTop();
-
-        int newX = lp.x + deltaX;
-        int newZoomRingX = newX + zoomRingLeft;
-        newZoomRingX = (newZoomRingX <= ownerBounds.left) ? ownerBounds.left :
-                (newZoomRingX + mZoomRingWidth > ownerBounds.right) ?
-                        ownerBounds.right - mZoomRingWidth : newZoomRingX;
-        lp.x = newZoomRingX - zoomRingLeft;
-
-        int newY = lp.y + deltaY;
-        int newZoomRingY = newY + zoomRingTop;
-        newZoomRingY = (newZoomRingY <= ownerBounds.top) ? ownerBounds.top :
-            (newZoomRingY + mZoomRingHeight > ownerBounds.bottom) ?
-                    ownerBounds.bottom - mZoomRingHeight : newZoomRingY;
-        lp.y = newZoomRingY - zoomRingTop;
-
-        refreshContainerLayout();
-        
-        // Check for pan
-        boolean horizontalPanning = true;
-        int leftGap = newZoomRingX - ownerBounds.left;
-        if (leftGap < mPanGap) {
-            if (leftGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
-                // Future moves in this direction should be accumulated in mMovingZoomRingOobX
-                mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
-            }
-            if (shouldPan(leftGap)) {
-                mPanner.setHorizontalStrength(-getStrengthFromGap(leftGap));
-            }
-        } else {
-            int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft);
-            if (rightGap < mPanGap) {
-                if (rightGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
-                    mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
-                }
-                if (shouldPan(rightGap)) {
-                    mPanner.setHorizontalStrength(getStrengthFromGap(rightGap));
-                }
-            } else {
-                mPanner.setHorizontalStrength(0);
-                horizontalPanning = false;
-            }
-        }
-
-        int topGap = newZoomRingY - ownerBounds.top;
-        if (topGap < mPanGap) {
-            if (topGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
-                mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
-            }
-            if (shouldPan(topGap)) {
-                mPanner.setVerticalStrength(-getStrengthFromGap(topGap));
-            }
-        } else {
-            int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop);
-            if (bottomGap < mPanGap) {
-                if (bottomGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
-                    mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
-                }
-                if (shouldPan(bottomGap)) {
-                    mPanner.setVerticalStrength(getStrengthFromGap(bottomGap));
-                }
-            } else {
-                mPanner.setVerticalStrength(0);
-                if (!horizontalPanning) {
-                    // Neither are panning, reset any timer to start pan mode
-                    mTouchingEdgeStartTime = 0;
-                    mPanningInitiated = false;
-                    mPanner.stop();
-                }
-            }
-        }
-
-        return true;
-    }
-
-    private boolean shouldPan(int gap) {
-        if (mPanningInitiated) return true;
-
-        if (gap < mInitiatePanGap) {
-            long time = SystemClock.elapsedRealtime();
-            if (mTouchingEdgeStartTime != 0 &&
-                    mTouchingEdgeStartTime + INITIATE_PAN_DELAY < time) {
-                mPanningInitiated = true;
-                return true;
-            } else if (mTouchingEdgeStartTime == 0) {
-                mTouchingEdgeStartTime = time;
-            } else {
-            }
-        } else {
-            // Moved away from the initiate pan gap, so reset the timer
-            mTouchingEdgeStartTime = 0;
-        }
-        return false;
-    }
-
-    /** @hide */
-    public void onZoomRingMovingStopped() {
-        mPanner.stop();
-        setPanningArrowsVisible(false);
-        if (mCallback != null) {
-            mCallback.onEndPan();
-        }
-    }
-
-    private int getStrengthFromGap(int gap) {
-        return gap > mPanGap ? 0 :
-            (mPanGap - gap) * 100 / mPanGap;
-    }
-
-    /** @hide */
-    public void onZoomRingThumbDraggingStarted() {
-        if (mCallback != null) {
-            mCallback.onBeginDrag();
-        }
-    }
-
-    /** @hide */
-    public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) {
-        if (mCallback != null) {
-            int deltaZoomLevel = -numLevels;
-
-            return mCallback.onDragZoom(deltaZoomLevel,
-                    getZoomRingCenterXInOwnerCoordinates(),
-                    getZoomRingCenterYInOwnerCoordinates(),
-                    (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER,
-                    (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER);
-        }
-
-        return false;
-    }
-
-    private int getZoomRingCenterXInOwnerCoordinates() {
-        int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() +
-                mZoomRingWidth / 2;
-        return globalZoomCenterX - mOwnerViewBounds.left;
-    }
-
-    private int getZoomRingCenterYInOwnerCoordinates() {
-        int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() +
-                mZoomRingHeight / 2;
-        return globalZoomCenterY - mOwnerViewBounds.top;
-    }
-
-    /** @hide */
-    public void onZoomRingThumbDraggingStopped() {
-        if (mCallback != null) {
-            mCallback.onEndDrag();
-        }
-    }
-
-    /** @hide */
-    public void onZoomRingDismissed() {
-        mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
-        setVisible(false);
-    }
-
-    /** @hide */
-    public void onRingDown(int tickAngle, int touchAngle) {
-    }
-
-    /** @hide */
-    public boolean onTouch(View v, MotionEvent event) {
-        if (sTutorialDialog != null && sTutorialDialog.isShowing() &&
-                SystemClock.elapsedRealtime() - sTutorialShowTime >= TUTORIAL_MIN_DISPLAY_TIME) {
-            finishZoomTutorial();
-        }
-
-        int action = event.getAction();
-
-        if (mReleaseTouchListenerOnUp) {
-            // The ring was dismissed but we need to throw away all events until the up
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-                mOwnerView.setOnTouchListener(null);
-                setTouchTargetView(null);
-                mReleaseTouchListenerOnUp = false;
-            }
-
-            // Eat this event
-            return true;
-        }
-
-        View targetView = mTouchTargetView;
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY());
-                setTouchTargetView(targetView);
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                setTouchTargetView(null);
-                break;
-        }
-
-        if (targetView != null) {
-            // The upperleft corner of the target view in raw coordinates
-            int targetViewRawX = mContainerLayoutParams.x + mTouchTargetLocationInWindow[0];
-            int targetViewRawY = mContainerLayoutParams.y + mTouchTargetLocationInWindow[1];
-
-            MotionEvent containerEvent = MotionEvent.obtain(event);
-            // Convert the motion event into the target view's coordinates (from
-            // owner view's coordinates)
-            containerEvent.offsetLocation(mOwnerViewBounds.left - targetViewRawX,
-                    mOwnerViewBounds.top - targetViewRawY);
-            boolean retValue = targetView.dispatchTouchEvent(containerEvent);
-            containerEvent.recycle();
-            return retValue;
-
-        } else {
-//            dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
-            if (action == MotionEvent.ACTION_DOWN) {
-                dismissZoomRingDelayed(OUTSIDE_TAP_DISMISS_DELAY);
-            }
-
-            return false;
-        }
-    }
-
-    private void setTouchTargetView(View view) {
-        mTouchTargetView = view;
-        if (view != null) {
-            view.getLocationInWindow(mTouchTargetLocationInWindow);
-        }
-    }
-
-    /**
-     * Returns the View that should receive a touch at the given coordinates.
-     *
-     * @param rawX The raw X.
-     * @param rawY The raw Y.
-     * @return The view that should receive the touches, or null if there is not one.
-     */
-    private View getViewForTouch(int rawX, int rawY) {
-        // Check to see if it is touching the ring
-        int containerCenterX = mContainerLayoutParams.x + mContainer.getWidth() / 2;
-        int containerCenterY = mContainerLayoutParams.y + mContainer.getHeight() / 2;
-        int distanceFromCenterX = rawX - containerCenterX;
-        int distanceFromCenterY = rawY - containerCenterY;
-        int zoomRingRadius = mZoomRing.getTrackOuterRadius();
-        if (distanceFromCenterX * distanceFromCenterX +
-                distanceFromCenterY * distanceFromCenterY <=
-                zoomRingRadius * zoomRingRadius) {
-            return mZoomRing;
-        }
-
-        // Check to see if it is touching any other clickable View.
-        // Reverse order so the child drawn on top gets first dibs.
-        int containerCoordsX = rawX - mContainerLayoutParams.x;
-        int containerCoordsY = rawY - mContainerLayoutParams.y;
-        Rect frame = mTempRect;
-        for (int i = mContainer.getChildCount() - 1; i >= 0; i--) {
-            View child = mContainer.getChildAt(i);
-            if (child == mZoomRing || child.getVisibility() != View.VISIBLE ||
-                    !child.isClickable()) {
-                continue;
-            }
-
-            child.getHitRect(frame);
-            if (frame.contains(containerCoordsX, containerCoordsY)) {
-                return child;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Steals key events from the owner view.
-     * 
-     * @hide
-     */
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                // Eat these
-                return true;
-
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                // Keep the zoom alive a little longer
-                dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
-                // They started zooming, hide the thumb arrows
-                mZoomRing.setThumbArrowsVisible(false);
-
-                if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) {
-                    mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP,
-                            getZoomRingCenterXInOwnerCoordinates(),
-                            getZoomRingCenterYInOwnerCoordinates());
-                }
-
-                return true;
-        }
-
-        return false;
-    }
-
-    private void onScrollerStep() {
-        if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return;
-
-        mContainerLayoutParams.x = mScroller.getCurrX();
-        mContainerLayoutParams.y = mScroller.getCurrY();
-        refreshContainerLayout();
-
-        mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
-    }
-
-    private void onPostConfigurationChanged() {
-        dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
-        refreshPositioningVariables();
-        ensureZoomRingIsCentered();
-    }
-
-    /*
-     * This is static so Activities can call this instead of the Views
-     * (Activities usually do not have a reference to the ZoomRingController
-     * instance.)
-     */
-    /**
-     * Shows a "tutorial" (some text) to the user teaching her the new zoom
-     * invocation method. Must call from the main thread.
-     * <p>
-     * It checks the global system setting to ensure this has not been seen
-     * before. Furthermore, if the application does not have privilege to write
-     * to the system settings, it will store this bit locally in a shared
-     * preference.
-     *
-     * @hide This should only be used by our main apps--browser, maps, and
-     *       gallery
-     */
-    public static void showZoomTutorialOnce(Context context) {
-        ContentResolver cr = context.getContentResolver();
-        if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TOAST, 0) == 1) {
-            return;
-        }
-
-        SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
-        if (sp.getInt(SETTING_NAME_SHOWN_TOAST, 0) == 1) {
-            return;
-        }
-
-        if (sTutorialDialog != null && sTutorialDialog.isShowing()) {
-            sTutorialDialog.dismiss();
-        }
-
-        LayoutInflater layoutInflater =
-                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        TextView textView = (TextView) layoutInflater.inflate(
-                com.android.internal.R.layout.alert_dialog_simple_text, null)
-                .findViewById(android.R.id.text1);
-        textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short);
-        
-        sTutorialDialog = new AlertDialog.Builder(context)
-                .setView(textView)
-                .setIcon(0)
-                .create();
-
-        Window window = sTutorialDialog.getWindow();
-        window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND |
-                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
-        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
-                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-
-        sTutorialDialog.show();
-        sTutorialShowTime = SystemClock.elapsedRealtime();
-    }
-
-    /** @hide Should only be used by Android platform apps */
-    public static void finishZoomTutorial(Context context, boolean userNotified) {
-        if (sTutorialDialog == null) return;
-
-        sTutorialDialog.dismiss();
-        sTutorialDialog = null;
-
-        // Record that they have seen the tutorial
-        if (userNotified) {
-            try {
-                Settings.System.putInt(context.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1);
-            } catch (SecurityException e) {
-                /*
-                 * The app does not have permission to clear this global flag, make
-                 * sure the user does not see the message when he comes back to this
-                 * same app at least.
-                 */
-                SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
-                sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit();
-            }
-        }
-    }
-
-    /** @hide Should only be used by Android platform apps */
-    public void finishZoomTutorial() {
-        finishZoomTutorial(mContext, true);
-    }
-
-    /**
-     * Sets the initial velocity of a pan.
-     * 
-     * @param startVelocity The initial velocity to move the owner view, in
-     *            pixels per second.
-     */
-    public void setPannerStartVelocity(float startVelocity) {
-        mPanner.mStartVelocity = startVelocity;
-    }
-
-    /**
-     * Sets the accelartion of the pan.
-     *
-     * @param acceleration The acceleration, in pixels per second squared.
-     */
-    public void setPannerAcceleration(float acceleration) {
-        mPanner.mAcceleration = acceleration;
-    }
-
-    /**
-     * Sets the maximum velocity of a pan.
-     * 
-     * @param maxVelocity The max velocity to move the owner view, in pixels per
-     *            second.
-     */
-    public void setPannerMaxVelocity(float maxVelocity) {
-        mPanner.mMaxVelocity = maxVelocity;
-    }
-
-    /**
-     * Sets the duration before acceleration will be applied.
-     * 
-     * @param duration The duration, in milliseconds.
-     */
-    public void setPannerStartAcceleratingDuration(int duration) {
-        mPanner.mStartAcceleratingDuration = duration;
-    }
-
-    private class Panner implements Runnable {
-        private static final int RUN_DELAY = 15;
-        private static final float STOP_SLOWDOWN = 0.8f;
-
-        private final Handler mUiHandler = new Handler();
-
-        private int mVerticalStrength;
-        private int mHorizontalStrength;
-
-        private boolean mStopping;
-
-        /** The time this current pan started. */
-        private long mStartTime;
-
-        /** The time of the last callback to pan the map/browser/etc. */
-        private long mPreviousCallbackTime;
-
-        // TODO Adjust to be DPI safe
-        private float mStartVelocity = 135;
-        private float mAcceleration = 160;
-        private float mMaxVelocity = 1000;
-        private int mStartAcceleratingDuration = 700;
-        private float mVelocity;
-
-        /** -100 (full left) to 0 (none) to 100 (full right) */
-        public void setHorizontalStrength(int horizontalStrength) {
-            if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) {
-                start();
-            } else if (mVerticalStrength == 0 && horizontalStrength == 0) {
-                stop();
-            }
-
-            mHorizontalStrength = horizontalStrength;
-            mStopping = false;
-        }
-
-        /** -100 (full up) to 0 (none) to 100 (full down) */
-        public void setVerticalStrength(int verticalStrength) {
-            if (mHorizontalStrength == 0 && mVerticalStrength == 0 && verticalStrength != 0) {
-                start();
-            } else if (mHorizontalStrength == 0 && verticalStrength == 0) {
-                stop();
-            }
-
-            mVerticalStrength = verticalStrength;
-            mStopping = false;
-        }
-
-        private void start() {
-            mUiHandler.post(this);
-            mPreviousCallbackTime = 0;
-            mStartTime = 0;
-        }
-
-        public void stop() {
-            mStopping = true;
-        }
-
-        public void run() {
-            if (mStopping) {
-                mHorizontalStrength *= STOP_SLOWDOWN;
-                mVerticalStrength *= STOP_SLOWDOWN;
-            }
-
-            if (mHorizontalStrength == 0 && mVerticalStrength == 0) {
-                return;
-            }
-
-            boolean firstRun = mPreviousCallbackTime == 0;
-            long curTime = SystemClock.elapsedRealtime();
-            int panAmount = getPanAmount(mPreviousCallbackTime, curTime);
-            mPreviousCallbackTime = curTime;
-
-            if (firstRun) {
-                mStartTime = curTime;
-                mVelocity = mStartVelocity;
-            } else {
-                int panX = panAmount * mHorizontalStrength / 100;
-                int panY = panAmount * mVerticalStrength / 100;
-
-                if (mCallback != null) {
-                    mCallback.onPan(panX, panY);
-                }
-            }
-
-            mUiHandler.postDelayed(this, RUN_DELAY);
-        }
-
-        private int getPanAmount(long previousTime, long currentTime) {
-            if (mVelocity > mMaxVelocity) {
-                mVelocity = mMaxVelocity;
-            } else if (mVelocity < mMaxVelocity) {
-                // See if it's time to add in some acceleration
-                if (currentTime - mStartTime > mStartAcceleratingDuration) {
-                    mVelocity += (currentTime - previousTime) * mAcceleration / 1000;
-                }
-            }
-
-            return (int) ((currentTime - previousTime) * mVelocity) / 1000;
-        }
-
-    }
-
-    /**
-     * Interface used to inform the client of zoom events that the user
-     * triggers.
-     */
-    public interface OnZoomListener {
-        /**
-         * Called when the user begins dragging the thumb on the zoom ring.
-         */
-        void onBeginDrag();
-
-        /**
-         * Called when the user drags the thumb and passes a tick causing a
-         * zoom.
-         * 
-         * @param deltaZoomLevel The number of levels to be zoomed. Positive to
-         *            zoom in, negative to zoom out.
-         * @param centerX The point about which to zoom. The zoom should pin
-         *            this point, leaving it at the same coordinate. This is
-         *            relative to the owner view's upper-left.
-         * @param centerY The point about which to zoom. The zoom should pin
-         *            this point, leaving it at the same coordinate. This is
-         *            relative to the owner view's upper-left.
-         * @param startAngle The angle where the user started dragging the thumb.
-         * @param curAngle The current angle of the thumb.
-         * @return Whether the owner was zoomed.
-         */
-        boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle,
-                float curAngle);
-        
-        /**
-         * Called when the user releases the thumb.
-         */
-        void onEndDrag();
-
-        /**
-         * Called when the user zooms via some other mechanism, for example
-         * arrow keys or a trackball.
-         * 
-         * @param zoomIn Whether to zoom in (true) or out (false).
-         * @param centerX See {@link #onDragZoom(int, int, int, float, float)}.
-         * @param centerY See {@link #onDragZoom(int, int, int, float, float)}.
-         */
-        void onSimpleZoom(boolean zoomIn, int centerX, int centerY);
-
-        /**
-         * Called when the user begins moving the zoom ring in order to pan the
-         * owner.
-         */
-        void onBeginPan();
-        
-        /**
-         * Called when the owner should pan as a result of the user moving the zoom ring.
-         * 
-         * @param deltaX The amount to pan horizontally.
-         * @param deltaY The amount to pan vertically.
-         * @return Whether the owner was panned.
-         */
-        boolean onPan(int deltaX, int deltaY);
-        
-        /**
-         * Called when the user releases the zoom ring.
-         */
-        void onEndPan();
-        
-        /**
-         * Called when the client should center the owner on the given point.
-         * 
-         * @param x The x to center on, relative to the owner view's upper-left.
-         * @param y The y to center on, relative to the owner view's upper-left.
-         */
-        void onCenter(int x, int y);
-        
-        /**
-         * Called when the zoom ring's visibility changes.
-         * 
-         * @param visible Whether the zoom ring is visible (true) or not (false).
-         */
-        void onVisibilityChanged(boolean visible);
-    }
-}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9030a3e..adec0a7 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,7 +43,7 @@
             in ResultReceiver resultReceiver);
     boolean hideSoftInput(in IInputMethodClient client, int flags,
             in ResultReceiver resultReceiver);
-    void windowGainedFocus(in IInputMethodClient client,
+    void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
             boolean viewHasFocus, boolean isTextEditor,
             int softInputMode, boolean first, int windowFlags);
             
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 6e5c4e0..18f2878 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -27,6 +27,7 @@
 	android_database_SQLiteProgram.cpp \
 	android_database_SQLiteQuery.cpp \
 	android_database_SQLiteStatement.cpp \
+	android_emoji_EmojiFactory.cpp \
 	android_view_Display.cpp \
 	android_view_Surface.cpp \
 	android_view_ViewRoot.cpp \
@@ -132,6 +133,7 @@
 	external/tremor/Tremor \
 	external/icu4c/i18n \
 	external/icu4c/common \
+	frameworks/opt/emoji
 
 LOCAL_SHARED_LIBRARIES := \
 	libexpat \
@@ -156,12 +158,13 @@
 	libicui18n \
 	libicudata \
 	libmedia \
-	libwpa_client
+	libwpa_client \
+	libemoji
 
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
 LOCAL_C_INCLUDES += \
 	external/dbus \
-	external/bluez/libs/include
+	system/bluetooth/bluez-clean-headers
 LOCAL_CFLAGS += -DHAVE_BLUETOOTH
 LOCAL_SHARED_LIBRARIES += libbluedroid libdbus
 endif
@@ -190,5 +193,4 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 40dc2a1..28c602b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -92,6 +92,7 @@
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_content_StringBlock(JNIEnv* env);
 extern int register_android_content_XmlBlock(JNIEnv* env);
+extern int register_android_emoji_EmojiFactory(JNIEnv* env);
 extern int register_android_graphics_Canvas(JNIEnv* env);
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -1026,6 +1027,7 @@
     REG_JNI(register_android_content_AssetManager),
     REG_JNI(register_android_content_StringBlock),
     REG_JNI(register_android_content_XmlBlock),
+    REG_JNI(register_android_emoji_EmojiFactory),
     REG_JNI(register_android_security_Md5MessageDigest),
     REG_JNI(register_android_text_AndroidCharacter),
     REG_JNI(register_android_text_KeyCharacterMap),
diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
index 7f87d80..bf23650 100755
--- a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
+++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
@@ -49,8 +49,6 @@
 
 #ifdef HAVE_BLUETOOTH
 #include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
 #include <bluetooth/rfcomm.h>
 #include <bluetooth/sco.h>
 #endif
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
new file mode 100644
index 0000000..59f63a8
--- /dev/null
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -0,0 +1,292 @@
+#include "SkTypes.h"
+#include "SkImageDecoder.h"
+
+#define LOG_TAG "DoCoMoEmojiFactory_jni"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "EmojiFactory.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <dlfcn.h>
+// #include <pthread.h>
+
+namespace android {
+
+// Note: This class is originally developed so that libandroid_runtime does
+// not have to depend on libemoji which is optional library. However, we
+// cannot use this class, since current (2009-02-16) bionic libc does not allow
+// dlopen()-ing inside dlopen(), while not only this class but also libemoji
+// uses dlopen().
+class EmojiFactoryCaller {
+ public:
+  EmojiFactoryCaller();
+  virtual ~EmojiFactoryCaller();
+  EmojiFactory *TryCallGetImplementation(const char* name);
+  EmojiFactory *TryCallGetAvailableImplementation();
+ private:
+  void *m_handle;
+  EmojiFactory *(*m_get_implementation)(const char*);
+  EmojiFactory *(*m_get_available_implementation)();
+};
+
+EmojiFactoryCaller::EmojiFactoryCaller() {
+  m_handle = dlopen("libemoji.so", RTLD_LAZY | RTLD_LOCAL);
+  const char* error_str = dlerror();
+  if (error_str) {
+    LOGI("Failed to load libemoji.so: %s", error_str);
+    return;
+  }
+
+  m_get_implementation =
+      reinterpret_cast<EmojiFactory *(*)(const char*)>(
+          dlsym(m_handle, "GetImplementation"));
+  error_str = dlerror();
+  if (error_str) {
+    LOGE("Failed to get symbol of GetImplementation: %s", error_str);
+    dlclose(m_handle);
+    m_handle = NULL;
+    return;
+  }
+
+  m_get_available_implementation =
+      reinterpret_cast<EmojiFactory *(*)()>(
+          dlsym(m_handle,"GetAvailableImplementation"));
+  error_str = dlerror();
+  if (error_str) {
+    LOGE("Failed to get symbol of GetAvailableImplementation: %s", error_str);
+    dlclose(m_handle);
+    m_handle = NULL;
+    return;
+  }
+}
+
+EmojiFactoryCaller::~EmojiFactoryCaller() {
+  if (m_handle) {
+    dlclose(m_handle);
+  }
+}
+
+EmojiFactory *EmojiFactoryCaller::TryCallGetImplementation(
+    const char* name) {
+  if (NULL == m_handle) {
+    return NULL;
+  }
+  return m_get_implementation(name);
+}
+
+EmojiFactory *EmojiFactoryCaller::TryCallGetAvailableImplementation() {
+  if (NULL == m_handle) {
+    return NULL;
+  }
+  return m_get_available_implementation();
+}
+
+// Note: bionic libc's dlopen() does not allow recursive dlopen(). So currently
+// we cannot use EmojiFactoryCaller here.
+// static EmojiFactoryCaller* gCaller;
+// static pthread_once_t g_once = PTHREAD_ONCE_INIT;
+
+static jclass    gString_class;
+
+static jclass    gBitmap_class;
+static jmethodID gBitmap_constructorMethodID;
+
+static jclass    gEmojiFactory_class;
+static jmethodID gEmojiFactory_constructorMethodID;
+
+// static void InitializeCaller() {
+//   gCaller = new EmojiFactoryCaller();
+// }
+
+static jobject create_java_EmojiFactory(
+    JNIEnv* env, EmojiFactory* factory, jstring name) {
+  jobject obj = env->AllocObject(gEmojiFactory_class);
+  if (obj) {
+    env->CallVoidMethod(obj, gEmojiFactory_constructorMethodID,
+                        (jint)factory, name);
+    if (env->ExceptionCheck() != 0) {
+      LOGE("*** Uncaught exception returned from Java call!\n");
+      env->ExceptionDescribe();
+      obj = NULL;
+    }
+  }
+  return obj;
+}
+
+static jobject android_emoji_EmojiFactory_newInstance(
+    JNIEnv* env, jobject clazz, jstring name) {
+  // pthread_once(&g_once, InitializeCaller);
+
+  if (NULL == name) {
+    return NULL;
+  }
+
+  const jchar* jchars = env->GetStringChars(name, NULL);
+  jsize len = env->GetStringLength(name);
+  String8 str(String16(jchars, len));
+
+  // EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string());
+  EmojiFactory *factory = EmojiFactory::GetImplementation(str.string());
+
+  env->ReleaseStringChars(name, jchars);
+
+  return create_java_EmojiFactory(env, factory, name);
+}
+
+static jobject android_emoji_EmojiFactory_newAvailableInstance(
+    JNIEnv* env, jobject clazz) {
+  // pthread_once(&g_once, InitializeCaller);
+
+  // EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation();
+  EmojiFactory *factory = EmojiFactory::GetAvailableImplementation();
+  if (NULL == factory) {
+    return NULL;
+  }
+  String16 name_16(String8(factory->Name()));
+  jstring jname = env->NewString(name_16.string(), name_16.size());
+  if (NULL == jname) {
+    return NULL;
+  }
+
+  return create_java_EmojiFactory(env, factory, jname);
+}
+
+static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
+    JNIEnv* env, jobject clazz, jint nativeEmojiFactory, jint pua) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+
+  int size;
+  const char *bytes = factory->GetImageBinaryFromAndroidPua(pua, &size);
+  if (bytes == NULL) {
+    return NULL;
+  }
+
+  SkBitmap *bitmap = new SkBitmap;
+  if (!SkImageDecoder::DecodeMemory(bytes, size, bitmap)) {
+    LOGE("SkImageDecoder::DecodeMemory() failed.");
+    return NULL;
+  }
+
+  jobject obj = env->AllocObject(gBitmap_class);
+  if (obj) {
+    env->CallVoidMethod(obj, gBitmap_constructorMethodID,
+                        reinterpret_cast<jint>(bitmap), false, NULL);
+    if (env->ExceptionCheck() != 0) {
+      LOGE("*** Uncaught exception returned from Java call!\n");
+      env->ExceptionDescribe();
+      return NULL;
+    }
+  }
+
+  return obj;
+}
+
+static void android_emoji_EmojiFactory_destructor(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  delete factory;
+}
+
+static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory, jchar sjis) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetAndroidPuaFromVendorSpecificSjis(sjis);
+}
+
+static jint android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetVendorSpecificSjisFromAndroidPua(pua);
+}
+
+static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint vsu) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetAndroidPuaFromVendorSpecificPua(vsu);
+}
+
+static jint android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetVendorSpecificPuaFromAndroidPua(pua);
+}
+
+static jint android_emoji_EmojiFactory_getMaximumVendorSpecificPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetMaximumVendorSpecificPua();
+}
+
+static jint android_emoji_EmojiFactory_getMinimumVendorSpecificPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetMinimumVendorSpecificPua();
+}
+
+static jint android_emoji_EmojiFactory_getMaximumAndroidPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetMaximumAndroidPua();
+}
+
+static jint android_emoji_EmojiFactory_getMinimumAndroidPua(
+    JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+  EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+  return factory->GetMinimumAndroidPua();
+}
+
+static JNINativeMethod gMethods[] = {
+  { "newInstance", "(Ljava/lang/String;)Landroid/emoji/EmojiFactory;",
+    (void*)android_emoji_EmojiFactory_newInstance},
+  { "newAvailableInstance", "()Landroid/emoji/EmojiFactory;",
+    (void*)android_emoji_EmojiFactory_newAvailableInstance},
+  { "nativeDestructor", "(I)V",
+    (void*)android_emoji_EmojiFactory_destructor},
+  { "nativeGetBitmapFromAndroidPua", "(II)Landroid/graphics/Bitmap;",
+    (void*)android_emoji_EmojiFactory_getBitmapFromAndroidPua},
+  { "nativeGetAndroidPuaFromVendorSpecificSjis", "(IC)I",
+    (void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis},
+  { "nativeGetVendorSpecificSjisFromAndroidPua", "(II)I",
+    (void*)android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua},
+  { "nativeGetAndroidPuaFromVendorSpecificPua", "(II)I",
+    (void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua},
+  { "nativeGetVendorSpecificPuaFromAndroidPua", "(II)I",
+    (void*)android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua},
+  { "nativeGetMaximumVendorSpecificPua", "(I)I",
+    (void*)android_emoji_EmojiFactory_getMaximumVendorSpecificPua},
+  { "nativeGetMinimumVendorSpecificPua", "(I)I",
+    (void*)android_emoji_EmojiFactory_getMinimumVendorSpecificPua},
+  { "nativeGetMaximumAndroidPua", "(I)I",
+    (void*)android_emoji_EmojiFactory_getMaximumAndroidPua},
+  { "nativeGetMinimumAndroidPua", "(I)I",
+    (void*)android_emoji_EmojiFactory_getMinimumAndroidPua}
+};
+
+static jclass make_globalref(JNIEnv* env, const char classname[])
+{
+    jclass c = env->FindClass(classname);
+    SkASSERT(c);
+    return (jclass)env->NewGlobalRef(c);
+}
+
+static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
+                                const char fieldname[], const char type[])
+{
+    jfieldID id = env->GetFieldID(clazz, fieldname, type);
+    SkASSERT(id);
+    return id;
+}
+
+int register_android_emoji_EmojiFactory(JNIEnv* env) {
+  gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
+  gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
+                                                 "(IZ[B)V");
+  gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory");
+  gEmojiFactory_constructorMethodID = env->GetMethodID(
+      gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V");
+  return jniRegisterNativeMethods(env, "android/emoji/EmojiFactory",
+                                  gMethods, NELEM(gMethods));
+}
+
+}  // namespace android
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index c98207a..fcab813 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -397,6 +397,16 @@
     return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
 }
 
+static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject clazz, jboolean setCoexScanMode)
+{
+    char cmdstr[256];
+
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
+    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
+
+    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+}
+
 static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz)
 {
     // Make sure we never write out a value for AP_SCAN other than 1
@@ -503,6 +513,8 @@
     { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand },
     { "setBluetoothCoexistenceModeCommand", "(I)Z",
     		(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
+    { "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
+    		(void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
     { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
     { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
     { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
diff --git a/core/res/Android.mk b/core/res/Android.mk
index 5fca5d0..cb5524a 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -24,7 +24,7 @@
 # since these resources will be used by many apps.
 LOCAL_AAPT_FLAGS := -x
 
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
 
 # Install this alongside the libraries.
 LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
diff --git a/core/res/res/anim/zoom_ring_enter.xml b/core/res/res/anim/zoom_ring_enter.xml
deleted file mode 100644
index 13d89b2..0000000
--- a/core/res/res/anim/zoom_ring_enter.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/anim/fade_in.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-    <scale android:fromXScale="0.75" android:toXScale="1.0"
-           android:fromYScale="0.75" android:toYScale="1.0"
-           android:pivotX="50%" android:pivotY="50%"
-           android:duration="75" />
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="75" />
-</set>
diff --git a/core/res/res/anim/zoom_ring_exit.xml b/core/res/res/anim/zoom_ring_exit.xml
deleted file mode 100644
index 177a4c3..0000000
--- a/core/res/res/anim/zoom_ring_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/anim/fade_out.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
-    <scale android:fromXScale="1.0" android:toXScale="0.75"
-           android:fromYScale="1.0" android:toYScale="0.75"
-           android:pivotX="50%" android:pivotY="50%"
-           android:duration="75" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="75"/>
-</set>
diff --git a/core/res/res/drawable-land/statusbar_background.png b/core/res/res/drawable-land/statusbar_background.png
index 8ecc24c..bafa5c5 100644
--- a/core/res/res/drawable-land/statusbar_background.png
+++ b/core/res/res/drawable-land/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle.xml b/core/res/res/drawable/btn_circle.xml
new file mode 100644
index 0000000..9208010
--- /dev/null
+++ b/core/res/res/drawable/btn_circle.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_circle_normal" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_circle_disable" />
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_circle_pressed" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_circle_selected" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_circle_normal" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_circle_disable_focused" />
+    <item
+         android:drawable="@drawable/btn_circle_disable" />
+</selector>
diff --git a/core/res/res/drawable/btn_circle_disable.png b/core/res/res/drawable/btn_circle_disable.png
new file mode 100644
index 0000000..33b74a6
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_disable_focused.png b/core/res/res/drawable/btn_circle_disable_focused.png
new file mode 100644
index 0000000..005ad8d
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_longpress.png b/core/res/res/drawable/btn_circle_longpress.png
new file mode 100644
index 0000000..f27d411
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_longpress.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_normal.png b/core/res/res/drawable/btn_circle_normal.png
new file mode 100644
index 0000000..fc5af1c
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_pressed.png b/core/res/res/drawable/btn_circle_pressed.png
new file mode 100644
index 0000000..8f40afd
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_selected.png b/core/res/res/drawable/btn_circle_selected.png
new file mode 100644
index 0000000..c74fac2
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml
index 9c6c4ba..c7f66f6 100644
--- a/core/res/res/drawable/extract_edit_text.xml
+++ b/core/res/res/drawable/extract_edit_text.xml
@@ -15,7 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/keyboard_textfield_pressed" />
     <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" />
     <item android:drawable="@drawable/textfield_disabled" />
 </selector>
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml b/core/res/res/drawable/ic_btn_round_more.xml
similarity index 71%
rename from core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml
rename to core/res/res/drawable/ic_btn_round_more.xml
index f2d495b..82b7593 100644
--- a/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml
+++ b/core/res/res/drawable/ic_btn_round_more.xml
@@ -14,9 +14,9 @@
      limitations under the License.
 -->
 
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-    android:pivotX="50%"
-    android:pivotY="50%"
-    android:fromDegrees="0"
-    android:toDegrees="360"
-    android:drawable="@drawable/zoom_ring_thumb_minus_arrow" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:drawable="@drawable/ic_btn_round_more_disabled" />
+    <item
+         android:drawable="@drawable/ic_btn_round_more_normal" />
+</selector>
diff --git a/core/res/res/drawable/ic_btn_round_more_disabled.png b/core/res/res/drawable/ic_btn_round_more_disabled.png
new file mode 100644
index 0000000..1ab98c9
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_round_more_normal.png b/core/res/res/drawable/ic_btn_round_more_normal.png
new file mode 100644
index 0000000..ebdc55c
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_textfield_pressed.9.png b/core/res/res/drawable/keyboard_textfield_pressed.9.png
deleted file mode 100644
index f4e3f10..0000000
--- a/core/res/res/drawable/keyboard_textfield_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call.png b/core/res/res/drawable/stat_sys_phone_call.png
index ad53693..c44d062 100644
--- a/core/res/res/drawable/stat_sys_phone_call.png
+++ b/core/res/res/drawable/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png
new file mode 100644
index 0000000..7abfd19
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable/statusbar_background.png
index 945ad92..25a2344 100644
--- a/core/res/res/drawable/statusbar_background.png
+++ b/core/res/res/drawable/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_arrows.png b/core/res/res/drawable/zoom_ring_arrows.png
deleted file mode 100644
index e443de7..0000000
--- a/core/res/res/drawable/zoom_ring_arrows.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_overview_tab.9.png b/core/res/res/drawable/zoom_ring_overview_tab.9.png
deleted file mode 100644
index d2658d8..0000000
--- a/core/res/res/drawable/zoom_ring_overview_tab.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb.png b/core/res/res/drawable/zoom_ring_thumb.png
deleted file mode 100644
index 1002724..0000000
--- a/core/res/res/drawable/zoom_ring_thumb.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus.png b/core/res/res/drawable/zoom_ring_thumb_minus.png
deleted file mode 100644
index faed674..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_minus.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png
deleted file mode 100644
index abe1b8a..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus.png b/core/res/res/drawable/zoom_ring_thumb_plus.png
deleted file mode 100644
index ba7aff2..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png
deleted file mode 100644
index d01ccb4..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml b/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml
deleted file mode 100644
index b77eaa4..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-    android:pivotX="50%"
-    android:pivotY="50%"
-    android:fromDegrees="0"
-    android:toDegrees="360"
-    android:drawable="@drawable/zoom_ring_thumb_plus_arrow" />
diff --git a/core/res/res/drawable/zoom_ring_track.png b/core/res/res/drawable/zoom_ring_track.png
deleted file mode 100644
index 1d5a44c..0000000
--- a/core/res/res/drawable/zoom_ring_track.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_track_absolute.png b/core/res/res/drawable/zoom_ring_track_absolute.png
deleted file mode 100644
index 2b87699..0000000
--- a/core/res/res/drawable/zoom_ring_track_absolute.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_trail.xml b/core/res/res/drawable/zoom_ring_trail.xml
deleted file mode 100644
index 08931ac..0000000
--- a/core/res/res/drawable/zoom_ring_trail.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-    android:pivotX="50%"
-    android:pivotY="50%"
-    android:fromDegrees="0"
-    android:toDegrees="360">
-
-    <shape
-        android:shape="ring"
-        android:innerRadius="60dip"
-        android:thickness="14dip"
-        android:useLevel="true">
-
-        <gradient
-            android:type="sweep"
-            android:useLevel="true"
-            android:startColor="#1000ceff"
-            android:endColor="#ffadd252" />
-
-    </shape>
-
- </rotate>
diff --git a/core/res/res/layout/preferences.xml b/core/res/res/layout/preferences.xml
index e6876ff..f0c2535 100644
--- a/core/res/res/layout/preferences.xml
+++ b/core/res/res/layout/preferences.xml
@@ -19,7 +19,8 @@
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginRight="7dip"
+    android:layout_marginRight="4dip"
     android:layout_gravity="center_vertical"
-    android:src="@drawable/ic_settings_indicator_next_page" />
+    android:background="@drawable/btn_circle"
+    android:src="@drawable/ic_btn_round_more" />
 
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 18a13554..81fd87d 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -65,13 +65,6 @@
         <item>@drawable/spinner_dropdown_background</item>
         <item>@drawable/title_bar</item>
         <item>@drawable/title_bar_shadow</item>
-        <item>@drawable/zoom_ring_arrows</item>
-        <item>@drawable/zoom_ring_overview_tab</item>
-        <item>@drawable/zoom_ring_thumb</item>
-        <item>@drawable/zoom_ring_thumb_minus_arrow_rotatable</item>
-        <item>@drawable/zoom_ring_thumb_plus_arrow_rotatable</item>
-        <item>@drawable/zoom_ring_track</item>
-        <item>@drawable/zoom_ring_track_absolute</item>
         <!-- Visual lock screen -->
         <item>@drawable/indicator_code_lock_drag_direction_green_up</item>
         <item>@drawable/indicator_code_lock_drag_direction_red_up</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 30b26fa..5fa5f8d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -371,8 +371,6 @@
         <attr name="spinnerItemStyle" format="reference" />
         <!-- Default MapView style. -->
         <attr name="mapViewStyle" format="reference" />
-        <!-- Default ZoomRing style. -->
-        <attr name="zoomRingStyle" format="reference" />
 
         <!-- =================== -->
         <!-- Preference styles   -->
@@ -585,29 +583,34 @@
     <attr name="imeOptions">
         <!-- There are no special semantics associated with this editor. -->
         <flag name="normal" value="0x00000000" />
-        <!-- There is no special action associated with this editor.
+        <!-- There is no specific action associated with this editor, let the
+             editor come up with its own if it can.
+             Corresponds to
+             {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED}. -->
+        <flag name="actionUnspecified" value="0x00000000" />
+        <!-- This editor has no action associated with it.
              Corresponds to
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_NONE}. -->
-        <flag name="actionNone" value="0x00000000" />
+        <flag name="actionNone" value="0x00000001" />
         <!-- The action key performs a "go"
              operation to take the user to the target of the text they typed.
              Typically used, for example, when entering a URL.
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_GO}. -->
-        <flag name="actionGo" value="0x00000001" />
+        <flag name="actionGo" value="0x00000002" />
         <!-- The action key performs a "search"
              operation, taking the user to the results of searching for the text
              the have typed (in whatever context is appropriate).
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_SEARCH}. -->
-        <flag name="actionSearch" value="0x00000002" />
+        <flag name="actionSearch" value="0x00000003" />
         <!-- The action key performs a "send"
              operation, delivering the text to its target.  This is typically used
              when composing a message.
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_SEND}. -->
-        <flag name="actionSend" value="0x00000003" />
+        <flag name="actionSend" value="0x00000004" />
         <!-- The action key performs a "next"
              operation, taking the user to the next field that will accept text.
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_NEXT}. -->
-        <flag name="actionNext" value="0x00000004" />
+        <flag name="actionNext" value="0x00000005" />
         <!-- Used in conjunction with a custom action,
              this indicates that the action should not
              be available in-line as the same as a "enter" key.  Typically this is
@@ -1935,34 +1938,6 @@
         </attr>
         <attr name="inputType" />
     </declare-styleable>
-    <declare-styleable name="ZoomRing">
-        <!-- Defines the drawable used as the thumb. -->
-        <attr name="thumbDrawable" format="reference" />
-        <!-- Defines the distance of the thumb from the center of the ring. -->
-        <attr name="thumbDistance" format="dimension" />
-        <!-- Defines the distance from the center of the ring to the beginning of the track. -->
-        <attr name="trackInnerRadius" format="dimension" />
-        <!-- Defines the distance from the center of the ring to the end of the track. -->
-        <attr name="trackOuterRadius" format="dimension" />
-        <!-- Defines the drawable used as a hint to show which direction is zoom in. This should be
-             the same size as the zoom ring's asset.  It will be rotated programmatically. -->
-        <attr name="zoomInArrowDrawable" format="reference" />
-        <!-- Defines the drawable used as a hint to show which direction is zoom out. This should be
-             the same size as the zoom ring's asset.  It will be rotated programmatically. -->
-        <attr name="zoomOutArrowDrawable" format="reference" />
-        <!-- Defines the drawable that is laid on top of the zoom in arrow.
-             For example, this could be a +. -->
-        <attr name="zoomInArrowHintDrawable" format="reference" />
-        <!-- Defines the drawable that is laid on top of the zoom out arrow.
-             For example, this could be a -. -->
-        <attr name="zoomOutArrowHintDrawable" format="reference" />
-        <!-- Defines the distance of the zoom arrow hint from the center of the ring. -->
-        <attr name="zoomArrowHintDistance" format="dimension" />
-        <!-- Defines the offset of the zoom arrow hints from the thumb. Valid ranges are [0, 359] -->
-        <attr name="zoomArrowHintOffsetAngle" format="integer" />
-        <!-- Defines the drawable used to hint that the zoom ring can pan its owner. -->
-        <attr name="panningArrowsDrawable" format="reference" />
-    </declare-styleable>    
     <declare-styleable name="PopupWindow">
         <attr name="popupBackground" format="reference|color" />
     </declare-styleable>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9f4d82e..54eba62 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -144,11 +144,6 @@
         <item name="windowExitAnimation">@anim/search_bar_exit</item>
     </style>
 
-    <!-- Standard animations for the zoom ring. -->
-    <style name="Animation.ZoomRing">
-        <item name="windowEnterAnimation">@anim/zoom_ring_enter</item>
-        <item name="windowExitAnimation">@anim/zoom_ring_exit</item>
-    </style>
     <!-- Status Bar Styles -->
 
     <style name="TextAppearance.StatusBarTitle">
@@ -461,21 +456,6 @@
         <item name="android:shadowRadius">2.75</item>
     </style>
 
-    <style name="Widget.ZoomRing">
-        <item name="android:background">@android:drawable/zoom_ring_track</item>
-        <item name="android:thumbDrawable">@android:drawable/zoom_ring_thumb</item>
-        <item name="android:thumbDistance">63dip</item>
-        <item name="android:trackInnerRadius">43dip</item>
-        <item name="android:trackOuterRadius">91dip</item>
-        <item name="android:zoomInArrowDrawable">@android:drawable/zoom_ring_thumb_plus_arrow_rotatable</item>
-        <item name="android:zoomOutArrowDrawable">@android:drawable/zoom_ring_thumb_minus_arrow_rotatable</item>
-        <item name="android:zoomInArrowHintDrawable">@android:drawable/zoom_ring_thumb_plus</item>
-        <item name="android:zoomOutArrowHintDrawable">@android:drawable/zoom_ring_thumb_minus</item>
-        <item name="android:zoomArrowHintDistance">69dip</item>
-        <item name="android:zoomArrowHintOffsetAngle">33</item>
-        <item name="android:panningArrowsDrawable">@android:drawable/zoom_ring_arrows</item>
-    </style>
-
     <!-- Text Appearances -->
     <eat-comment />
 
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bde6b2a..01c46de 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -162,7 +162,6 @@
         <item name="spinnerItemStyle">@android:style/Widget.TextView.SpinnerItem</item>
         <item name="dropDownHintAppearance">@android:style/TextAppearance.Widget.DropDownHint</item>
         <item name="keyboardViewStyle">@android:style/Widget.KeyboardView</item>
-        <item name="zoomRingStyle">@android:style/Widget.ZoomRing</item>
         
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 4b5464f..a32d8ea 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE := platform.xml
 
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
 
 LOCAL_MODULE_CLASS := ETC
 
diff --git a/data/sounds/Android.mk b/data/sounds/Android.mk
deleted file mode 100644
index 9e3697f..0000000
--- a/data/sounds/Android.mk
+++ /dev/null
@@ -1,240 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# don't understand what's wrong here... bs
-#
-#copy_from := $(wildcard $(LOCAL_PATH)/*.mp3) 
-#copy_to := $(addprefix $(TARGET_OUT)/sounds/,$(patsubst $(LOCAL_PATH)/%,%,$(copy_from)))
-#
-#$(copy_to) : PRIVATE_MODULE := sounds 
-#$(copy_to) : $(TARGET_OUT)/sounds/% : $(LOCAL_PATH)/% | $(ACP)
-#	$(transform-prebuilt-to-target)
-#
-#ALL_PREBUILT += $(copy_to)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_MissedCall.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_MissedCall.ogg : $(LOCAL_PATH)/F1_MissedCall.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_New_MMS.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_New_MMS.ogg : $(LOCAL_PATH)/F1_New_MMS.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_New_SMS.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_New_SMS.ogg : $(LOCAL_PATH)/F1_New_SMS.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Buzzer.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Buzzer.ogg : $(LOCAL_PATH)/Alarm_Buzzer.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_01.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_01.ogg : $(LOCAL_PATH)/Alarm_Beep_01.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_02.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_02.ogg : $(LOCAL_PATH)/Alarm_Beep_02.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Classic.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Classic.ogg : $(LOCAL_PATH)/Alarm_Classic.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_03.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_03.ogg : $(LOCAL_PATH)/Alarm_Beep_03.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Rooster_02.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Rooster_02.ogg : $(LOCAL_PATH)/Alarm_Rooster_02.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Classic_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Classic_02.ogg : $(LOCAL_PATH)/Ring_Classic_02.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Digital_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Digital_02.ogg : $(LOCAL_PATH)/Ring_Digital_02.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Synth_04.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Synth_04.ogg : $(LOCAL_PATH)/Ring_Synth_04.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Synth_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Synth_02.ogg : $(LOCAL_PATH)/Ring_Synth_02.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-#
-# --- New Wave Labs ringtones
-#
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BeatPlucker.ogg
-$(TARGET_OUT)/media/audio/ringtones/BeatPlucker.ogg : $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BentleyDubs.ogg
-$(TARGET_OUT)/media/audio/ringtones/BentleyDubs.ogg : $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BirdLoop.ogg
-$(TARGET_OUT)/media/audio/ringtones/BirdLoop.ogg : $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/CaribbeanIce.ogg
-$(TARGET_OUT)/media/audio/ringtones/CaribbeanIce.ogg : $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/CurveBall.ogg
-$(TARGET_OUT)/media/audio/ringtones/CurveBall.ogg : $(LOCAL_PATH)/newwavelabs/CurveBall.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/EtherShake.ogg
-$(TARGET_OUT)/media/audio/ringtones/EtherShake.ogg : $(LOCAL_PATH)/newwavelabs/EtherShake.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/FriendlyGhost.ogg
-$(TARGET_OUT)/media/audio/ringtones/FriendlyGhost.ogg : $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/GameOverGuitar.ogg
-$(TARGET_OUT)/media/audio/ringtones/GameOverGuitar.ogg : $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Growl.ogg
-$(TARGET_OUT)/media/audio/ringtones/Growl.ogg : $(LOCAL_PATH)/newwavelabs/Growl.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/InsertCoin.ogg
-$(TARGET_OUT)/media/audio/ringtones/InsertCoin.ogg : $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/LoopyLounge.ogg
-$(TARGET_OUT)/media/audio/ringtones/LoopyLounge.ogg : $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/LoveFlute.ogg
-$(TARGET_OUT)/media/audio/ringtones/LoveFlute.ogg : $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/MidEvilJaunt.ogg
-$(TARGET_OUT)/media/audio/ringtones/MidEvilJaunt.ogg : $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/MildlyAlarming.ogg
-$(TARGET_OUT)/media/audio/ringtones/MildlyAlarming.ogg : $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/NewPlayer.ogg
-$(TARGET_OUT)/media/audio/ringtones/NewPlayer.ogg : $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises1.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises1.ogg : $(LOCAL_PATH)/newwavelabs/Noises1.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises2.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises2.ogg : $(LOCAL_PATH)/newwavelabs/Noises2.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises3.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises3.ogg : $(LOCAL_PATH)/newwavelabs/Noises3.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/OrganDub.ogg
-$(TARGET_OUT)/media/audio/ringtones/OrganDub.ogg : $(LOCAL_PATH)/newwavelabs/OrganDub.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/RomancingTheTone.ogg
-$(TARGET_OUT)/media/audio/ringtones/RomancingTheTone.ogg : $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/SitarVsSitar.ogg
-$(TARGET_OUT)/media/audio/ringtones/SitarVsSitar.ogg : $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/SpringyJalopy.ogg
-$(TARGET_OUT)/media/audio/ringtones/SpringyJalopy.ogg : $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Terminated.ogg
-$(TARGET_OUT)/media/audio/ringtones/Terminated.ogg : $(LOCAL_PATH)/newwavelabs/Terminated.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/TwirlAway.ogg
-$(TARGET_OUT)/media/audio/ringtones/TwirlAway.ogg : $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/VeryAlarmed.ogg
-$(TARGET_OUT)/media/audio/ringtones/VeryAlarmed.ogg : $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/World.ogg
-$(TARGET_OUT)/media/audio/ringtones/World.ogg : $(LOCAL_PATH)/newwavelabs/World.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-#
-# --- New Wave Labs notifications
-#
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/CaffeineSnake.ogg
-$(TARGET_OUT)/media/audio/notifications/CaffeineSnake.ogg : $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/DearDeer.ogg
-$(TARGET_OUT)/media/audio/notifications/DearDeer.ogg : $(LOCAL_PATH)/newwavelabs/DearDeer.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-		
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/DontPanic.ogg
-$(TARGET_OUT)/media/audio/notifications/DontPanic.ogg : $(LOCAL_PATH)/newwavelabs/DontPanic.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-	
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Highwire.ogg
-$(TARGET_OUT)/media/audio/notifications/Highwire.ogg : $(LOCAL_PATH)/newwavelabs/Highwire.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-	
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/KzurbSonar.ogg
-$(TARGET_OUT)/media/audio/notifications/KzurbSonar.ogg : $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-	
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/OnTheHunt.ogg
-$(TARGET_OUT)/media/audio/notifications/OnTheHunt.ogg : $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-	
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Voila.ogg
-$(TARGET_OUT)/media/audio/notifications/Voila.ogg : $(LOCAL_PATH)/newwavelabs/Voila.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Beat_Box_Android.ogg
-$(TARGET_OUT)/media/audio/notifications/Beat_Box_Android.ogg : $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Heaven.ogg
-$(TARGET_OUT)/media/audio/notifications/Heaven.ogg : $(LOCAL_PATH)/notifications/Heaven.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/TaDa.ogg
-$(TARGET_OUT)/media/audio/notifications/TaDa.ogg : $(LOCAL_PATH)/notifications/TaDa.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Tinkerbell.ogg
-$(TARGET_OUT)/media/audio/notifications/Tinkerbell.ogg : $(LOCAL_PATH)/notifications/Tinkerbell.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-# UI effects
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/Effect_Tick.ogg
-$(TARGET_OUT)/media/audio/ui/Effect_Tick.ogg : $(LOCAL_PATH)/effects/Effect_Tick.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressStandard.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressStandard.ogg : $(LOCAL_PATH)/effects/KeypressStandard.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressSpacebar.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressSpacebar.ogg : $(LOCAL_PATH)/effects/KeypressSpacebar.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressDelete.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressDelete.ogg : $(LOCAL_PATH)/effects/KeypressDelete.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressReturn.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressReturn.ogg : $(LOCAL_PATH)/effects/KeypressReturn.ogg | $(ACP)
-	$(transform-prebuilt-to-target)
-
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
new file mode 100644
index 0000000..8722983
--- /dev/null
+++ b/data/sounds/OriginalAudio.mk
@@ -0,0 +1,68 @@
+#
+# Original audio package that shipped on G1
+# 
+# This file is included from core.mk so that all devices will have these sounds
+# 
+# TODO: Clean up for future releases
+#
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg
diff --git a/data/sounds/effects/KeypressDelete.ogg b/data/sounds/effects/KeypressDelete.ogg
index b35ea65..738f6ae 100644
--- a/data/sounds/effects/KeypressDelete.ogg
+++ b/data/sounds/effects/KeypressDelete.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressDelete.wav b/data/sounds/effects/KeypressDelete.wav
index 5903f7f..0a2f3cc5 100644
--- a/data/sounds/effects/KeypressDelete.wav
+++ b/data/sounds/effects/KeypressDelete.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressReturn.ogg b/data/sounds/effects/KeypressReturn.ogg
index 1b0fd56..6c807b0 100644
--- a/data/sounds/effects/KeypressReturn.ogg
+++ b/data/sounds/effects/KeypressReturn.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressReturn.wav b/data/sounds/effects/KeypressReturn.wav
index 9558fcf..59ff13a 100644
--- a/data/sounds/effects/KeypressReturn.wav
+++ b/data/sounds/effects/KeypressReturn.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressSpacebar.ogg b/data/sounds/effects/KeypressSpacebar.ogg
index 5299337..b59c8ce 100644
--- a/data/sounds/effects/KeypressSpacebar.ogg
+++ b/data/sounds/effects/KeypressSpacebar.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressSpacebar.wav b/data/sounds/effects/KeypressSpacebar.wav
index 1226a21..1f819c9 100644
--- a/data/sounds/effects/KeypressSpacebar.wav
+++ b/data/sounds/effects/KeypressSpacebar.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressStandard.ogg b/data/sounds/effects/KeypressStandard.ogg
index 8a152d1..a465cf8 100644
--- a/data/sounds/effects/KeypressStandard.ogg
+++ b/data/sounds/effects/KeypressStandard.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressStandard.wav b/data/sounds/effects/KeypressStandard.wav
index 0c9aa2a..56a7911 100644
--- a/data/sounds/effects/KeypressStandard.wav
+++ b/data/sounds/effects/KeypressStandard.wav
Binary files differ
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index a72962e..befb018 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -447,7 +447,7 @@
 
 <p>This animation runs for just three frames. By setting the <code>android:oneshot</code> attribute of the 
 list to <var>true</var>, it will cycle just once then stop and hold on the last frame. If it is set <var>false</var> then
-the animation will loop. With this XML saved as <code>rocket_thrust.xml</p> in the <code>res/anim/</code> directory
+the animation will loop. With this XML saved as <code>rocket_thrust.xml</code> in the <code>res/anim/</code> directory
 of the project, it can be added as the background image to a View and then called to play. Here's an example Activity,
 in which the animation is added to an {@link android.widget.ImageView} and then animated when the screen is touched:</p>
 <pre>
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
index eb00f8c..2974e32 100644
--- a/libs/audioflinger/A2dpAudioInterface.cpp
+++ b/libs/audioflinger/A2dpAudioInterface.cpp
@@ -48,7 +48,6 @@
         int format, int channelCount, uint32_t sampleRate, status_t *status)
 {
     LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate);
-    Mutex::Autolock lock(mLock);
     status_t err = 0;
 
     // only one output stream allowed
@@ -134,7 +133,8 @@
     mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL)
 {
     // use any address by default
-    strncpy(mA2dpAddress, "00:00:00:00:00:00", sizeof(mA2dpAddress));
+    strcpy(mA2dpAddress, "00:00:00:00:00:00");
+    init();
 }
 
 status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
@@ -163,18 +163,12 @@
 
 ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
 {    
-    status_t status = NO_INIT;
-    size_t remaining = bytes;
+    Mutex::Autolock lock(mLock);
 
-    if (!mData) {
-        status = a2dp_init(44100, 2, &mData);
-        if (status < 0) {
-            LOGE("a2dp_init failed err: %d\n", status);
-            mData = NULL;
-            goto Error;
-        }
-        a2dp_set_sink(mData, mA2dpAddress);
-    }
+    size_t remaining = bytes;
+    status_t status = init();
+    if (status < 0)
+        goto Error;
     
     while (remaining > 0) {
         status = a2dp_write(mData, buffer, remaining);
@@ -197,10 +191,27 @@
     return status;
 }
 
+status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
+{
+    if (!mData) {
+        status_t status = a2dp_init(44100, 2, &mData);
+        if (status < 0) {
+            LOGE("a2dp_init failed err: %d\n", status);
+            mData = NULL;
+            return status;
+        }
+        a2dp_set_sink(mData, mA2dpAddress);
+    }
+
+    return 0;
+}
+
 status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
 {
     int result = 0;
 
+    Mutex::Autolock lock(mLock);
+
     if (!mStandby) {
         result = a2dp_stop(mData);
         if (result == 0)
@@ -212,15 +223,15 @@
 
 status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
 {
-    if (strlen(address) < sizeof(mA2dpAddress))
+    Mutex::Autolock lock(mLock);
+
+    if (strlen(address) != strlen("00:00:00:00:00:00"))
         return -EINVAL;
 
-    if (strcmp(address, mA2dpAddress)) {
-        strcpy(mA2dpAddress, address);
-        if (mData)
-            a2dp_set_sink(mData, mA2dpAddress);
-    }
-    
+    strcpy(mA2dpAddress, address);
+    if (mData)
+        a2dp_set_sink(mData, mA2dpAddress);
+
     return NO_ERROR;
 }
 
diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h
index a56e8a0..99614dc 100644
--- a/libs/audioflinger/A2dpAudioInterface.h
+++ b/libs/audioflinger/A2dpAudioInterface.h
@@ -82,11 +82,12 @@
         virtual status_t    setVolume(float volume) { return INVALID_OPERATION; }
         virtual ssize_t     write(const void* buffer, size_t bytes);
                 status_t    standby();
-                status_t    close();
         virtual status_t    dump(int fd, const Vector<String16>& args);
 
     private:
         friend class A2dpAudioInterface;
+                status_t    init();
+                status_t    close();
         status_t            setAddress(const char* address);
 
     private:
@@ -96,9 +97,9 @@
                 int         mRetryCount;
                 char        mA2dpAddress[20];
                 void*       mData;
+                Mutex       mLock;
     };
 
-    Mutex                   mLock;
     A2dpAudioStreamOut*     mOutput;
 };
 
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 92c40e9..440778d 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -71,6 +71,9 @@
 static const int kStartSleepTime = 30000;
 static const int kStopSleepTime = 30000;
 
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
 // Maximum number of pending buffers allocated by OutputTrack::write()
 static const uint8_t kMaxOutputTrackBuffers = 5;
 
@@ -115,8 +118,7 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mAudioHardware(0), mA2dpAudioInterface(0),
-        mA2dpEnabled(false), mA2dpEnabledReq(false),
+        mAudioHardware(0), mA2dpAudioInterface(0), mA2dpEnabled(false), mNotifyA2dpChange(false),
         mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false)
 {
     mHardwareStatus = AUDIO_HW_IDLE;
@@ -190,13 +192,44 @@
 
 
 #ifdef WITH_A2DP
-void AudioFlinger::setA2dpEnabled(bool enable)
+// setA2dpEnabled_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::setA2dpEnabled_l(bool enable)
 {
+    SortedVector < sp<MixerThread::Track> > tracks;
+    SortedVector < wp<MixerThread::Track> > activeTracks;
+    
     LOGV_IF(enable, "set output to A2DP\n");
     LOGV_IF(!enable, "set output to hardware audio\n");
 
-    mA2dpEnabledReq = enable;
-    mA2dpMixerThread->wakeUp();
+    // Transfer tracks playing on MUSIC stream from one mixer to the other
+    if (enable) {
+        mHardwareMixerThread->getTracks_l(tracks, activeTracks);
+        mA2dpMixerThread->putTracks_l(tracks, activeTracks);
+    } else {
+        mA2dpMixerThread->getTracks_l(tracks, activeTracks);
+        mHardwareMixerThread->putTracks_l(tracks, activeTracks);
+    }
+    mA2dpEnabled = enable;
+    mNotifyA2dpChange = true;
+    mWaitWorkCV.broadcast();
+}
+
+// checkA2dpEnabledChange_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::checkA2dpEnabledChange_l()
+{
+    if (mNotifyA2dpChange) {
+        // Notify AudioSystem of the A2DP activation/deactivation
+        size_t size = mNotificationClients.size();
+        for (size_t i = 0; i < size; i++) {
+            sp<IBinder> binder = mNotificationClients.itemAt(i).promote();
+            if (binder != NULL) {
+                LOGV("Notifying output change to client %p", binder.get());
+                sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
+                client->a2dpEnabledChanged(mA2dpEnabled);
+            }
+        }
+        mNotifyA2dpChange = false;
+    }
 }
 #endif // WITH_A2DP
 
@@ -236,8 +269,12 @@
     const size_t SIZE = 256;
     char buffer[SIZE];
     String8 result;
-
-    snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus);
+    int hardwareStatus = mHardwareStatus;
+    
+    if (hardwareStatus == AUDIO_HW_IDLE && mHardwareMixerThread->mStandby) {
+        hardwareStatus = AUDIO_HW_STANDBY;
+    }
+    snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
     result.append(buffer);
     write(fd, result.string(), result.size());
     return NO_ERROR;
@@ -262,7 +299,14 @@
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
         dumpPermissionDenial(fd, args);
     } else {
-        AutoMutex lock(&mLock);
+        bool locked = false;
+        for (int i = 0; i < kDumpLockRetries; ++i) {
+            if (mLock.tryLock() == NO_ERROR) {
+                locked = true;
+                break;
+            }
+            usleep(kDumpLockSleep);
+        }
 
         dumpClients(fd, args);
         dumpInternals(fd, args);
@@ -277,6 +321,7 @@
         if (mAudioHardware) {
             mAudioHardware->dumpState(fd, args);
         }
+        if (locked) mLock.unlock();
     }
     return NO_ERROR;
 }
@@ -320,18 +365,19 @@
         }
 #ifdef WITH_A2DP
         if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) {
-            track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format,
+            track = mA2dpMixerThread->createTrack_l(client, streamType, sampleRate, format,
                     channelCount, frameCount, sharedBuffer, &lStatus);            
         } else 
 #endif
         {
-            track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format,
+            track = mHardwareMixerThread->createTrack_l(client, streamType, sampleRate, format,
                     channelCount, frameCount, sharedBuffer, &lStatus);            
         }
-        if (track != NULL) {
-            trackHandle = new TrackHandle(track);
-            lStatus = NO_ERROR;            
-        }
+    }
+    if (lStatus == NO_ERROR) {
+        trackHandle = new TrackHandle(track);
+    } else {
+        track.clear();
     }
 
 Exit:
@@ -409,7 +455,7 @@
 #ifdef WITH_A2DP
     mA2dpMixerThread->setMasterVolume(value);
 #endif
-    
+
     return NO_ERROR;
 }
 
@@ -436,7 +482,7 @@
         if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) {
             enableA2dp = true;
         }
-        setA2dpEnabled(enableA2dp);
+        setA2dpEnabled_l(enableA2dp);
         LOGV("setOutput done\n");
     }
 #endif
@@ -704,46 +750,6 @@
     }
 }
 
-void AudioFlinger::handleOutputSwitch()
-{
-    if (mA2dpEnabled != mA2dpEnabledReq)
-    {
-        Mutex::Autolock _l(mLock);
-
-        if (mA2dpEnabled != mA2dpEnabledReq)
-        {
-            mA2dpEnabled = mA2dpEnabledReq;
-            SortedVector < sp<MixerThread::Track> > tracks;
-            SortedVector < wp<MixerThread::Track> > activeTracks;
-            
-            // We hold mA2dpMixerThread mLock already 
-            Mutex::Autolock _l(mHardwareMixerThread->mLock);
-            
-            // Transfer tracks playing on MUSIC stream from one mixer to the other
-            if (mA2dpEnabled) {
-                mHardwareMixerThread->getTracks(tracks, activeTracks);
-                mA2dpMixerThread->putTracks(tracks, activeTracks);
-            } else {
-                mA2dpMixerThread->getTracks(tracks, activeTracks);
-                mHardwareMixerThread->putTracks(tracks, activeTracks);
-            }            
-            
-            // Notify AudioSystem of the A2DP activation/deactivation
-            size_t size = mNotificationClients.size();
-            for (size_t i = 0; i < size; i++) {
-                sp<IBinder> binder = mNotificationClients.itemAt(i).promote();
-                if (binder != NULL) {
-                    LOGV("Notifying output change to client %p", binder.get());
-                    sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
-                    client->a2dpEnabledChanged(mA2dpEnabled);
-                }
-            }
-
-            mHardwareMixerThread->wakeUp();
-        }
-    }
-}
-
 void AudioFlinger::removeClient(pid_t pid)
 {
     LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
@@ -751,17 +757,9 @@
     mClients.removeItem(pid);
 }
 
-void AudioFlinger::wakeUp()
-{
-    mHardwareMixerThread->wakeUp();
-#ifdef WITH_A2DP
-    mA2dpMixerThread->wakeUp();
-#endif // WITH_A2DP
-}
-
 bool AudioFlinger::isA2dpEnabled() const
 {
-    return mA2dpEnabledReq;
+    return mA2dpEnabled;
 }
 
 void AudioFlinger::handleForcedSpeakerRoute(int command)
@@ -803,7 +801,7 @@
                 LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount);            
             } else {
                 LOGE("mForcedSpeakerCount is already zero");            
-            }            
+            }
         }
         break;
     case CHECK_ROUTE_RESTORE_TIME:
@@ -946,20 +944,20 @@
 
     do {
         enabledTracks = 0;
-        { // scope for the mLock
+        { // scope for the AudioFlinger::mLock
         
-            Mutex::Autolock _l(mLock);
+            Mutex::Autolock _l(mAudioFlinger->mLock);
 
 #ifdef WITH_A2DP
-            if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) {
-                mAudioFlinger->handleOutputSwitch();
-            }
             if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) {
                 if (outputTrackActive) {
+                    mAudioFlinger->mLock.unlock();
                     mOutputTrack->stop();
+                    mAudioFlinger->mLock.lock();
                     outputTrackActive = false;
                 }
             }
+            mAudioFlinger->checkA2dpEnabledChange_l();
 #endif
 
             const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
@@ -968,7 +966,6 @@
             if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) {
                 // wait until we have something to do...
                 LOGV("Audio hardware entering standby, output %d\n", mOutputType);
-//                mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY;
                 if (!mStandby) {
                     mOutput->standby();
                     mStandby = true;
@@ -976,17 +973,18 @@
                 
 #ifdef WITH_A2DP
                 if (outputTrackActive) {
+                    mAudioFlinger->mLock.unlock();
                     mOutputTrack->stop();
+                    mAudioFlinger->mLock.lock();
                     outputTrackActive = false;
                 }
 #endif
                 if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
                     mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE);
                 }                
-//                mHardwareStatus = AUDIO_HW_IDLE;
                 // we're about to wait, flush the binder command buffer
                 IPCThreadState::self()->flushCommands();
-                mWaitWorkCV.wait(mLock);
+                mAudioFlinger->mWaitWorkCV.wait(mAudioFlinger->mLock);
                 LOGV("Audio hardware exiting standby, output %d\n", mOutputType);
                 
                 if (mMasterMute == false) {
@@ -1104,13 +1102,13 @@
             if (UNLIKELY(count)) {
                 for (size_t i=0 ; i<count ; i++) {
                     const sp<Track>& track = tracksToRemove[i];
-                    removeActiveTrack(track);
+                    removeActiveTrack_l(track);
                     if (track->isTerminated()) {
                         mTracks.remove(track);
-                        deleteTrackName(track->mName);
+                        deleteTrackName_l(track->mName);
                     }
                 }
-            }  
+            }
        }
         
         if (LIKELY(enabledTracks)) {
@@ -1197,8 +1195,8 @@
     run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
 }
 
-
-sp<AudioFlinger::MixerThread::Track>  AudioFlinger::MixerThread::createTrack(
+// MixerThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::MixerThread::Track>  AudioFlinger::MixerThread::createTrack_l(
         const sp<AudioFlinger::Client>& client,
         int streamType,
         uint32_t sampleRate,
@@ -1218,26 +1216,22 @@
         goto Exit;
     }
 
-    {
-        Mutex::Autolock _l(mLock);
 
-        if (mSampleRate == 0) {
-            LOGE("Audio driver not initialized.");
-            lStatus = NO_INIT;
-            goto Exit;
-        }
-
-        track = new Track(this, client, streamType, sampleRate, format,
-                channelCount, frameCount, sharedBuffer);
-        if (track->getCblk() == NULL) {
-            track.clear();
-            lStatus = NO_MEMORY;
-            goto Exit;
-        }
-        mTracks.add(track);
-        lStatus = NO_ERROR;
+    if (mSampleRate == 0) {
+        LOGE("Audio driver not initialized.");
+        lStatus = NO_INIT;
+        goto Exit;
     }
 
+    track = new Track(this, client, streamType, sampleRate, format,
+            channelCount, frameCount, sharedBuffer);
+    if (track->getCblk() == NULL) {
+        lStatus = NO_MEMORY;
+        goto Exit;
+    }
+    mTracks.add(track);
+    lStatus = NO_ERROR;
+
 Exit:
     if(status) {
         *status = lStatus;
@@ -1245,12 +1239,13 @@
     return track;
 }
 
-void AudioFlinger::MixerThread::getTracks(
+// getTracks_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::getTracks_l(
         SortedVector < sp<Track> >& tracks,
         SortedVector < wp<Track> >& activeTracks)
 {
     size_t size = mTracks.size();
-    LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType,  mTracks.size(), mActiveTracks.size());
+    LOGV ("MixerThread::getTracks_l() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType,  mTracks.size(), mActiveTracks.size());
     for (size_t i = 0; i < size; i++) {
         sp<Track> t = mTracks[i];
         if (AudioSystem::routedToA2dpOutput(t->mStreamType)) {
@@ -1267,28 +1262,29 @@
 
     size = activeTracks.size();
     for (size_t i = 0; i < size; i++) {
-        removeActiveTrack(activeTracks[i]);
+        removeActiveTrack_l(activeTracks[i]);
     }
     
     size = tracks.size();
     for (size_t i = 0; i < size; i++) {
         sp<Track> t = tracks[i];
         mTracks.remove(t);
-        deleteTrackName(t->name());
+        deleteTrackName_l(t->name());
     }
 }
 
-void AudioFlinger::MixerThread::putTracks(
+// putTracks_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::putTracks_l(
         SortedVector < sp<Track> >& tracks,
         SortedVector < wp<Track> >& activeTracks)
 {
 
-    LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType,  tracks.size(), activeTracks.size());
+    LOGV ("MixerThread::putTracks_l() for output %d, tracks.size %d, activeTracks.size %d", mOutputType,  tracks.size(), activeTracks.size());
 
     size_t size = tracks.size();
     for (size_t i = 0; i < size ; i++) {
         sp<Track> t = tracks[i];
-        int name = getTrackName();
+        int name = getTrackName_l();
 
         if (name < 0) return;
         
@@ -1298,7 +1294,7 @@
 
         int j = activeTracks.indexOf(t);
         if (j >= 0) {
-            addActiveTrack(t);
+            addActiveTrack_l(t);
         }            
     }
 }
@@ -1390,10 +1386,10 @@
     return false;
 }
 
-status_t AudioFlinger::MixerThread::addTrack(const sp<Track>& track)
+// addTrack_l() must be called with AudioFlinger::mLock held
+status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track)
 {
     status_t status = ALREADY_EXISTS;
-    Mutex::Autolock _l(mLock);
 
     // here the track could be either new, or restarted
     // in both cases "unstop" the track
@@ -1412,55 +1408,41 @@
         // effectively get the latency it requested.
         track->mFillingUpStatus = Track::FS_FILLING;
         track->mResetDone = false;
-        addActiveTrack(track);
+        addActiveTrack_l(track);
         status = NO_ERROR;
     }
     
     LOGV("mWaitWorkCV.broadcast");
-    mWaitWorkCV.broadcast();
+    mAudioFlinger->mWaitWorkCV.broadcast();
 
     return status;
 }
 
-void AudioFlinger::MixerThread::removeTrack(wp<Track> track, int name)
+// removeTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::removeTrack_l(wp<Track> track, int name)
 {
-    Mutex::Autolock _l(mLock);
     sp<Track> t = track.promote();
     if (t!=NULL && (t->mState <= TrackBase::STOPPED)) {
-        remove_track_l(track, name);
-    }
-}
-
-void AudioFlinger::MixerThread::remove_track_l(wp<Track> track, int name)
-{
-    sp<Track> t = track.promote();
-    if (t!=NULL) {
         t->reset();
+        deleteTrackName_l(name);
+        removeActiveTrack_l(track);
+        mAudioFlinger->mWaitWorkCV.broadcast();
     }
-    deleteTrackName(name);
-    removeActiveTrack(track);
-    mWaitWorkCV.broadcast();
 }
 
-void AudioFlinger::MixerThread::destroyTrack(const sp<Track>& track)
+// destroyTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track)
 {
-    // NOTE: We're acquiring a strong reference on the track before
-    // acquiring the lock, this is to make sure removing it from
-    // mTracks won't cause the destructor to be called while the lock is
-    // held (note that technically, 'track' could be a reference to an item
-    // in mTracks, which is why we need to do this).
-    sp<Track> keep(track);
-    Mutex::Autolock _l(mLock);
     track->mState = TrackBase::TERMINATED;
     if (mActiveTracks.indexOf(track) < 0) {
         LOGV("remove track (%d) and delete from mixer", track->name());
         mTracks.remove(track);
-        deleteTrackName(keep->name());
+        deleteTrackName_l(track->name());
     }
 }
 
-
-void AudioFlinger::MixerThread::addActiveTrack(const wp<Track>& t)
+// addActiveTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::addActiveTrack_l(const wp<Track>& t)
 {
     mActiveTracks.add(t);
 
@@ -1476,7 +1458,8 @@
     }
 }
 
-void AudioFlinger::MixerThread::removeActiveTrack(const wp<Track>& t)
+// removeActiveTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::removeActiveTrack_l(const wp<Track>& t)
 {
     mActiveTracks.remove(t);
 
@@ -1492,12 +1475,14 @@
     }
 }
 
-int AudioFlinger::MixerThread::getTrackName()
+// getTrackName_l() must be called with AudioFlinger::mLock held
+int AudioFlinger::MixerThread::getTrackName_l()
 {
     return mAudioMixer->getTrackName();
 }
 
-void AudioFlinger::MixerThread::deleteTrackName(int name)
+// deleteTrackName_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::deleteTrackName_l(int name)
 {
     mAudioMixer->deleteTrackName(name);
 }
@@ -1509,6 +1494,7 @@
 
 // ----------------------------------------------------------------------------
 
+// TrackBase constructor must be called with AudioFlinger::mLock held
 AudioFlinger::MixerThread::TrackBase::TrackBase(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
@@ -1529,7 +1515,7 @@
         mFormat(format),
         mFlags(flags & ~SYSTEM_FLAGS_MASK)
 {
-    mName = mixerThread->getTrackName();
+    mName = mixerThread->getTrackName_l();
     LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
     if (mName < 0) {
         LOGE("no more track names availlable");
@@ -1661,6 +1647,7 @@
 
 // ----------------------------------------------------------------------------
 
+// Track constructor must be called with AudioFlinger::mLock held
 AudioFlinger::MixerThread::Track::Track(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
@@ -1681,13 +1668,26 @@
 AudioFlinger::MixerThread::Track::~Track()
 {
     wp<Track> weak(this); // never create a strong ref from the dtor
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
     mState = TERMINATED;
-    mMixerThread->removeTrack(weak, mName);
+    mMixerThread->removeTrack_l(weak, mName);
 }
 
 void AudioFlinger::MixerThread::Track::destroy()
 {
-    mMixerThread->destroyTrack(this);
+    // NOTE: destroyTrack_l() can remove a strong reference to this Track 
+    // by removing it from mTracks vector, so there is a risk that this Tracks's
+    // desctructor is called. As the destructor needs to lock AudioFlinger::mLock,
+    // we must acquire a strong reference on this Track before locking AudioFlinger::mLock
+    // here so that the destructor is called only when exiting this function.
+    // On the other hand, as long as Track::destroy() is only called by 
+    // TrackHandle destructor, the TrackHandle still holds a strong ref on 
+    // this Track with its member mTrack.
+    sp<Track> keep(this);
+    { // scope for AudioFlinger::mLock
+        Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+        mMixerThread->destroyTrack_l(this);
+    }
 }
 
 void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size)
@@ -1765,14 +1765,15 @@
 status_t AudioFlinger::MixerThread::Track::start()
 {
     LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
-    mMixerThread->addTrack(this);
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+    mMixerThread->addTrack_l(this);
     return NO_ERROR;
 }
 
 void AudioFlinger::MixerThread::Track::stop()
 {
     LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
-    Mutex::Autolock _l(mMixerThread->mLock);
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
     if (mState > STOPPED) {
         mState = STOPPED;
         // If the track is not active (PAUSED and buffers full), flush buffers
@@ -1786,7 +1787,7 @@
 void AudioFlinger::MixerThread::Track::pause()
 {
     LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
-    Mutex::Autolock _l(mMixerThread->mLock);
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
     if (mState == ACTIVE || mState == RESUMING) {
         mState = PAUSING;
         LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
@@ -1796,7 +1797,7 @@
 void AudioFlinger::MixerThread::Track::flush()
 {
     LOGV("flush(%d)", mName);
-    Mutex::Autolock _l(mMixerThread->mLock);
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
     if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
         return;
     }
@@ -1840,6 +1841,7 @@
 
 // ----------------------------------------------------------------------------
 
+// RecordTrack constructor must be called with AudioFlinger::mLock held
 AudioFlinger::MixerThread::RecordTrack::RecordTrack(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
@@ -1857,7 +1859,8 @@
 
 AudioFlinger::MixerThread::RecordTrack::~RecordTrack()
 {
-    mMixerThread->deleteTrackName(mName);
+    Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+    mMixerThread->deleteTrackName_l(mName);
 }
 
 status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
@@ -2183,7 +2186,6 @@
         uint32_t flags,
         status_t *status)
 {
-    sp<AudioRecordThread> thread;
     sp<MixerThread::RecordTrack> recordTrack;
     sp<RecordHandle> recordHandle;
     sp<Client> client;
@@ -2227,7 +2229,7 @@
     }
 
     // add client to list
-    {
+    { // scope for mLock
         Mutex::Autolock _l(mLock);
         wclient = mClients.valueFor(pid);
         if (wclient != NULL) {
@@ -2236,15 +2238,15 @@
             client = new Client(this, pid);
             mClients.add(pid, client);
         }
+
+        // frameCount must be a multiple of input buffer size
+        inFrameCount = inputBufferSize/channelCount/sizeof(short);
+        frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
+    
+        // create new record track. The record track uses one track in mHardwareMixerThread by convention.
+        recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
+                                                   format, channelCount, frameCount, flags);
     }
-
-    // frameCount must be a multiple of input buffer size
-    inFrameCount = inputBufferSize/channelCount/sizeof(short);
-    frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
-
-    // create new record track and pass to record thread
-    recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
-                                               format, channelCount, frameCount, flags);
     if (recordTrack->getCblk() == NULL) {
         recordTrack.clear();
         lStatus = NO_MEMORY;
@@ -2369,7 +2371,8 @@
         } else if (mRecordTrack != 0) {
 
             buffer.frameCount = inFrameCount;
-            if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+            if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR &&
+                       (int)buffer.frameCount == inFrameCount)) {
                 LOGV("AudioRecordThread read: %d frames", buffer.frameCount);
                 ssize_t bytesRead = input->read(buffer.raw, inBufferSize);
                 if (bytesRead < 0) {
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 77f064b..c505336 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -112,7 +112,7 @@
     
     virtual     size_t      getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
     
-    virtual     void        wakeUp();
+    virtual     void        wakeUp()    { mWaitWorkCV.broadcast(); }
     
     // IBinder::DeathRecipient
     virtual     void        binderDied(const wp<IBinder>& who);
@@ -161,7 +161,8 @@
     void                    doSetOutput(int outputType);
 
 #ifdef WITH_A2DP
-    void                    setA2dpEnabled(bool enable);
+    void                    setA2dpEnabled_l(bool enable);
+    void                    checkA2dpEnabledChange_l();
 #endif
     static bool             streamForcedToSpeaker(int streamType);
     
@@ -459,7 +460,7 @@
                     bool        isMusicActive() const;
         
                     
-        sp<Track> createTrack(
+                    sp<Track>   createTrack_l(
                                     const sp<AudioFlinger::Client>& client,
                                     int streamType,
                                     uint32_t sampleRate,
@@ -468,12 +469,10 @@
                                     int frameCount,
                                     const sp<IMemory>& sharedBuffer,
                                     status_t *status);
-
-                    void        wakeUp() { mWaitWorkCV.broadcast(); }
                     
-                    void        getTracks(SortedVector < sp<Track> >& tracks,
+                    void        getTracks_l(SortedVector < sp<Track> >& tracks,
                                           SortedVector < wp<Track> >& activeTracks);
-                    void        putTracks(SortedVector < sp<Track> >& tracks,
+                    void        putTracks_l(SortedVector < sp<Track> >& tracks,
                                           SortedVector < wp<Track> >& activeTracks);
                     void        setOuputTrack(OutputTrack *track) { mOutputTrack = track; }
                     
@@ -498,22 +497,19 @@
         MixerThread(const Client&);
         MixerThread& operator = (const MixerThread&);
   
-        status_t    addTrack(const sp<Track>& track);
-        void        removeTrack(wp<Track> track, int name);
-        void        remove_track_l(wp<Track> track, int name);
-        void        destroyTrack(const sp<Track>& track);
-        int         getTrackName();
-        void        deleteTrackName(int name);
-        void        addActiveTrack(const wp<Track>& t);
-        void        removeActiveTrack(const wp<Track>& t);
+        status_t    addTrack_l(const sp<Track>& track);
+        void        removeTrack_l(wp<Track> track, int name);
+        void        destroyTrack_l(const sp<Track>& track);
+        int         getTrackName_l();
+        void        deleteTrackName_l(int name);
+        void        addActiveTrack_l(const wp<Track>& t);
+        void        removeActiveTrack_l(const wp<Track>& t);
         size_t      getOutputFrameCount();
 
         status_t    dumpInternals(int fd, const Vector<String16>& args);
         status_t    dumpTracks(int fd, const Vector<String16>& args);
         
         sp<AudioFlinger>                mAudioFlinger;       
-        mutable     Mutex               mLock;
-        mutable     Condition           mWaitWorkCV;
         SortedVector< wp<Track> >       mActiveTracks;
         SortedVector< sp<Track> >       mTracks;
         stream_type_t                   mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
@@ -607,11 +603,11 @@
 
                 status_t    startRecord(MixerThread::RecordTrack* recordTrack);
                 void        stopRecord(MixerThread::RecordTrack* recordTrack);
-                
-                void        handleOutputSwitch();
 
-    mutable     Mutex                                       mHardwareLock;
-    mutable     Mutex                                       mLock;
+    mutable     Mutex                               mHardwareLock;
+    mutable     Mutex                               mLock;
+    mutable     Condition                           mWaitWorkCV;
+
                 DefaultKeyedVector< pid_t, wp<Client> >     mClients;
 
                 sp<MixerThread>                     mA2dpMixerThread;
@@ -620,7 +616,7 @@
                 AudioHardwareInterface*             mA2dpAudioInterface;
                 sp<AudioRecordThread>               mAudioRecordThread;
                 bool                                mA2dpEnabled;
-                bool                                mA2dpEnabledReq;
+                bool                                mNotifyA2dpChange;
     mutable     int                                 mHardwareStatus;
                 SortedVector< wp<IBinder> >         mNotificationClients;
                 int                                 mForcedSpeakerCount;
diff --git a/location/data/Android.mk b/location/data/Android.mk
index befd792..794e6c7 100644
--- a/location/data/Android.mk
+++ b/location/data/Android.mk
@@ -13,7 +13,7 @@
 
 LOCAL_MODULE := nmea
 
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(local_target_dir)/gps
@@ -27,7 +27,7 @@
 
 LOCAL_MODULE := location
 
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(local_target_dir)/gps
@@ -41,7 +41,7 @@
 
 LOCAL_MODULE := properties
 
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(local_target_dir)/gps
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5416629..8560593 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -182,8 +182,9 @@
             mLock.lock();
             if (mState == TONE_STARTING) {
                 LOGV("Wait for start callback");
-                if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) {
-                    LOGE("--- Immediate start timed out");
+                status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+                if (lStatus != NO_ERROR) {
+                    LOGE("--- Immediate start timed out, status %d", lStatus);
                     mState = TONE_IDLE;
                     lResult = false;
                 }
@@ -195,13 +196,14 @@
         LOGV("Delayed start\n");
 
         mState = TONE_RESTARTING;
-        if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) {
+        status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+        if (lStatus == NO_ERROR) {
             if (mState != TONE_IDLE) {
                 lResult = true;
             }
             LOGV("cond received");
         } else {
-            LOGE("--- Delayed start timed out");
+            LOGE("--- Delayed start timed out, status %d", lStatus);
             mState = TONE_IDLE;
         }
     }
@@ -368,6 +370,8 @@
             break;
         default:
             LOGV("Extra Cbk");
+            // Force loop exit
+            lNumSmp = 0;
             goto audioCallback_EndLoop;
         }
         
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 5e9c488..07b43bb 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -494,4 +494,18 @@
     "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp",
     "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4"
   };
+  
+  //Streaming test files
+  public static final String STREAM_H264_480_360_1411k = 
+      "http://sridharg.googlejunta.com/yslau/stress_media/h264_regular.mp4"; 
+  public static final String STREAM_WMV = 
+      "http://sridharg.googlejunta.com/yslau/stress_media/bugs.wmv"; 
+  public static final String STREAM_H263_176x144_325k = 
+      "http://sridharg.googlejunta.com/yslau/stress_media/h263_regular.3gp";
+  public static final String STREAM_H264_352x288_1536k = 
+      "http://sridharg.googlejunta.com/yslau/stress_media/h264_highBitRate.mp4";
+  public static final String STREAM_MP3= 
+      "http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3";
+  public static final String STREAM_MPEG4_QVGA_128k = 
+      "http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp";
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index 0e88719..caba47c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -28,6 +28,7 @@
 import android.media.MediaMetadataRetriever;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -39,6 +40,16 @@
  */  
 public class CodecTest {    
     private static String TAG = "MediaPlayerApiTest";
+    private static MediaPlayer mMediaPlayer;
+    private MediaPlayer.OnPreparedListener mOnPreparedListener;
+    
+    private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000;  //10 seconds max.
+    private static boolean mInitialized = false;
+    private static Looper mLooper = null;
+    private static final Object lock = new Object();
+    private static final Object prepareDone = new Object();
+    private static boolean onPrepareSuccess = false;
+    
 
     public static String printCpuInfo(){      
         String cm = "dumpsys cpuinfo";
@@ -573,5 +584,89 @@
 
         return true;
     }
+    
+    /*
+     * Initializes the message looper so that the mediaPlayer object can 
+     * receive the callback messages.
+     */
+    private static void initializeMessageLooper() {
+        Log.v(TAG, "start looper");
+        new Thread() {
+            @Override
+            public void run() {
+                // Set up a looper to be used by camera.
+                Looper.prepare();
+                Log.v(TAG, "start loopRun");
+                // Save the looper so that we can terminate this thread 
+                // after we are done with it.
+                mLooper = Looper.myLooper();                
+                mMediaPlayer = new MediaPlayer();                                
+                synchronized (lock) {
+                    mInitialized = true;
+                    lock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+                Log.v(TAG, "initializeMessageLooper: quit.");
+            }
+        }.start();
+    }
+    
+    /*
+     * Terminates the message looper thread.
+     */
+    private static void terminateMessageLooper() {
+        mLooper.quit();
+        mMediaPlayer.release();
+    }
+    
+    static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
+        public void onPrepared(MediaPlayer mp) {
+            synchronized (prepareDone) {
+                Log.v(TAG, "notify the prepare callback");
+                prepareDone.notify();
+                onPrepareSuccess = true;
+            }
+        }
+    };
+   
+    public static boolean prepareAsyncCallback(String filePath) throws Exception {
+        int videoWidth = 0;
+        int videoHeight = 0;
+        boolean checkVideoDimension = false;
+        
+        initializeMessageLooper();
+        synchronized (lock) {
+            try {
+                lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+            } catch(Exception e) {
+                Log.v(TAG, "looper was interrupted.");
+                return false;
+            }
+        }
+        try{
+            mMediaPlayer.setOnPreparedListener(mPreparedListener);
+            mMediaPlayer.setDataSource(filePath);
+            mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+            mMediaPlayer.prepareAsync(); 
+            synchronized (prepareDone) {
+                try {
+                    prepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+                    Log.v(TAG, "setPreview done");
+                } catch (Exception e) {
+                    Log.v(TAG, "wait was interrupted.");
+                }
+            }
+            videoWidth = mMediaPlayer.getVideoWidth();
+            videoHeight = mMediaPlayer.getVideoHeight();
+            
+            terminateMessageLooper();
+        }catch (Exception e){
+            Log.v(TAG,e.getMessage());
+        }      
+       return onPrepareSuccess;
+    }
+    
+    
+    
 }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index dd94164..ee6a727 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -52,7 +52,8 @@
         return true;
     }   
     
-   
+
+    
     //Audio    
     //Wait for PV bugs for MP3 duration
     @MediumTest
@@ -410,8 +411,6 @@
       assertTrue("Play mp3 from resources", mp3Resources);         
     }
     
-    //Bug# 1422662
-    @Suppress
     @MediumTest
     public void testPrepareAsyncReset() throws Exception {
       boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_LARGE_MP3);
@@ -429,5 +428,19 @@
         boolean isLooping = CodecTest.isLoopingAfterReset(MediaNames.AMR);
         assertTrue("isLooping after reset", isLooping);
     }
+    
+    @LargeTest
+    public void testLocalMp3PrepareAsyncCallback() throws Exception {
+        boolean onPrepareSuccess = 
+            CodecTest.prepareAsyncCallback(MediaNames.VIDEO_H263_AMR);
+        assertTrue("LocalMp3prepareAsyncCallback", onPrepareSuccess);
+    }
+    
+    @LargeTest
+    public void testStreamPrepareAsyncCallback() throws Exception {
+        boolean onPrepareSuccess = 
+            CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k);
+        assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess);
+    }
 }
 
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index 330a673..724e988 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/SettingsProvider/etc/Android.mk b/packages/SettingsProvider/etc/Android.mk
index e3f958c..d73175f 100644
--- a/packages/SettingsProvider/etc/Android.mk
+++ b/packages/SettingsProvider/etc/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE := bookmarks.xml
 
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
 
 # This will install the file in /system/etc
 #
@@ -36,7 +36,7 @@
 
 LOCAL_MODULE := favorites.xml
 
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
 
 # This will install the file in /system/etc
 #
diff --git a/services/Android.mk b/services/java/Android.mk
similarity index 100%
rename from services/Android.mk
rename to services/java/Android.mk
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a254081..9948322 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -196,6 +196,11 @@
     ClientState mCurClient;
     
     /**
+     * The last window token that gained focus.
+     */
+    IBinder mCurFocusedWindow;
+    
+    /**
      * The input context last provided by the current client.
      */
     IInputContext mCurInputContext;
@@ -557,7 +562,7 @@
          }
     }
     
-    void unbindCurrentInputLocked() {
+    void unbindCurrentClientLocked() {
         if (mCurClient != null) {
             if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = "
                     + mCurClient.client.asBinder());
@@ -658,7 +663,7 @@
         if (mCurClient != cs) {
             // If the client is changing, we need to switch over to the new
             // one.
-            unbindCurrentInputLocked();
+            unbindCurrentClientLocked();
             if (DEBUG) Log.v(TAG, "switching to client: client = "
                     + cs.client.asBinder());
 
@@ -721,21 +726,7 @@
             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
         }
         
-        if (mHaveConnection) {
-            mContext.unbindService(this);
-            mHaveConnection = false;
-        }
-        
-        if (mCurToken != null) {
-            try {
-                if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
-                mIWindowManager.removeWindowToken(mCurToken);
-            } catch (RemoteException e) {
-            }
-            mCurToken = null;
-        }
-        
-        clearCurMethod();
+        unbindCurrentMethodLocked(false);
         
         mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
         mCurIntent.setComponent(info.getComponent());
@@ -814,7 +805,30 @@
         }
     }
     
-    void clearCurMethod() {
+    void unbindCurrentMethodLocked(boolean reportToClient) {
+        if (mHaveConnection) {
+            mContext.unbindService(this);
+            mHaveConnection = false;
+        }
+        
+        if (mCurToken != null) {
+            try {
+                if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
+                mIWindowManager.removeWindowToken(mCurToken);
+            } catch (RemoteException e) {
+            }
+            mCurToken = null;
+        }
+        
+        clearCurMethodLocked();
+        
+        if (reportToClient && mCurClient != null) {
+            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+        }
+    }
+    
+    void clearCurMethodLocked() {
         if (mCurMethod != null) {
             for (ClientState cs : mClients.values()) {
                 cs.sessionRequested = false;
@@ -831,7 +845,7 @@
                     + " mCurIntent=" + mCurIntent);
             if (mCurMethod != null && mCurIntent != null
                     && name.equals(mCurIntent.getComponent())) {
-                clearCurMethod();
+                clearCurMethodLocked();
                 // We consider this to be a new bind attempt, since the system
                 // should now try to restart the service for us.
                 mLastBindTime = SystemClock.uptimeMillis();
@@ -871,14 +885,22 @@
     }
 
     void updateFromSettingsLocked() {
+        // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
+        // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
+        // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
+        // enabled.
         String id = Settings.Secure.getString(mContext.getContentResolver(),
             Settings.Secure.DEFAULT_INPUT_METHOD);
-        if (id != null) {
+        if (id != null && id.length() > 0) {
             try {
                 setInputMethodLocked(id);
             } catch (IllegalArgumentException e) {
                 Log.w(TAG, "Unknown input method from prefs: " + id, e);
+                unbindCurrentMethodLocked(true);
             }
+        } else {
+            // There is no longer an input method set, so stop any current one.
+            unbindCurrentMethodLocked(true);
         }
     }
     
@@ -903,7 +925,7 @@
                 intent.putExtra("input_method_id", id);
                 mContext.sendBroadcast(intent);
             }
-            unbindCurrentInputLocked();
+            unbindCurrentClientLocked();
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1023,7 +1045,7 @@
         return res;
     }
     
-    public void windowGainedFocus(IInputMethodClient client,
+    public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
             boolean viewHasFocus, boolean isTextEditor, int softInputMode,
             boolean first, int windowFlags) {
         long ident = Binder.clearCallingIdentity();
@@ -1043,13 +1065,19 @@
                         // focus in the window manager, to allow this call to
                         // be made before input is started in it.
                         if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                            Log.w(TAG, "Ignoring focus gain of: " + client);
+                            Log.w(TAG, "Client not active, ignoring focus gain of: " + client);
                             return;
                         }
                     } catch (RemoteException e) {
                     }
                 }
     
+                if (mCurFocusedWindow == windowToken) {
+                    Log.w(TAG, "Window already focused, ignoring focus gain of: " + client);
+                    return;
+                }
+                mCurFocusedWindow = windowToken;
+                
                 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
                         if (!isTextEditor || (softInputMode &
@@ -1558,7 +1586,8 @@
             p.println("  mInputMethodData=" + mInputMethodData);
             p.println("  mCurrentMethod=" + mCurMethodId);
             client = mCurClient;
-            p.println("  mCurSeq=" + mCurSeq + " mCurClient=" + client);
+            p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
+            p.println("  mCurFocusedWindow=" + mCurFocusedWindow);
             p.println("  mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
                     + " mBoundToMethod=" + mBoundToMethod);
             p.println("  mCurToken=" + mCurToken);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index eece581..e298f49 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -22,9 +22,9 @@
 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
 
-import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.bluetooth.BluetoothA2dp;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1491,6 +1491,12 @@
                     return;
                 }
                 mPluggedType = pluggedType;
+            } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+                boolean isBluetoothPlaying =
+                        intent.getIntExtra(
+                                BluetoothA2dp.SINK_STATE,
+                                BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING;
+                mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
             } else {
                 return;
             }
@@ -1603,13 +1609,11 @@
 
     private void registerForBroadcasts() {
         IntentFilter intentFilter = new IntentFilter();
-        if (isAirplaneSensitive()) {
-            intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        }
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         intentFilter.addAction(ACTION_DEVICE_IDLE);
+        intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
         mContext.registerReceiver(mReceiver, intentFilter);
     }
     
diff --git a/core/jni/server/Android.mk b/services/jni/Android.mk
similarity index 100%
rename from core/jni/server/Android.mk
rename to services/jni/Android.mk
diff --git a/core/jni/server/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_AlarmManagerService.cpp
rename to services/jni/com_android_server_AlarmManagerService.cpp
diff --git a/core/jni/server/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_BatteryService.cpp
rename to services/jni/com_android_server_BatteryService.cpp
diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_HardwareService.cpp
rename to services/jni/com_android_server_HardwareService.cpp
diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
similarity index 100%
rename from core/jni/server/com_android_server_KeyInputQueue.cpp
rename to services/jni/com_android_server_KeyInputQueue.cpp
diff --git a/core/jni/server/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_SensorService.cpp
rename to services/jni/com_android_server_SensorService.cpp
diff --git a/core/jni/server/com_android_server_SystemServer.cpp b/services/jni/com_android_server_SystemServer.cpp
similarity index 100%
rename from core/jni/server/com_android_server_SystemServer.cpp
rename to services/jni/com_android_server_SystemServer.cpp
diff --git a/core/jni/server/onload.cpp b/services/jni/onload.cpp
similarity index 100%
rename from core/jni/server/onload.cpp
rename to services/jni/onload.cpp
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
index de3e68b..d731243 100644
--- a/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
+++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
@@ -21,6 +21,7 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.test.ViewAsserts;
 import android.view.View;
 
@@ -73,6 +74,7 @@
         ViewAsserts.assertVerticalCenterAligned(mReference2, mCenter);
     }
 
+    @Suppress
     @MediumTest
     public void testBottomGravity() throws Exception {
         ViewAsserts.assertBottomAligned(mReference3, mBottom);
diff --git a/tests/StatusBar/Android.mk b/tests/StatusBar/Android.mk
index 44f5099..18fcad0 100644
--- a/tests/StatusBar/Android.mk
+++ b/tests/StatusBar/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := test
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 2a47a71..3851ac0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -117,6 +117,16 @@
      * @return Whether the mode was successfully set.
      */
     public native static boolean setBluetoothCoexistenceModeCommand(int mode);
+
+    /**
+     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+     * some of the low-level scan parameters used by the driver are changed to
+     * reduce interference with A2DP streaming.
+     *
+     * @param isSet whether to enable or disable this mode
+     * @return {@code true} if the command succeeded, {@code false} otherwise.
+     */
+    public native static boolean setBluetoothCoexistenceScanModeCommand(boolean setCoexScanMode);
     
     public native static boolean saveConfigCommand();
 
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f0009be..452a8fa 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -38,6 +38,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.Context;
@@ -244,6 +245,10 @@
     private int mRunState;
 
     private boolean mIsScanOnly;
+
+    private BluetoothA2dp mBluetoothA2dp;
+
+    private boolean mBluetoothScanMode;
     
     private String mInterfaceName;
     private static String LS = System.getProperty("line.separator");
@@ -577,6 +582,30 @@
         }
     }
 
+    /**
+     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+     * some of the low-level scan parameters used by the driver are changed to
+     * reduce interference with A2DP streaming.
+     *
+     * @param isBluetoothPlaying whether to enable or disable this mode
+     */
+    public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
+        WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
+    }
+
+    private void checkIsBluetoothPlaying() {
+        boolean isBluetoothPlaying = false;
+        List<String> connected = mBluetoothA2dp.listConnectedSinks();
+
+        for (String address : connected) {
+            if (mBluetoothA2dp.getSinkState(address) == BluetoothA2dp.STATE_PLAYING) {
+                isBluetoothPlaying = true;
+                break;
+            }
+        }
+        setBluetoothScanMode(isBluetoothPlaying);
+    }
+
     @Override
     public void releaseWakeLock() {
         if (mReleaseWakeLockCallback != null) {
@@ -682,9 +711,13 @@
                  * are going to end up being thrown away. Obviously, if we
                  * ever want to support multicast, this will have to change.
                  */
+                if (mBluetoothA2dp == null) {
+                    mBluetoothA2dp = new BluetoothA2dp(mContext);
+                }
                 synchronized (this) {
                     WifiNative.startPacketFiltering();
                 }
+                checkIsBluetoothPlaying();
                 break;
 
             case EVENT_SUPPLICANT_DISCONNECT: