Merge "Fix bcm4329 FW dump in bugreport for user-debug build"
diff --git a/api/current.xml b/api/current.xml
index d3e673e..9dc2218 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -80540,6 +80540,17 @@
  visibility="public"
 >
 </method>
+<method name="isBluetoothScoAvailableOffCall"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isBluetoothScoOn"
  return="boolean"
  abstract="false"
@@ -80860,6 +80871,28 @@
 <parameter name="vibrateType" type="int">
 </parameter>
 </method>
+<method name="startBluetoothSco"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stopBluetoothSco"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="unloadSoundEffects"
  return="void"
  abstract="false"
@@ -80895,6 +80928,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_SCO_AUDIO_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.SCO_AUDIO_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ADJUST_LOWER"
  type="int"
  transient="false"
@@ -80950,6 +80994,17 @@
  visibility="public"
 >
 </field>
+<field name="AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AUDIOFOCUS_LOSS"
  type="int"
  transient="false"
@@ -80972,6 +81027,17 @@
  visibility="public"
 >
 </field>
+<field name="AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AUDIOFOCUS_REQUEST_FAILED"
  type="int"
  transient="false"
@@ -81005,6 +81071,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_SCO_AUDIO_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.media.extra.SCO_AUDIO_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_VIBRATE_SETTING"
  type="java.lang.String"
  transient="false"
@@ -81368,6 +81445,39 @@
  visibility="public"
 >
 </field>
+<field name="SCO_AUDIO_STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCO_AUDIO_STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCO_AUDIO_STATE_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="STREAM_ALARM"
  type="int"
  transient="false"
@@ -193625,7 +193735,7 @@
  visibility="public"
 >
 </method>
-<method name="getUseSystemOverscrollBackground"
+<method name="getUseWebViewBackgroundForOverscrollBackground"
  return="boolean"
  abstract="false"
  native="false"
@@ -194215,7 +194325,7 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
-<method name="setUseSystemOverscrollBackground"
+<method name="setUseWebViewBackgroundForOverscrollBackground"
  return="void"
  abstract="false"
  native="false"
@@ -194225,7 +194335,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="system" type="boolean">
+<parameter name="view" type="boolean">
 </parameter>
 </method>
 <method name="setUseWideViewPort"
@@ -199610,6 +199720,8 @@
 >
 <implements name="android.widget.ExpandableListAdapter">
 </implements>
+<implements name="android.widget.HeterogeneousExpandableList">
+</implements>
 <constructor name="BaseExpandableListAdapter"
  type="android.widget.BaseExpandableListAdapter"
  static="false"
@@ -199629,6 +199741,32 @@
  visibility="public"
 >
 </method>
+<method name="getChildType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getCombinedChildId"
  return="long"
  abstract="false"
@@ -199657,6 +199795,30 @@
 <parameter name="groupId" type="long">
 </parameter>
 </method>
+<method name="getGroupType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isEmpty"
  return="boolean"
  abstract="false"
@@ -203399,6 +203561,64 @@
 </parameter>
 </method>
 </class>
+<interface name="HeterogeneousExpandableList"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getChildType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGroupType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <class name="HorizontalScrollView"
  extends="android.widget.FrameLayout"
  abstract="false"
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp
index 394ce41..c4fa765 100644
--- a/cmds/rawbu/backup.cpp
+++ b/cmds/rawbu/backup.cpp
@@ -318,7 +318,7 @@
                 result = 0;
                 goto done;
             }
-        } else {
+        } else if (S_ISREG(statBuffer.st_mode)) {
             printf("Saving file %s...\n", fullPath);
             
             if (write_header(fh, TYPE_FILE, fullPath, &statBuffer) == 0) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4464ab9..f296f43 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2935,9 +2935,14 @@
         private boolean writeFileLocked() {
             // Rename the current file so it may be used as a backup during the next read
             if (mFile.exists()) {
-                if (!mFile.renameTo(mBackupFile)) {
-                    Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile);
-                    return false;
+                if (!mBackupFile.exists()) {
+                    if (!mFile.renameTo(mBackupFile)) {
+                        Log.e(TAG, "Couldn't rename file " + mFile
+                                + " to backup file " + mBackupFile);
+                        return false;
+                    }
+                } else {
+                    mFile.delete();
                 }
             }
             
diff --git a/core/java/android/bluetooth/ScoSocket.java b/core/java/android/bluetooth/ScoSocket.java
index 116310a..b65a99a 100644
--- a/core/java/android/bluetooth/ScoSocket.java
+++ b/core/java/android/bluetooth/ScoSocket.java
@@ -86,14 +86,14 @@
     /** Connect this SCO socket to the given BT address.
      *  Does not block.
      */
-    public synchronized boolean connect(String address) {
+    public synchronized boolean connect(String address, String name) {
         if (DBG) log("connect() " + this);
         if (mState != STATE_READY) {
             if (DBG) log("connect(): Bad state");
             return false;
         }
         acquireWakeLock();
-        if (connectNative(address)) {
+        if (connectNative(address, name)) {
             mState = STATE_CONNECTING;
             return true;
         } else {
@@ -102,7 +102,7 @@
             return false;
         }
     }
-    private native boolean connectNative(String address);
+    private native boolean connectNative(String address, String name);
 
     /** Accept incoming SCO connections.
      *  Does not block.
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index e9a9f31..af327c3 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -153,6 +153,12 @@
      * the {@link android.R.attr#installLocation} attribute.
      * @hide
      */
+    public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+    /**
+     * Constant corresponding to <code>auto</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
     public static final int INSTALL_LOCATION_AUTO = 0;
     /**
      * Constant corresponding to <code>internalOnly</code> in
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c33f305..3be4cebd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -96,7 +96,8 @@
     private static final Object mSync = new Object();
     private static WeakReference<byte[]> mReadBuffer;
 
-    private static boolean sCompatibilityModeEnabled = true; 
+    private static boolean sCompatibilityModeEnabled = true;
+    private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
 
     static class ParsePackageItemArgs {
         final Package owner;
@@ -352,6 +353,7 @@
     public final static int PARSE_IGNORE_PROCESSES = 1<<3;
     public final static int PARSE_FORWARD_LOCK = 1<<4;
     public final static int PARSE_ON_SDCARD = 1<<5;
+    public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
 
     public int getParseError() {
         return mParseError;
@@ -707,12 +709,12 @@
                 + pkgName + "\": " + nameError;
             return null;
         }
-        int installLocation = PackageInfo.INSTALL_LOCATION_AUTO;
+        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
             String attr = attrs.getAttributeName(i);
             if (attr.equals("installLocation")) {
                 installLocation = attrs.getAttributeIntValue(i,
-                        PackageInfo.INSTALL_LOCATION_AUTO);
+                        PARSE_DEFAULT_INSTALL_LOCATION);
                 break;
             }
         }
@@ -778,7 +780,7 @@
 
         pkg.installLocation = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_installLocation,
-                PackageInfo.INSTALL_LOCATION_AUTO);
+                PARSE_DEFAULT_INSTALL_LOCATION);
 
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 816f8a8..72ceb9b 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -139,7 +139,10 @@
             if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
                 Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
             }
-            Log.w(TAG, "finalizer should never be called on sql: " + mSqlStmt, mStackTrace);
+            int len = mSqlStmt.length();
+            Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
+                    "that you explicitly call close() on your cursor: " +
+                    mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
             releaseSqlStatement();
         } finally {
             super.finalize();
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4780cf3..a17b7fe 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -115,6 +115,9 @@
      */
     public static boolean copyToFile(InputStream inputStream, File destFile) {
         try {
+            if (destFile.exists()) {
+                destFile.delete();
+            }
             OutputStream out = new FileOutputStream(destFile);
             try {
                 byte[] buffer = new byte[4096];
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 837ce91..726793d 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -24,7 +24,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;
-import android.view.Window;
 
 /**
  * Shows a hierarchy of {@link Preference} objects as
@@ -81,6 +80,8 @@
     
     private PreferenceManager mPreferenceManager;
     
+    private Bundle mSavedInstanceState;
+
     /**
      * The starting request code given out to preference framework.
      */
@@ -137,15 +138,19 @@
 
     @Override
     protected void onRestoreInstanceState(Bundle state) {
-        super.onRestoreInstanceState(state);
-
         Bundle container = state.getBundle(PREFERENCES_TAG);
         if (container != null) {
             final PreferenceScreen preferenceScreen = getPreferenceScreen();
             if (preferenceScreen != null) {
                 preferenceScreen.restoreHierarchyState(container);
+                mSavedInstanceState = state;
+                return;
             }
         }
+
+        // Only call this if we didn't save the instance state for later.
+        // If we did save it, it will be restored when we bind the adapter.
+        super.onRestoreInstanceState(state);
     }
 
     @Override
@@ -176,6 +181,10 @@
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         if (preferenceScreen != null) {
             preferenceScreen.bind(getListView());
+            if (mSavedInstanceState != null) {
+                super.onRestoreInstanceState(mSavedInstanceState);
+                mSavedInstanceState = null;
+            }
         }
     }
     
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index dda9018..1d27828 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -870,6 +870,13 @@
             public static final String ARTIST = "artist";
 
             /**
+             * The artist credited for the album that contains the audio file
+             * <P>Type: TEXT</P>
+             * @hide
+             */
+            public static final String ALBUM_ARTIST = "album_artist";
+
+            /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
              * <P>Type: TEXT</P>
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 984e48b..7e748c0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5728,7 +5728,7 @@
      * of the thumb within the scrollbar's track.</p>
      *
      * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeHorizontalScrollRange()} and
+     * units used by {@link #computeVerticalScrollRange()} and
      * {@link #computeVerticalScrollOffset()}.</p>
      *
      * <p>The default extent is the drawing height of this view.</p>
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8311bdc..2b5489c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -546,6 +546,9 @@
         recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
         recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
         try {
+            if (recyclerDump.exists()) {
+                recyclerDump.delete();
+            }
             final FileOutputStream file = new FileOutputStream(recyclerDump);
             final DataOutputStream out = new DataOutputStream(file);
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index c87ffee..9df8ac3 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1675,6 +1675,16 @@
         return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
     }
 
+    private static void forceLayout(View view) {
+        view.forceLayout();
+        if (view instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) view;
+            final int count = group.getChildCount();
+            for (int i = 0; i < count; i++) {
+                forceLayout(group.getChildAt(i));
+            }
+        }
+    }
 
     public final static int DO_TRAVERSAL = 1000;
     public final static int DIE = 1001;
@@ -1862,6 +1872,10 @@
                 if (msg.what == RESIZED_REPORT) {
                     mReportNextDraw = true;
                 }
+
+                if (mView != null) {
+                    forceLayout(mView);
+                }
                 requestLayout();
             }
             break;
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 176169e..2709cff 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -28,11 +28,11 @@
 public class DecelerateInterpolator implements Interpolator {
     public DecelerateInterpolator() {
     }
-    
+
     /**
      * Constructor
      * 
-     * @param factor Degree to which the animation should be eased. Seting factor to 1.0f produces
+     * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
      *        an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
      *        ease-out effect (i.e., it starts even faster and ends evens slower)
      */
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index 1c17575..6216603 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 
 import java.util.ListIterator;
 import java.util.LinkedList;
@@ -52,6 +53,14 @@
     private static final int AUTH_PROCEED = 100;
     private static final int AUTH_CANCEL = 200;
 
+    // Use to synchronize when making synchronous calls to
+    // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
+    // both the lock and the state, because Boolean is immutable.
+    Object mRequestInFlightLock = new Object();
+    boolean mRequestInFlight;
+    String mUsername;
+    String mPassword;
+
     /**
      * Creates a new HTTP authentication handler with an empty
      * loader queue
@@ -70,6 +79,7 @@
         synchronized (mLoaderQueue) {
             loader = mLoaderQueue.poll();
         }
+        assert(loader.isSynchronous() == false);
 
         switch (msg.what) {
             case AUTH_PROCEED:
@@ -87,25 +97,70 @@
         processNextLoader();
     }
 
+    /**
+     * Helper method used to unblock handleAuthRequest(), which in the case of a
+     * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
+     * call back to either proceed() or cancel().
+     *
+     * @param username The username to use for authentication
+     * @param password The password to use for authentication
+     * @return True if the request is synchronous and handleAuthRequest() has
+     * been unblocked
+     */
+    private boolean handleResponseForSynchronousRequest(String username, String password) {
+        LoadListener loader = null;
+        synchronized (mLoaderQueue) {
+            loader = mLoaderQueue.peek();
+        }
+        if (loader.isSynchronous()) {
+            mUsername = username;
+            mPassword = password;
+            return true;
+        }
+        return false;
+    }
+
+    private void signalRequestComplete() {
+        synchronized (mRequestInFlightLock) {
+            assert(mRequestInFlight);
+            mRequestInFlight = false;
+            mRequestInFlightLock.notify();
+        }
+    }
 
     /**
      * Proceed with the authorization with the given credentials
      *
+     * May be called on the UI thread, rather than the WebCore thread.
+     *
      * @param username The username to use for authentication
      * @param password The password to use for authentication
      */
     public void proceed(String username, String password) {
+        if (handleResponseForSynchronousRequest(username, password)) {
+            signalRequestComplete();
+            return;
+        }
         Message msg = obtainMessage(AUTH_PROCEED);
         msg.getData().putString("username", username);
         msg.getData().putString("password", password);
         sendMessage(msg);
+        signalRequestComplete();
     }
 
     /**
      * Cancel the authorization request
+     *
+     * May be called on the UI thread, rather than the WebCore thread.
+     *
      */
     public void cancel() {
+        if (handleResponseForSynchronousRequest(null, null)) {
+            signalRequestComplete();
+            return;
+        }
         sendMessage(obtainMessage(AUTH_CANCEL));
+        signalRequestComplete();
     }
 
     /**
@@ -132,6 +187,34 @@
      * authentication request
      */
     /* package */ void handleAuthRequest(LoadListener loader) {
+        // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
+        // the request is synchronous, we must block here until we have a
+        // response.
+        if (loader.isSynchronous()) {
+            // If there's a request in flight, wait for it to complete. The
+            // response will queue a message on this thread.
+            waitForRequestToComplete();
+            // Make a request to the proxy for this request, jumping the queue.
+            // We use the queue so that the loader is present in
+            // useHttpAuthUsernamePassword().
+            synchronized (mLoaderQueue) {
+                mLoaderQueue.addFirst(loader);
+            }
+            processNextLoader();
+            // Wait for this request to complete.
+            waitForRequestToComplete();
+            // Pop the loader from the queue.
+            synchronized (mLoaderQueue) {
+                assert(mLoaderQueue.peek() == loader);
+                mLoaderQueue.poll();
+            }
+            // Call back.
+            loader.handleAuthResponse(mUsername, mPassword);
+            // The message queued by the response from the last asynchronous
+            // request, if present, will start the next request.
+            return;
+        }
+
         boolean processNext = false;
 
         synchronized (mLoaderQueue) {
@@ -146,6 +229,21 @@
     }
 
     /**
+     * Wait for the request in flight, if any, to complete
+     */
+    private void waitForRequestToComplete() {
+        synchronized (mRequestInFlightLock) {
+            while (mRequestInFlight) {
+                try {
+                    mRequestInFlightLock.wait();
+                } catch(InterruptedException e) {
+                    Log.e(LOGTAG, "Interrupted while waiting for request to complete");
+                }
+            }
+        }
+    }
+
+    /**
      * Process the next loader in the queue (helper method)
      */
     private void processNextLoader() {
@@ -154,6 +252,11 @@
             loader = mLoaderQueue.peek();
         }
         if (loader != null) {
+            synchronized (mRequestInFlightLock) {
+                assert(mRequestInFlight == false);
+                mRequestInFlight = true;
+            }
+
             CallbackProxy proxy = loader.getFrame().getCallbackProxy();
 
             String hostname = loader.proxyAuthenticate() ?
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 4e0e081..fc5c425 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -18,6 +18,7 @@
 
 import android.view.SurfaceView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.AbsoluteLayout;
 
 import java.util.ArrayList;
@@ -155,17 +156,18 @@
             v.isFixedSize = true;
         }
 
-        AbsoluteLayout.LayoutParams lp =
-            (AbsoluteLayout.LayoutParams) v.mView.getLayoutParams();
+        AbsoluteLayout.LayoutParams lp;
+        ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams();
 
-        if (lp == null)
-            lp = new AbsoluteLayout.LayoutParams(ctvD(v.width), ctvD(v.height),
-                    ctvX(v.x), ctvY(v.y));
-        else {
+        if (layoutParams instanceof AbsoluteLayout.LayoutParams) {
+            lp = (AbsoluteLayout.LayoutParams) layoutParams;
             lp.width = ctvD(v.width);
             lp.height = ctvD(v.height);
             lp.x = ctvX(v.x);
             lp.y = ctvY(v.y);
+        } else {
+            lp = new AbsoluteLayout.LayoutParams(ctvD(v.width), ctvD(v.height),
+                    ctvX(v.x), ctvY(v.y));
         }
         return lp;
     }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index fb15f78..d1da5ea 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -192,7 +192,7 @@
     private boolean         mBuiltInZoomControls = false;
     private boolean         mAllowFileAccess = true;
     private boolean         mLoadWithOverviewMode = false;
-    private boolean         mUseSystemOverscrollBackground = false;
+    private boolean         mUseWebViewBackgroundOverscrollBackground = true;
 
     // private WebSettings, not accessible by the host activity
     static private int      mDoubleTapToastCount = 3;
@@ -471,20 +471,20 @@
     }
 
     /**
-     * Set whether the WebView uses system background for over scroll
-     * background. If false, it will use the WebView's background. Default is
-     * false.
+     * Set whether the WebView uses its background for over scroll background.
+     * If true, it will use the WebView's background. If false, it will use an
+     * internal pattern. Default is true.
      */
-    public void setUseSystemOverscrollBackground(boolean system) {
-        mUseSystemOverscrollBackground = system;
+    public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+        mUseWebViewBackgroundOverscrollBackground = view;
     }
 
     /**
-     * Returns true if this WebView uses system background instead of WebView
-     * background for over scroll background.
+     * Returns true if this WebView uses WebView's background instead of
+     * internal pattern for over scroll background.
      */
-    public boolean getUseSystemOverscrollBackground() {
-        return mUseSystemOverscrollBackground;
+    public boolean getUseWebViewBackgroundForOverscrollBackground() {
+        return mUseWebViewBackgroundOverscrollBackground;
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 622d22d..8f7bf8d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1903,15 +1903,8 @@
     // Expects y in view coordinates
     private int pinLocY(int y) {
         if (mInOverScrollMode) return y;
-        int titleH = getTitleHeight();
-        // if the titlebar is still visible, just pin against 0
-        if (y <= titleH) {
-            return Math.max(y, 0);
-        }
-        // convert to 0-based coordinate (subtract the title height)
-        // pin(), and then add the title height back in
-        return pinLoc(y - titleH, getViewHeight(),
-                      computeVerticalScrollRange()) + titleH;
+        return pinLoc(y, getViewHeightWithTitle(),
+                      computeVerticalScrollRange() + getTitleHeight());
     }
 
     /**
@@ -3127,13 +3120,13 @@
         }
 
         int saveCount = canvas.save();
-        if (mInOverScrollMode
-                && getSettings().getUseSystemOverscrollBackground()) {
+        if (mInOverScrollMode && !getSettings()
+                .getUseWebViewBackgroundForOverscrollBackground()) {
             if (mOverScrollBackground == null) {
                 mOverScrollBackground = new Paint();
                 Bitmap bm = BitmapFactory.decodeResource(
                         mContext.getResources(),
-                        com.android.internal.R.drawable.pattern_underwear);
+                        com.android.internal.R.drawable.status_bar_background);
                 mOverScrollBackground.setShader(new BitmapShader(bm,
                         Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
             }
@@ -4203,12 +4196,6 @@
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
         sendOurVisibleRect();
-        // update WebKit if visible title bar height changed. The logic is same
-        // as getVisibleTitleHeight.
-        int titleHeight = getTitleHeight();
-        if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
-            sendViewSizeZoom();
-        }
     }
 
     @Override
@@ -5371,7 +5358,7 @@
 
     private int computeMaxScrollY() {
         return Math.max(computeVerticalScrollRange() + getTitleHeight()
-                - getViewHeightWithTitle(), getTitleHeight());
+                - getViewHeightWithTitle(), 0);
     }
 
     public void flingScroll(int vx, int vy) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a78429e..86011d7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -32,6 +32,7 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
@@ -2388,6 +2389,7 @@
     protected void onOverscrolled(int scrollX, int scrollY,
             boolean clampedX, boolean clampedY) {
         mScrollY = scrollY;
+
         if (clampedY) {
             // Velocity is broken by hitting the limit; don't start a fling off of this.
             if (mVelocityTracker != null) {
@@ -2561,7 +2563,7 @@
         /**
          * Tracks the decay of a fling scroll
          */
-        private OverScroller mScroller;
+        private final OverScroller mScroller;
 
         /**
          * Y value reported by mScroller on the previous fling
@@ -2598,6 +2600,21 @@
 
         void startOverfling(int initialVelocity) {
             mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight());
+            edgeReached();
+            mTouchMode = TOUCH_MODE_OVERFLING;
+            invalidate();
+            post(this);
+        }
+
+        void edgeReached() {
+            mScroller.notifyVerticalEdgeReached(mScrollY, 0, Integer.MAX_VALUE);
+            mTouchMode = TOUCH_MODE_OVERFLING;
+            invalidate();
+            post(this);
+        }
+
+        void marginReached() {
+            mScroller.notifyVerticalBoundaryReached(mScrollY, 0);
             mTouchMode = TOUCH_MODE_OVERFLING;
             invalidate();
             post(this);
@@ -2677,11 +2694,7 @@
                         overscrollBy(0, overshoot, 0, mScrollY, 0, 0,
                                 0, getOverscrollMax(), false);
                     }
-                    float vel = scroller.getCurrVelocity();
-                    if (delta > 0) {
-                        vel = -vel;
-                    }
-                    startOverfling(Math.round(vel));
+                    edgeReached();
                     break;
                 }
 
@@ -2738,7 +2751,7 @@
         private int mBoundPos;
         private int mLastSeenPos;
         private int mScrollDuration;
-        private int mExtraScroll;
+        private final int mExtraScroll;
         
         PositionScroller() {
             mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
@@ -3977,7 +3990,7 @@
             for (int i = 0; i < count; i++) {
                 if (activeViews[i] != null) {
                     result = false;
-                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
+                    Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
                             "AbsListView " + this + " has a view in its active recycler: " +
                                     activeViews[i]);
                 }
@@ -4005,12 +4018,12 @@
             final View view = scrap.get(i);
             if (view.getParent() != null) {
                 result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
                         " has a view in its scrap heap still attached to a parent: " + view);
             }
             if (indexOfChild(view) >= 0) {
                 result = false;
-                android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+                Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
                         " has a view in its scrap heap that is also a direct child: " + view);
             }
         }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 5b52107..eb2da71 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1629,7 +1629,10 @@
                 // of iterating throught he list of observers.
                 post(new Runnable() {
                     public void run() {
-                        updateDropDownForFilter(mAdapter.getCount());
+                        final ListAdapter adapter = mAdapter;
+                        if (adapter != null) {
+                            updateDropDownForFilter(adapter.getCount());
+                        }
                     }
                 });
             }
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 1bba7f0..396b7ae 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -18,7 +18,6 @@
 
 import android.database.DataSetObservable;
 import android.database.DataSetObserver;
-import android.view.KeyEvent;
 
 /**
  * Base class for a {@link ExpandableListAdapter} used to provide data and Views
@@ -31,7 +30,8 @@
  * @see SimpleExpandableListAdapter
  * @see SimpleCursorTreeAdapter
  */
-public abstract class BaseExpandableListAdapter implements ExpandableListAdapter {
+public abstract class BaseExpandableListAdapter implements ExpandableListAdapter, 
+        HeterogeneousExpandableList {
     private final DataSetObservable mDataSetObservable = new DataSetObservable();
     
     public void registerDataSetObserver(DataSetObserver observer) {
@@ -102,5 +102,37 @@
     public boolean isEmpty() {
         return getGroupCount() == 0;
     }
-    
+
+
+    /**
+     * {@inheritDoc}
+     * @return 0 for any group or child position, since only one child type count is declared.
+     */
+    public int getChildType(int groupPosition, int childPosition) {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 1 as a default value in BaseExpandableListAdapter.
+     */
+    public int getChildTypeCount() {
+        return 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 0 for any groupPosition, since only one group type count is declared.
+     */
+    public int getGroupType(int groupPosition) {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 1 as a default value in BaseExpandableListAdapter.
+     */
+    public int getGroupTypeCount() {
+        return 1;
+    }
 }
diff --git a/core/java/android/widget/ExpandableListAdapter.java b/core/java/android/widget/ExpandableListAdapter.java
index b75983c..7f6781b 100644
--- a/core/java/android/widget/ExpandableListAdapter.java
+++ b/core/java/android/widget/ExpandableListAdapter.java
@@ -17,7 +17,6 @@
 package android.widget;
 
 import android.database.DataSetObserver;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -108,7 +107,7 @@
     /**
      * Gets a View that displays the given group. This View is only for the
      * group--the Views for the group's children will be fetched using
-     * getChildrenView.
+     * {@link #getChildView(int, int, boolean, View, ViewGroup)}.
      * 
      * @param groupPosition the position of the group for which the View is
      *            returned
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 01d3a4a..2ff6b70 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -442,8 +442,8 @@
 
         View retValue;
         if (posMetadata.position.type == ExpandableListPosition.GROUP) {
-            retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos, posMetadata
-                    .isExpanded(), convertView, parent);
+            retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos,
+                    posMetadata.isExpanded(), convertView, parent);
         } else if (posMetadata.position.type == ExpandableListPosition.CHILD) {
             final boolean isLastChild = posMetadata.groupMetadata.lastChildFlPos == flatListPos;
             
@@ -464,10 +464,21 @@
         final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position;
 
         int retValue;
-        if (pos.type == ExpandableListPosition.GROUP) {
-            retValue = 0;
+        if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+            HeterogeneousExpandableList adapter =
+                    (HeterogeneousExpandableList) mExpandableListAdapter;
+            if (pos.type == ExpandableListPosition.GROUP) {
+                retValue = adapter.getGroupType(pos.groupPos);
+            } else {
+                final int childType = adapter.getChildType(pos.groupPos, pos.childPos);
+                retValue = adapter.getGroupTypeCount() + childType;
+            }
         } else {
-            retValue = 1;
+            if (pos.type == ExpandableListPosition.GROUP) {
+                retValue = 0;
+            } else {
+                retValue = 1;
+            }
         }
         
         pos.recycle();
@@ -477,7 +488,13 @@
 
     @Override
     public int getViewTypeCount() {
-        return 2;
+        if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+            HeterogeneousExpandableList adapter =
+                    (HeterogeneousExpandableList) mExpandableListAdapter;
+            return adapter.getGroupTypeCount() + adapter.getChildTypeCount();
+        } else {
+            return 2;
+        }
     }
     
     @Override
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
new file mode 100644
index 0000000..1292733
--- /dev/null
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Additional methods that when implemented make an
+ * {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
+ * mechanism.
+ * 
+ * An {@link ExpandableListAdapter} declares one view type for its group items
+ * and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
+ * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
+ * different types of group and/or child item views, should use an adapter that implements this
+ * interface. This way, the recycled views that will be provided to
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * and
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * will be of the appropriate group or child type, resulting in a more efficient reuse of the
+ * previously created views.
+ */
+public interface HeterogeneousExpandableList {
+    /**
+     * Get the type of group View that will be created by
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . for the specified group item.
+     * 
+     * @param groupPosition the position of the group for which the type should be returned.
+     * @return An integer representing the type of group View. Two group views should share the same
+     *         type if one can be converted to the other in
+     *         {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     *         . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
+     *         {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+     * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+     * @see getGroupTypeCount()
+     */
+    int getGroupType(int groupPosition);
+
+    /**
+     * Get the type of child View that will be created by
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * for the specified child item.
+     * 
+     * @param groupPosition the position of the group that the child resides in
+     * @param childPosition the position of the child with respect to other children in the group
+     * @return An integer representing the type of child View. Two child views should share the same
+     *         type if one can be converted to the other in
+     *         {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     *         Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
+     *         {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+     * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+     * @see getChildTypeCount()
+     */
+    int getChildType(int groupPosition, int childPosition);
+
+    /**
+     * <p>
+     * Returns the number of types of group Views that will be created by
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . Each type represents a set of views that can be converted in
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . If the adapter always returns the same type of View for all group items, this method should
+     * return 1.
+     * </p>
+     * <p>
+     * This method will only be called when the adapter is set on the {@link AdapterView}.
+     * </p>
+     * 
+     * @return The number of types of group Views that will be created by this adapter.
+     * @see getChildTypeCount()
+     * @see getGroupType()
+     */
+    int getGroupTypeCount();
+
+    /**
+     * <p>
+     * Returns the number of types of child Views that will be created by
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * . Each type represents a set of views that can be converted in
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * , for any group. If the adapter always returns the same type of View for
+     * all child items, this method should return 1.
+     * </p>
+     * <p>
+     * This method will only be called when the adapter is set on the {@link AdapterView}.
+     * </p>
+     * 
+     * @return The total number of types of child Views that will be created by this adapter.
+     * @see getGroupTypeCount()
+     * @see getChildType()
+     */
+    int getChildTypeCount();
+}
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 8469c8b..6258024 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -17,8 +17,8 @@
 package android.widget;
 
 import android.content.Context;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 /**
  * This class encapsulates scrolling with the ability to overshoot the bounds
@@ -27,110 +27,34 @@
  * 
  * @hide Pending API approval
  */
-public class OverScroller {
-    private static final int SPRINGBACK_DURATION = 150;
-    private static final int OVERFLING_DURATION = 150;
-    
-    private static final int MODE_DEFAULT = 0;
-    private static final int MODE_OVERFLING = 1;
-    private static final int MODE_SPRINGBACK = 2;
-    
-    private Scroller mDefaultScroller;
-    private Scroller mDecelScroller;
-    private Scroller mAccelDecelScroller;
-    private Scroller mCurrScroller;
-    
-    private int mScrollMode = MODE_DEFAULT;
-    
-    private int mMinimumX;
-    private int mMinimumY;
-    private int mMaximumX;
-    private int mMaximumY;
-    
-    public OverScroller(Context context) {
-        mDefaultScroller = new Scroller(context);
-        mDecelScroller = new Scroller(context, new DecelerateInterpolator());
-        mAccelDecelScroller = new Scroller(context, new AccelerateDecelerateInterpolator());
-        mCurrScroller = mDefaultScroller;
-    }
-    
+public class OverScroller extends Scroller {
+
+    // Identical to mScrollers, but casted to MagneticOverScroller. 
+    private MagneticOverScroller mOverScrollerX;
+    private MagneticOverScroller mOverScrollerY;
+
     /**
-     * Call this when you want to know the new location.  If it returns true,
-     * the animation is not yet finished.  loc will be altered to provide the
-     * new location.
-     */ 
-    public boolean computeScrollOffset() {
-        boolean inProgress = mCurrScroller.computeScrollOffset();
-        
-        switch (mScrollMode) {
-        case MODE_OVERFLING:
-            if (!inProgress) {
-                // Overfling ended
-                if (springback(mCurrScroller.getCurrX(), mCurrScroller.getCurrY(),
-                        mMinimumX, mMaximumX, mMinimumY, mMaximumY, mAccelDecelScroller)) {
-                    return mCurrScroller.computeScrollOffset();
-                } else {
-                    mCurrScroller = mDefaultScroller;
-                    mScrollMode = MODE_DEFAULT;
-                }
-            }
-            break;
-            
-        case MODE_SPRINGBACK:
-            if (!inProgress) {
-                mCurrScroller = mDefaultScroller;
-                mScrollMode = MODE_DEFAULT;
-            }
-            break;
-            
-        case MODE_DEFAULT:
-            // Fling/autoscroll - did we go off the edge?
-            if (inProgress) {
-                Scroller scroller = mCurrScroller;
-                final int x = scroller.getCurrX();
-                final int y = scroller.getCurrY();
-                final int minX = mMinimumX;
-                final int maxX = mMaximumX;
-                final int minY = mMinimumY;
-                final int maxY = mMaximumY;
-                if (x < minX || x > maxX || y < minY || y > maxY) {
-                    final int startx = scroller.getStartX();
-                    final int starty = scroller.getStartY();
-                    final int time = scroller.timePassed();
-                    final float timeSecs = time / 1000.f;
-                    final float xvel = ((x - startx) / timeSecs);
-                    final float yvel = ((y - starty) / timeSecs);
-                    
-                    if ((x < minX && xvel > 0) || (y < minY && yvel > 0) ||
-                            (x > maxX && xvel < 0) || (y > maxY && yvel < 0)) {
-                        // If our velocity would take us back into valid areas,
-                        // try to springback rather than overfling.
-                        if (springback(x, y, minX, maxX, minY, maxY)) {
-                            return mCurrScroller.computeScrollOffset();
-                        }
-                    } else {
-                        overfling(x, y, xvel, yvel);
-                        return mCurrScroller.computeScrollOffset();
-                    }
-                }
-            }
-            break;
-        }
-        
-        return inProgress;
+     * Creates an OverScroller with a viscous fluid scroll interpolator.
+     * @param context
+     */
+    public OverScroller(Context context) {
+        this(context, null);
     }
-    
-    private void overfling(int startx, int starty, float xvel, float yvel) {
-        Scroller scroller = mDecelScroller;
-        final float durationSecs = (OVERFLING_DURATION / 1000.f);
-        int dx = (int)(xvel * durationSecs) / 8;
-        int dy = (int)(yvel * durationSecs) / 8;
-        mCurrScroller.abortAnimation();
-        scroller.startScroll(startx, starty, dx, dy, OVERFLING_DURATION);
-        mCurrScroller = scroller;
-        mScrollMode = MODE_OVERFLING;
+
+    /**
+     * Creates a Scroller with the specified interpolator. If the interpolator is
+     * null, the default (viscous) interpolator will be used.
+     */
+    public OverScroller(Context context, Interpolator interpolator) {
+        super(context, interpolator);
     }
-    
+
+    @Override
+    void instantiateScrollers() {
+        mScrollerX = mOverScrollerX = new MagneticOverScroller();
+        mScrollerY = mOverScrollerY = new MagneticOverScroller();
+    }
+
     /**
      * Call this when you want to 'spring back' into a valid coordinate range.
      *
@@ -140,310 +64,262 @@
      * @param maxX Maximum valid X value
      * @param minY Minimum valid Y value
      * @param maxY Minimum valid Y value
-     * @return true if a springback was initiated, false if startX/startY was
+     * @return true if a springback was initiated, false if startX and startY were
      *          already within the valid range.
      */
-    public boolean springback(int startX, int startY, int minX, int maxX,
-            int minY, int maxY) {
-        return springback(startX, startY, minX, maxX, minY, maxY, mDecelScroller);
-    }
-    
-    private boolean springback(int startX, int startY, int minX, int maxX,
-            int minY, int maxY, Scroller scroller) {
-        int xoff = 0;
-        int yoff = 0;
-        if (startX < minX) {
-            xoff = minX - startX;
-        } else if (startX > maxX) {
-            xoff = maxX - startX;
-        }
-        if (startY < minY) {
-            yoff = minY - startY;
-        } else if (startY > maxY) {
-            yoff = maxY - startY;
-        }
-        
-        if (xoff != 0 || yoff != 0) {
-            mCurrScroller.abortAnimation();
-            scroller.startScroll(startX, startY, xoff, yoff, SPRINGBACK_DURATION);
-            mCurrScroller = scroller;
-            mScrollMode = MODE_SPRINGBACK;
-            return true;
-        }
-        
-        return false;
+    public boolean springback(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+        mMode = FLING_MODE;
+        return mOverScrollerX.springback(startX, minX, maxX)
+                || mOverScrollerY.springback(startY, minY, maxY);
     }
 
-    /**
-     * 
-     * Returns whether the scroller has finished scrolling.
-     * 
-     * @return True if the scroller has finished scrolling, false otherwise.
-     */
-    public final boolean isFinished() {
-        return mCurrScroller.isFinished();
-    }
-
-    /**
-     * Returns the current X offset in the scroll. 
-     * 
-     * @return The new X offset as an absolute distance from the origin.
-     */
-    public final int getCurrX() {
-        return mCurrScroller.getCurrX();
-    }
-    
-    /**
-     * Returns the current Y offset in the scroll. 
-     * 
-     * @return The new Y offset as an absolute distance from the origin.
-     */
-    public final int getCurrY() {
-        return mCurrScroller.getCurrY();
-    }
-    
-    /**
-     * Stops the animation, resets any springback/overfling and completes
-     * any standard flings/scrolls in progress.
-     */
-    public void abortAnimation() {
-        mCurrScroller.abortAnimation();
-        mCurrScroller = mDefaultScroller;
-        mScrollMode = MODE_DEFAULT;
-        mCurrScroller.abortAnimation();
-    }
-    
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * The scroll will use the default value of 250 milliseconds for the
-     * duration. This version does not spring back to boundaries.
-     * 
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy) {
-        final int minX = Math.min(startX, startX + dx);
-        final int maxX = Math.max(startX, startX + dx);
-        final int minY = Math.min(startY, startY + dy);
-        final int maxY = Math.max(startY, startY + dy);
-        startScroll(startX, startY, dx, dy, minX, maxX, minY, maxY);
-    }
-    
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * The scroll will use the default value of 250 milliseconds for the
-     * duration. This version will spring back to the provided boundaries if
-     * the scroll value would take it too far.
-     * 
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point.
-     * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy,
-            int minX, int maxX, int minY, int maxY) {
-        mCurrScroller.abortAnimation();
-        mCurrScroller = mDefaultScroller;
-        mScrollMode = MODE_DEFAULT;
-        mMinimumX = minX;
-        mMaximumX = maxX; 
-        mMinimumY = minY;
-        mMaximumY = maxY;
-        mCurrScroller.startScroll(startX, startY, dx, dy);
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * 
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     * @param duration Duration of the scroll in milliseconds.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
-        mCurrScroller.abortAnimation();
-        mCurrScroller = mDefaultScroller;
-        mScrollMode = MODE_DEFAULT;
-        mMinimumX = Math.min(startX, startX + dx);
-        mMinimumY = Math.min(startY, startY + dy);
-        mMaximumX = Math.max(startX, startX + dx);
-        mMaximumY = Math.max(startY, startY + dy);
-        mCurrScroller.startScroll(startX, startY, dx, dy, duration);
-    }
-    
-    /**
-     * Returns the duration of the active scroll in progress; standard, fling,
-     * springback, or overfling. Does not account for any overflings or springback
-     * that may result.
-     */
-    public int getDuration() {
-        return mCurrScroller.getDuration();
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance travelled will
-     * depend on the initial velocity of the fling.
-     * 
-     * @param startX Starting point of the scroll (X)
-     * @param startY Starting point of the scroll (Y)
-     * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
-     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point.
-     * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point.
-     */
+    @Override
     public void fling(int startX, int startY, int velocityX, int velocityY,
             int minX, int maxX, int minY, int maxY) {
-        this.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+        fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
     }
 
     /**
-     * Start scrolling based on a fling gesture. The distance travelled will
+     * Start scrolling based on a fling gesture. The distance traveled will
      * depend on the initial velocity of the fling.
      * 
      * @param startX Starting point of the scroll (X)
      * @param startY Starting point of the scroll (Y)
      * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
+     *            second.
      * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point unless overX > 0. If overfling is allowed, it will use minX
-     *        as a springback boundary.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point unless overX > 0. If overfling is allowed, it will use maxX
-     *        as a springback boundary.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point unless overY > 0. If overfling is allowed, it will use minY
-     *        as a springback boundary.
-     * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point unless overY > 0. If overfling is allowed, it will use maxY
-     *        as a springback boundary.
+     *            second
+     * @param minX Minimum X value. The scroller will not scroll past this point
+     *            unless overX > 0. If overfling is allowed, it will use minX as
+     *            a springback boundary.
+     * @param maxX Maximum X value. The scroller will not scroll past this point
+     *            unless overX > 0. If overfling is allowed, it will use maxX as
+     *            a springback boundary.
+     * @param minY Minimum Y value. The scroller will not scroll past this point
+     *            unless overY > 0. If overfling is allowed, it will use minY as
+     *            a springback boundary.
+     * @param maxY Maximum Y value. The scroller will not scroll past this point
+     *            unless overY > 0. If overfling is allowed, it will use maxY as
+     *            a springback boundary.
      * @param overX Overfling range. If > 0, horizontal overfling in either
-     *        direction will be possible.
+     *            direction will be possible.
      * @param overY Overfling range. If > 0, vertical overfling in either
-     *        direction will be possible.
+     *            direction will be possible.
      */
     public void fling(int startX, int startY, int velocityX, int velocityY,
             int minX, int maxX, int minY, int maxY, int overX, int overY) {
-        mCurrScroller = mDefaultScroller;
-        mScrollMode = MODE_DEFAULT;
-        mMinimumX = minX;
-        mMaximumX = maxX;
-        mMinimumY = minY;
-        mMaximumY = maxY;
-        mCurrScroller.fling(startX, startY, velocityX, velocityY, 
-                minX - overX, maxX + overX, minY - overY, maxY + overY);
+        mMode = FLING_MODE;
+        mOverScrollerX.fling(startX, velocityX, minX, maxX, overX);
+        mOverScrollerY.fling(startY, velocityY, minY, maxY, overY);
+    }
+
+    void notifyHorizontalBoundaryReached(int startX, int finalX) {
+        mOverScrollerX.springback(startX, finalX, finalX);
+    }
+
+    void notifyVerticalBoundaryReached(int startY, int finalY) {
+        mOverScrollerY.springback(startY, finalY, finalY);
+    }
+
+    void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
+        mOverScrollerX.notifyEdgeReached(startX, finalX, overX);
+    }
+
+    void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
+        mOverScrollerY.notifyEdgeReached(startY, finalY, overY);
     }
 
     /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
+     * Returns whether the current Scroller position is overscrolled or still within the minimum and
+     * maximum bounds provided in the
+     * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
      * 
-     * @return The final X offset as an absolute distance from the origin.
-     */
-    public int getFinalX() {
-        return mCurrScroller.getFinalX();
-    }
-    
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
+     * One should check this value before calling
+     * {@link startScroll(int, int, int, int)} as the interpolation currently in progress to restore
+     * a valid position will then be stopped. The caller has to take into account the fact that the
+     * started scroll will start from an overscrolled position.
      * 
-     * @return The final Y offset as an absolute distance from the origin.
+     * @return true when the current position is overscrolled.
      */
-    public int getFinalY() {
-        return mCurrScroller.getFinalY();
+    public boolean isOverscrolled() {
+        return ((!mOverScrollerX.mFinished && mOverScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+                (!mOverScrollerY.mFinished && mOverScrollerY.mState != MagneticOverScroller.TO_EDGE));
     }
-    
-    /**
-     * @hide
-     * Returns the current velocity.
-     *
-     * @return The original velocity less the deceleration. Result may be
-     * negative.
-     */
-    public float getCurrVelocity() {
-        return mCurrScroller.getCurrVelocity();
-    }
-    
-    /**
-     * Extend the scroll animation. This allows a running animation to scroll
-     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
-     *
-     * @param extend Additional time to scroll in milliseconds.
-     * @see #setFinalX(int)
-     * @see #setFinalY(int)
-     */
-    public void extendDuration(int extend) {
-        if (mScrollMode == MODE_DEFAULT) {
-            mDefaultScroller.extendDuration(extend);
+
+    static class MagneticOverScroller extends Scroller.MagneticScroller {
+        private static final int TO_EDGE = 0;
+        private static final int TO_BOUNDARY = 1;
+        private static final int TO_BOUNCE = 2;
+
+        private int mState = TO_EDGE;
+
+        // The allowed overshot distance before boundary is reached.
+        private int mOver;
+
+        // When the scroll goes beyond the edges limits, the deceleration is
+        // multiplied by this coefficient, so that the return to a valid
+        // position is faster.
+        private static final float OVERSCROLL_DECELERATION_COEF = 16.0f;
+
+        // If the velocity is smaller than this value, no bounce is triggered
+        // when the edge limits are reached (would result in a zero pixels
+        // displacement anyway).
+        private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 200.0f;
+
+        // Could be made public for tuning, but applications would no longer
+        // have the same look and feel.
+        private static final float BOUNCE_COEFFICIENT = 0.4f;
+
+        /*
+         * Get a signed deceleration that will reduce the velocity.
+         */
+        @Override
+        float getDeceleration(int velocity) {
+            float decelerationY = super.getDeceleration(velocity);
+            if (mState != TO_EDGE) {
+                decelerationY *= OVERSCROLL_DECELERATION_COEF;
+            }
+            return decelerationY;
         }
-    }
-    
-    /**
-     * Sets the final position (X) for this scroller.
-     *
-     * @param newX The new X offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalY(int)
-     */
-    public void setFinalX(int newX) {
-        if (mScrollMode == MODE_DEFAULT) {
-            if (newX < mMinimumX) {
-                mMinimumX = newX;
+
+        boolean springback(int start, int min, int max) {
+            mFinished = true;
+
+            mStart = start;
+            mVelocity = 0;
+
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            mDuration = 0;
+
+            if (start < min) {
+                startSpringback(start, min, -1);
+            } else if (start > max) {
+                startSpringback(start, max, 1);
             }
-            if (newX > mMaximumX) {
-                mMaximumX = newX;
-            }
-            mDefaultScroller.setFinalX(newX);
+
+            return !mFinished;
         }
-    }
-    
-    /**
-     * Sets the final position (Y) for this scroller.
-     *
-     * @param newY The new Y offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalX(int)
-     */
-    public void setFinalY(int newY) {
-        if (mScrollMode == MODE_DEFAULT) {
-            if (newY < mMinimumY) {
-                mMinimumY = newY;
+
+        private void startSpringback(int start, int end, int sign) {
+            mFinished = false;
+            mState = TO_BOUNCE;
+            mDeceleration = getDeceleration(sign);
+            mFinal = end;
+            mDuration = (int) (1000.0f * Math.sqrt(2.0f * (end - start) / mDeceleration));
+        }
+
+        void fling(int start, int velocity, int min, int max, int over) {
+            mState = TO_EDGE;
+            mOver = over;
+
+            super.fling(start, velocity, min, max);
+
+            if (mStart > max) {
+                if (mStart >= max + over) {
+                    springback(max + over, min, max);
+                } else {
+                    // Make sure the deceleration brings us back to edge
+                    mVelocity = velocity > 0 ? velocity : -velocity;
+                    mCurrVelocity = velocity;
+                    notifyEdgeReached(start, max, over);
+                }
+            } else {
+                if (mStart < min) {
+                    if (mStart <= min - over) {
+                        springback(min - over, min, max);
+                    } else {
+                        // Make sure the deceleration brings us back to edge
+                        mVelocity = velocity < 0 ? velocity : -velocity;
+                        mCurrVelocity = velocity;
+                        notifyEdgeReached(start, min, over);
+                    }
+                }
             }
-            if (newY > mMaximumY) {
-                mMaximumY = newY;
+        }
+
+        void notifyEdgeReached(int start, int end, int over) {
+            // Compute post-edge deceleration
+            mState = TO_BOUNDARY;
+            mDeceleration = getDeceleration(mVelocity);
+
+            // Local time, used to compute edge crossing time.
+            float timeCurrent = mCurrVelocity / mDeceleration;
+            final int distance = end - start;
+            float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration)
+                             + (timeCurrent * timeCurrent));
+
+            mVelocity = (int) (mDeceleration * timeEdge);
+
+            // Simulate a symmetric bounce that started from edge
+            mStart = end;
+
+            mOver = over;
+
+            long time = AnimationUtils.currentAnimationTimeMillis();
+            mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge));
+
+            onEdgeReached();
+        }
+
+        void onEdgeReached() {
+            // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
+            mState = TO_BOUNDARY;
+            mDeceleration = getDeceleration(mVelocity);
+
+            int distance = Math.round((mVelocity * mVelocity) / (2.0f * mDeceleration));
+
+            if (Math.abs(distance) < mOver) {
+                // Deceleration will bring us back to final position
+                mState = TO_BOUNCE;
+                mFinal = mStart;
+                mDuration = (int) (-2000.0f * mVelocity / mDeceleration);
+            } else {
+                // Velocity is too high, we will hit the boundary limit
+                mFinal = mStart + (mVelocity > 0 ? mOver : -mOver);
+                mDuration = computeDuration(mStart, mFinal, mVelocity, mDeceleration);
             }
-            mDefaultScroller.setFinalY(newY);
+        }
+
+        @Override
+        boolean continueWhenFinished() {
+            switch (mState) {
+                case TO_EDGE:
+                    // Duration from start to null velocity
+                    int duration = (int) (-1000.0f * mVelocity / mDeceleration);
+                    if (mDuration < duration) {
+                        // If the animation was clamped, we reached the edge
+                        mStart = mFinal;
+                        // Speed when edge was reached
+                        mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f);
+                        mStartTime += mDuration;
+                        onEdgeReached();
+                    } else {
+                        // Normal stop, no need to continue
+                        return false;
+                    }
+                    break;
+                case TO_BOUNDARY:
+                    mStartTime += mDuration;
+                    mStart = mFinal;
+                    mFinal = mStart - (mVelocity > 0 ? mOver : -mOver);
+                    mVelocity = 0;
+                    mDuration = (int) (1000.0f * Math.sqrt(Math.abs(2.0f * mOver / mDeceleration)));
+                    mState = TO_BOUNCE;
+                    break;
+                case TO_BOUNCE:
+                    float edgeVelocity = mVelocity + mDeceleration * mDuration / 1000.0f;
+                    mVelocity = (int) (-edgeVelocity * BOUNCE_COEFFICIENT);
+                    if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
+                        return false;
+                    }
+                    mStart = mFinal;
+                    mStartTime += mDuration;
+                    mDuration = (int) (-2000.0f * mVelocity / mDeceleration);
+                    break;
+            }
+
+            update();
+            return true;
         }
     }
 }
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 68c0ff0..239c5f4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1272,7 +1272,7 @@
      * Fling the scroll view
      *
      * @param velocityY The initial velocity in the Y direction. Positive
-     *                  numbers mean that the finger/curor is moving down the screen,
+     *                  numbers mean that the finger/cursor is moving down the screen,
      *                  which means we want to scroll towards the top.
      */
     public void fling(int velocityY) {
@@ -1307,6 +1307,7 @@
      *
      * <p>This version also clamps the scrolling to the bounds of our child.
      */
+    @Override
     public void scrollTo(int x, int y) {
         // we rely on the fact the View.scrollBy calls scrollTo.
         if (getChildCount() > 0) {
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 11dab02..542866a 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -16,8 +16,10 @@
 
 package android.widget;
 
+
 import android.content.Context;
 import android.hardware.SensorManager;
+import android.util.FloatMath;
 import android.view.ViewConfiguration;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -25,48 +27,34 @@
 
 /**
  * This class encapsulates scrolling.  The duration of the scroll
- * can be passed in the constructor and specifies the maximum time that
- * the scrolling animation should take.  Past this time, the scrolling is 
- * automatically moved to its final stage and computeScrollOffset()
- * will always return false to indicate that scrolling is over.
+ * is either specified along with the distance or depends on the initial fling velocity.
+ * Past this time, the scrolling is automatically moved to its final stage and
+ * computeScrollOffset() will always return false to indicate that scrolling is over.
  */
 public class Scroller  {
-    private int mMode;
+    int mMode;
 
-    private int mStartX;
-    private int mStartY;
-    private int mFinalX;
-    private int mFinalY;
+    MagneticScroller mScrollerX;
+    MagneticScroller mScrollerY;
 
-    private int mMinX;
-    private int mMaxX;
-    private int mMinY;
-    private int mMaxY;
+    private final Interpolator mInterpolator;
 
-    private int mCurrX;
-    private int mCurrY;
-    private long mStartTime;
-    private int mDuration;
-    private float mDurationReciprocal;
-    private float mDeltaX;
-    private float mDeltaY;
-    private float mViscousFluidScale;
-    private float mViscousFluidNormalize;
-    private boolean mFinished;
-    private Interpolator mInterpolator;
+    static final int DEFAULT_DURATION = 250;
+    static final int SCROLL_MODE = 0;
+    static final int FLING_MODE = 1;
 
-    private float mCoeffX = 0.0f;
-    private float mCoeffY = 1.0f;
-    private float mVelocity;
+    // This controls the viscous fluid effect (how much of it)
+    private final static float VISCOUS_FLUID_SCALE = 8.0f;
+    private static float VISCOUS_FLUID_NORMALIZE;
 
-    private static final int DEFAULT_DURATION = 250;
-    private static final int SCROLL_MODE = 0;
-    private static final int FLING_MODE = 1;
-
-    private final float mDeceleration;
+    static {
+        // Set a neutral value that will be used in the next call to viscousFluid().
+        VISCOUS_FLUID_NORMALIZE = 1.0f;
+        VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
+    }
 
     /**
-     * Create a Scroller with the default duration and interpolator.
+     * Create a Scroller with a viscous fluid scroll interpolator.
      */
     public Scroller(Context context) {
         this(context, null);
@@ -77,15 +65,17 @@
      * null, the default (viscous) interpolator will be used.
      */
     public Scroller(Context context, Interpolator interpolator) {
-        mFinished = true;
+        instantiateScrollers();
+        MagneticScroller.initializeFromContext(context);
+
         mInterpolator = interpolator;
-        float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
-        mDeceleration = SensorManager.GRAVITY_EARTH   // g (m/s^2)
-                      * 39.37f                        // inch/meter
-                      * ppi                           // pixels per inch
-                      * ViewConfiguration.getScrollFriction();
     }
     
+    void instantiateScrollers() {
+        mScrollerX = new MagneticScroller();
+        mScrollerY = new MagneticScroller();        
+    }
+
     /**
      * 
      * Returns whether the scroller has finished scrolling.
@@ -93,150 +83,148 @@
      * @return True if the scroller has finished scrolling, false otherwise.
      */
     public final boolean isFinished() {
-        return mFinished;
+        return mScrollerX.mFinished && mScrollerY.mFinished;
     }
-    
+
     /**
      * Force the finished field to a particular value.
-     *  
+     * 
      * @param finished The new finished value.
      */
     public final void forceFinished(boolean finished) {
-        mFinished = finished;
+        mScrollerX.mFinished = mScrollerY.mFinished = finished;
     }
-    
+
     /**
      * Returns how long the scroll event will take, in milliseconds.
      * 
      * @return The duration of the scroll in milliseconds.
      */
     public final int getDuration() {
-        return mDuration;
+        return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
     }
-    
+
     /**
-     * Returns the current X offset in the scroll. 
+     * Returns the current X offset in the scroll.
      * 
      * @return The new X offset as an absolute distance from the origin.
      */
     public final int getCurrX() {
-        return mCurrX;
+        return mScrollerX.mCurrentPosition;
     }
-    
+
     /**
-     * Returns the current Y offset in the scroll. 
+     * Returns the current Y offset in the scroll.
      * 
      * @return The new Y offset as an absolute distance from the origin.
      */
     public final int getCurrY() {
-        return mCurrY;
-    }
-    
-    /**
-     * @hide
-     * Returns the current velocity.
-     *
-     * @return The original velocity less the deceleration. Result may be
-     * negative.
-     */
-    public float getCurrVelocity() {
-        return mVelocity - mDeceleration * timePassed() / 2000.0f;
+        return mScrollerY.mCurrentPosition;
     }
 
     /**
-     * Returns the start X offset in the scroll. 
+     * @hide
+     * Returns the current velocity.
+     * 
+     * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
+     */
+    public float getCurrVelocity() {
+        float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+        squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+        return FloatMath.sqrt(squaredNorm);
+    }
+
+    /**
+     * Returns the start X offset in the scroll.
      * 
      * @return The start X offset as an absolute distance from the origin.
      */
     public final int getStartX() {
-        return mStartX;
+        return mScrollerX.mStart;
     }
-    
+
     /**
-     * Returns the start Y offset in the scroll. 
+     * Returns the start Y offset in the scroll.
      * 
      * @return The start Y offset as an absolute distance from the origin.
      */
     public final int getStartY() {
-        return mStartY;
+        return mScrollerY.mStart;
     }
-    
+
     /**
      * Returns where the scroll will end. Valid only for "fling" scrolls.
      * 
      * @return The final X offset as an absolute distance from the origin.
      */
     public final int getFinalX() {
-        return mFinalX;
+        return mScrollerX.mFinal;
     }
-    
+
     /**
      * Returns where the scroll will end. Valid only for "fling" scrolls.
      * 
      * @return The final Y offset as an absolute distance from the origin.
      */
     public final int getFinalY() {
-        return mFinalY;
+        return mScrollerY.mFinal;
     }
 
     /**
-     * Call this when you want to know the new location.  If it returns true,
-     * the animation is not yet finished.  loc will be altered to provide the
-     * new location.
-     */ 
+     * Call this when you want to know the new location. If it returns true, the
+     * animation is not yet finished.
+     */
     public boolean computeScrollOffset() {
-        if (mFinished) {
+        if (isFinished()) {
             return false;
         }
 
-        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
-    
-        if (timePassed < mDuration) {
-            switch (mMode) {
+        switch (mMode) {
             case SCROLL_MODE:
-                float x = (float)timePassed * mDurationReciprocal;
-    
-                if (mInterpolator == null)
-                    x = viscousFluid(x); 
-                else
-                    x = mInterpolator.getInterpolation(x);
-    
-                mCurrX = mStartX + Math.round(x * mDeltaX);
-                mCurrY = mStartY + Math.round(x * mDeltaY);
-                if ((mCurrX == mFinalX) && (mCurrY == mFinalY)) {
-                    mFinished = true;
-                }
-                break;
-            case FLING_MODE:
-                float timePassedSeconds = timePassed / 1000.0f;
-                float distance = (mVelocity * timePassedSeconds)
-                        - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
-                
-                mCurrX = mStartX + Math.round(distance * mCoeffX);
-                // Pin to mMinX <= mCurrX <= mMaxX
-                mCurrX = Math.min(mCurrX, mMaxX);
-                mCurrX = Math.max(mCurrX, mMinX);
-                
-                mCurrY = mStartY + Math.round(distance * mCoeffY);
-                // Pin to mMinY <= mCurrY <= mMaxY
-                mCurrY = Math.min(mCurrY, mMaxY);
-                mCurrY = Math.max(mCurrY, mMinY);
+                long time = AnimationUtils.currentAnimationTimeMillis();
+                // Any scroller can be used for time, since they were started
+                // together in scroll mode. We use X here.
+                final long elapsedTime = time - mScrollerX.mStartTime;
 
-                if (mCurrX == mFinalX && mCurrY == mFinalY) {
-                    mFinished = true;
+                final int duration = mScrollerX.mDuration;
+                if (elapsedTime < duration) {
+                    float q = (float) (elapsedTime) / duration;
+
+                    if (mInterpolator == null)
+                        q = viscousFluid(q);
+                    else
+                        q = mInterpolator.getInterpolation(q);
+
+                    mScrollerX.updateScroll(q);
+                    mScrollerY.updateScroll(q);
+                } else {
+                    abortAnimation();
                 }
-                
                 break;
-            }
+
+            case FLING_MODE:
+                if (!mScrollerX.mFinished) {
+                    if (!mScrollerX.update()) {
+                        if (!mScrollerX.continueWhenFinished()) {
+                            mScrollerX.finish();
+                        }
+                    }
+                }
+
+                if (!mScrollerY.mFinished) {
+                    if (!mScrollerY.update()) {
+                        if (!mScrollerY.continueWhenFinished()) {
+                            mScrollerY.finish();
+                        }
+                    }
+                }
+
+                break;
         }
-        else {
-            mCurrX = mFinalX;
-            mCurrY = mFinalY;
-            mFinished = true;
-        }
+
         return true;
     }
-    
+
     /**
      * Start scrolling by providing a starting point and the distance to travel.
      * The scroll will use the default value of 250 milliseconds for the
@@ -270,83 +258,39 @@
      */
     public void startScroll(int startX, int startY, int dx, int dy, int duration) {
         mMode = SCROLL_MODE;
-        mFinished = false;
-        mDuration = duration;
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mStartX = startX;
-        mStartY = startY;
-        mFinalX = startX + dx;
-        mFinalY = startY + dy;
-        mDeltaX = dx;
-        mDeltaY = dy;
-        mDurationReciprocal = 1.0f / (float) mDuration;
-        // This controls the viscous fluid effect (how much of it)
-        mViscousFluidScale = 8.0f;
-        // must be set to 1.0 (used in viscousFluid())
-        mViscousFluidNormalize = 1.0f;
-        mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+        mScrollerX.startScroll(startX, dx, duration);
+        mScrollerY.startScroll(startY, dy, duration);
     }
 
     /**
-     * Start scrolling based on a fling gesture. The distance travelled will
-     * depend on the initial velocity of the fling.
+     * Start scrolling based on a fling gesture. The distance traveled will
+     * depend on the initial velocity of the fling. Velocity is slowed down by a
+     * constant deceleration until it reaches 0 or the limits are reached.
      * 
      * @param startX Starting point of the scroll (X)
      * @param startY Starting point of the scroll (Y)
      * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
+     *            second.
      * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
+     *            second.
      * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
+     *            point.
      * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
+     *            point.
      * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point.
+     *            point.
      * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point.
+     *            point.
      */
     public void fling(int startX, int startY, int velocityX, int velocityY,
             int minX, int maxX, int minY, int maxY) {
         mMode = FLING_MODE;
-        mFinished = false;
-
-        float velocity = (float)Math.hypot(velocityX, velocityY);
-     
-        mVelocity = velocity;
-        mDuration = (int) (1000 * velocity / mDeceleration); // Duration is in
-                                                            // milliseconds
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mStartX = startX;
-        mStartY = startY;
-
-        mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity; 
-        mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
-
-        int totalDistance = (int) ((velocity * velocity) / (2 * mDeceleration));
-        
-        mMinX = minX;
-        mMaxX = maxX;
-        mMinY = minY;
-        mMaxY = maxY;
-        
-        
-        mFinalX = startX + Math.round(totalDistance * mCoeffX);
-        // Pin to mMinX <= mFinalX <= mMaxX
-        mFinalX = Math.min(mFinalX, mMaxX);
-        mFinalX = Math.max(mFinalX, mMinX);
-        
-        mFinalY = startY + Math.round(totalDistance * mCoeffY);
-        // Pin to mMinY <= mFinalY <= mMaxY
-        mFinalY = Math.min(mFinalY, mMaxY);
-        mFinalY = Math.max(mFinalY, mMinY);
+        mScrollerX.fling(startX, velocityX, minX, maxX);
+        mScrollerY.fling(startY, velocityY, minY, maxY);
     }
-    
-    
-    
-    private float viscousFluid(float x)
-    {
-        x *= mViscousFluidScale;
+
+    private static float viscousFluid(float x) {
+        x *= VISCOUS_FLUID_SCALE;
         if (x < 1.0f) {
             x -= (1.0f - (float)Math.exp(-x));
         } else {
@@ -354,70 +298,237 @@
             x = 1.0f - (float)Math.exp(1.0f - x);
             x = start + x * (1.0f - start);
         }
-        x *= mViscousFluidNormalize;
+        x *= VISCOUS_FLUID_NORMALIZE;
         return x;
     }
-    
+
     /**
      * Stops the animation. Contrary to {@link #forceFinished(boolean)},
      * aborting the animating cause the scroller to move to the final x and y
      * position
-     *
+     * 
      * @see #forceFinished(boolean)
      */
     public void abortAnimation() {
-        mCurrX = mFinalX;
-        mCurrY = mFinalY;
-        mFinished = true;
+        mScrollerX.finish();
+        mScrollerY.finish();
     }
-    
+
     /**
      * Extend the scroll animation. This allows a running animation to scroll
      * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
-     *
+     * 
      * @param extend Additional time to scroll in milliseconds.
      * @see #setFinalX(int)
      * @see #setFinalY(int)
      */
     public void extendDuration(int extend) {
-        int passed = timePassed();
-        mDuration = passed + extend;
-        mDurationReciprocal = 1.0f / (float)mDuration;
-        mFinished = false;
+        mScrollerX.extendDuration(extend);
+        mScrollerY.extendDuration(extend);
     }
 
     /**
      * Returns the time elapsed since the beginning of the scrolling.
-     *
+     * 
      * @return The elapsed time in milliseconds.
      */
     public int timePassed() {
-        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+        final long time = AnimationUtils.currentAnimationTimeMillis();
+        final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+        return (int) (time - startTime);
     }
 
     /**
      * Sets the final position (X) for this scroller.
-     *
+     * 
      * @param newX The new X offset as an absolute distance from the origin.
      * @see #extendDuration(int)
      * @see #setFinalY(int)
      */
     public void setFinalX(int newX) {
-        mFinalX = newX;
-        mDeltaX = mFinalX - mStartX;
-        mFinished = false;
+        mScrollerX.setFinalPosition(newX);
     }
 
     /**
      * Sets the final position (Y) for this scroller.
-     *
+     * 
      * @param newY The new Y offset as an absolute distance from the origin.
      * @see #extendDuration(int)
      * @see #setFinalX(int)
      */
     public void setFinalY(int newY) {
-        mFinalY = newY;
-        mDeltaY = mFinalY - mStartY;
-        mFinished = false;
+        mScrollerY.setFinalPosition(newY);
+    }
+
+    static class MagneticScroller {
+        // Initial position
+        int mStart;
+
+        // Current position
+        int mCurrentPosition;
+
+        // Final position
+        int mFinal;
+
+        // Initial velocity
+        int mVelocity;
+
+        // Current velocity
+        float mCurrVelocity;
+
+        // Constant current deceleration
+        float mDeceleration;
+
+        // Animation starting time, in system milliseconds
+        long mStartTime;
+
+        // Animation duration, in milliseconds
+        int mDuration;
+
+        // Whether the animation is currently in progress
+        boolean mFinished;
+
+        // Constant gravity value, used to scale deceleration
+        static float GRAVITY;
+
+        static void initializeFromContext(Context context) {
+            final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
+            GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
+                    * 39.37f // inch/meter
+                    * ppi // pixels per inch
+                    * ViewConfiguration.getScrollFriction();
+        }
+
+        MagneticScroller() {
+            mFinished = true;
+        }
+
+        void updateScroll(float q) {
+            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
+        }
+
+        /*
+         * Update the current position and velocity for current time. Returns
+         * true if update has been done and false if animation duration has been
+         * reached.
+         */
+        boolean update() {
+            final long time = AnimationUtils.currentAnimationTimeMillis();
+            final long duration = time - mStartTime;
+
+            if (duration > mDuration) {
+                return false;
+            }
+
+            final float t = duration / 1000.0f;
+            mCurrVelocity = mVelocity + mDeceleration * t;
+            final float distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+            mCurrentPosition = mStart + (int) distance;
+
+            return true;
+        }
+
+        /*
+         * Get a signed deceleration that will reduce the velocity.
+         */
+        float getDeceleration(int velocity) {
+            return velocity > 0 ? -GRAVITY : GRAVITY;
+        }
+
+        /*
+         * Returns the time (in milliseconds) it will take to go from start to end.
+         */
+        static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
+            final int distance = start - end;
+            final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
+                    * distance;
+            if (discriminant >= 0.0f) {
+                float delta = (float) Math.sqrt(discriminant);
+                if (deceleration < 0.0f) {
+                    delta = -delta;
+                }
+                return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
+            }
+
+            // End position can not be reached
+            return 0;
+        }
+
+        void startScroll(int start, int distance, int duration) {
+            mFinished = false;
+
+            mStart = start;
+            mFinal = start + distance;
+
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+            mDuration = duration;
+
+            // Unused
+            mDeceleration = 0.0f;
+            mVelocity = 0;
+        }
+
+        void fling(int start, int velocity, int min, int max) {
+            mFinished = false;
+
+            mStart = start;
+            mStartTime = AnimationUtils.currentAnimationTimeMillis();
+
+            mVelocity = velocity;
+
+            mDeceleration = getDeceleration(velocity);
+
+            // A start from an invalid position immediately brings back to a valid position
+            if (mStart < min) {
+                mDuration = 0;
+                mFinal = min;
+                return;
+            }
+
+            if (mStart > max) {
+                mDuration = 0;
+                mFinal = max;
+                return;
+            }
+
+            // Duration are expressed in milliseconds
+            mDuration = (int) (-1000.0f * velocity / mDeceleration);
+
+            mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
+
+            // Clamp to a valid final position
+            if (mFinal < min) {
+                mFinal = min;
+                mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
+            }
+
+            if (mFinal > max) {
+                mFinal = max;
+                mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
+            }
+        }
+
+        void finish() {
+            mCurrentPosition = mFinal;
+            // Not reset since WebView relies on this value for fast fling.
+            // mCurrVelocity = 0.0f;
+            mFinished = true;
+        }
+
+        boolean continueWhenFinished() {
+            return false;
+        }
+
+        void setFinalPosition(int position) {
+            mFinal = position;
+            mFinished = false;
+        }
+
+        void extendDuration(int extend) {
+            final long time = AnimationUtils.currentAnimationTimeMillis();
+            final int elapsedTime = (int) (time - mStartTime);
+            mDuration = elapsedTime + extend;
+            mFinished = false;
+        }
     }
 }
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 9e4b606..b436363 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -105,6 +105,9 @@
                         + " key64=" + base64Key);
 
                 if (dataSize >= 0) {
+                    if (entityFile.exists()) {
+                        entityFile.delete();
+                    }
                     FileOutputStream entity = new FileOutputStream(entityFile);
 
                     if (dataSize > bufSize) {
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
index ca0345f..e675ef0 100644
--- a/core/java/com/android/internal/os/AtomicFile.java
+++ b/core/java/com/android/internal/os/AtomicFile.java
@@ -45,12 +45,13 @@
     public FileOutputStream startWrite() throws IOException {
         // Rename the current file so it may be used as a backup during the next read
         if (mBaseName.exists()) {
-            if (!mBaseName.renameTo(mBackupName)) {
-                mBackupName.delete();
+            if (!mBackupName.exists()) {
                 if (!mBaseName.renameTo(mBackupName)) {
                     Log.w("AtomicFile", "Couldn't rename file " + mBaseName
                             + " to backup file " + mBackupName);
                 }
+            } else {
+                mBaseName.delete();
             }
         }
         FileOutputStream str = null;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 71ccb3b..24275ec 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import com.android.internal.util.JournaledFile;
+
 import android.bluetooth.BluetoothHeadset;
 import android.net.TrafficStats;
 import android.os.BatteryStats;
@@ -30,6 +32,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import java.io.BufferedReader;
@@ -43,6 +46,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -57,7 +61,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 42;
+    private static final int VERSION = 43;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -68,8 +72,7 @@
     
     private static int sNumSpeedSteps;
 
-    private final File mFile;
-    private final File mBackupFile;
+    private final JournaledFile mFile;
 
     /**
      * The statistics we have collected organized by uids.
@@ -216,7 +219,7 @@
     
     // For debugging
     public BatteryStatsImpl() {
-        mFile = mBackupFile = null;
+        mFile = null;
     }
 
     public static interface Unpluggable {
@@ -228,14 +231,15 @@
      * State for keeping track of counting information.
      */
     public static class Counter extends BatteryStats.Counter implements Unpluggable {
-        int mCount;
+        final AtomicInteger mCount = new AtomicInteger();
         int mLoadedCount;
         int mLastCount;
         int mUnpluggedCount;
         int mPluggedCount;
         
         Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
-            mPluggedCount = mCount = in.readInt();
+            mPluggedCount = in.readInt();
+            mCount.set(mPluggedCount);
             mLoadedCount = in.readInt();
             mLastCount = in.readInt();
             mUnpluggedCount = in.readInt();
@@ -247,18 +251,19 @@
         }
         
         public void writeToParcel(Parcel out) {
-            out.writeInt(mCount);
+            out.writeInt(mCount.get());
             out.writeInt(mLoadedCount);
             out.writeInt(mLastCount);
             out.writeInt(mUnpluggedCount);
         }
 
         public void unplug(long batteryUptime, long batteryRealtime) {
-            mUnpluggedCount = mCount = mPluggedCount;
+            mUnpluggedCount = mPluggedCount;
+            mCount.set(mPluggedCount);
         }
 
         public void plug(long batteryUptime, long batteryRealtime) {
-            mPluggedCount = mCount;
+            mPluggedCount = mCount.get();
         }
         
         /**
@@ -283,7 +288,7 @@
             if (which == STATS_LAST) {
                 val = mLastCount;
             } else {
-                val = mCount;
+                val = mCount.get();
                 if (which == STATS_UNPLUGGED) {
                     val -= mUnpluggedCount;
                 } else if (which != STATS_TOTAL) {
@@ -295,25 +300,27 @@
         }
 
         public void logState(Printer pw, String prefix) {
-            pw.println(prefix + "mCount=" + mCount
+            pw.println(prefix + "mCount=" + mCount.get()
                     + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
                     + " mUnpluggedCount=" + mUnpluggedCount
                     + " mPluggedCount=" + mPluggedCount);
         }
         
-        void stepLocked() {
-            mCount++;
+        void stepAtomic() {
+            mCount.incrementAndGet();
         }
 
         void writeSummaryFromParcelLocked(Parcel out) {
-            out.writeInt(mCount);
-            out.writeInt(mCount - mLoadedCount);
+            int count = mCount.get();
+            out.writeInt(count);
+            out.writeInt(count - mLoadedCount);
         }
 
         void readSummaryFromParcelLocked(Parcel in) {
-            mCount = mLoadedCount = in.readInt();
+            mLoadedCount = in.readInt();
+            mCount.set(mLoadedCount);
             mLastCount = in.readInt();
-            mUnpluggedCount = mPluggedCount = mCount;
+            mUnpluggedCount = mPluggedCount = mLoadedCount;
         }
     }
 
@@ -327,8 +334,8 @@
             super(unpluggables);
         }
 
-        public void addCountLocked(long count) {
-            mCount += count;
+        public void addCountAtomic(long count) {
+            mCount.addAndGet((int)count);
         }
     }
 
@@ -1122,8 +1129,8 @@
         }
     }
     
-    public void noteInputEventLocked() {
-        mInputEventCounter.stepLocked();
+    public void noteInputEventAtomic() {
+        mInputEventCounter.stepAtomic();
     }
     
     public void noteUserActivityLocked(int uid, int event) {
@@ -1678,7 +1685,7 @@
             }
             if (type < 0) type = 0;
             else if (type >= NUM_USER_ACTIVITY_TYPES) type = NUM_USER_ACTIVITY_TYPES-1;
-            mUserActivityCounters[type].stepLocked();
+            mUserActivityCounters[type].stepAtomic();
         }
         
         @Override
@@ -2170,7 +2177,7 @@
             /* Called by ActivityManagerService when CPU times are updated. */
             public void addSpeedStepTimes(long[] values) {
                 for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
-                    mSpeedBins[i].addCountLocked(values[i]);
+                    mSpeedBins[i].addCountAtomic(values[i]);
                 }
             }
 
@@ -2704,8 +2711,7 @@
     }
 
     public BatteryStatsImpl(String filename) {
-        mFile = new File(filename);
-        mBackupFile = new File(filename + ".bak");
+        mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
         mStartCount++;
         mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -2736,7 +2742,7 @@
     }
 
     public BatteryStatsImpl(Parcel p) {
-        mFile = mBackupFile = null;
+        mFile = null;
         readFromParcel(p);
     }
 
@@ -2799,7 +2805,7 @@
         
         if (m == null) {
             // Not crashing might make board bringup easier.
-            Log.w(TAG, "Couldn't get kernel wake lock stats");
+            Slog.w(TAG, "Couldn't get kernel wake lock stats");
             return;
         }
 
@@ -3047,26 +3053,19 @@
         return u.getServiceStatsLocked(pkg, name);
     }
 
+    private static JournaledFile makeJournaledFile() {
+        final String base = "/data/system/device_policies.xml";
+        return new JournaledFile(new File(base), new File(base + ".tmp"));
+    }
+
     public void writeLocked() {
-        if ((mFile == null) || (mBackupFile == null)) {
-            Log.w("BatteryStats", "writeLocked: no file associated with this instance");
+        if (mFile == null) {
+            Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
             return;
         }
 
-        // Keep the old file around until we know the new one has
-        // been successfully written.
-        if (mFile.exists()) {
-            if (mBackupFile.exists()) {
-                mBackupFile.delete();
-            }
-            if (!mFile.renameTo(mBackupFile)) {
-                Log.w("BatteryStats", "Failed to back up file before writing new stats");
-                return;
-            }
-        }
-
         try {
-            FileOutputStream stream = new FileOutputStream(mFile);
+            FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
             Parcel out = Parcel.obtain();
             writeSummaryToParcel(out);
             stream.write(out.marshall());
@@ -3074,18 +3073,14 @@
 
             stream.flush();
             stream.close();
-            mBackupFile.delete();
+            mFile.commit();
 
             mLastWriteTime = SystemClock.elapsedRealtime();
             return;
         } catch (IOException e) {
-            Log.w("BatteryStats", "Error writing battery statistics", e);
+            Slog.w("BatteryStats", "Error writing battery statistics", e);
         }
-        if (mFile.exists()) {
-            if (!mFile.delete()) {
-                Log.w(TAG, "Failed to delete mangled file " + mFile);
-            }
-        }
+        mFile.rollback();
     }
 
     static byte[] readFully(FileInputStream stream) throws java.io.IOException {
@@ -3112,29 +3107,19 @@
     }
 
     public void readLocked() {
-        if ((mFile == null) || (mBackupFile == null)) {
-            Log.w("BatteryStats", "readLocked: no file associated with this instance");
+        if (mFile == null) {
+            Slog.w("BatteryStats", "readLocked: no file associated with this instance");
             return;
         }
 
         mUidStats.clear();
 
-        FileInputStream stream = null;
-        if (mBackupFile.exists()) {
-            try {
-                stream = new FileInputStream(mBackupFile);
-            } catch (java.io.IOException e) {
-                // We'll try for the normal settings file.
-            }
-        }
-
         try {
-            if (stream == null) {
-                if (!mFile.exists()) {
-                    return;
-                }
-                stream = new FileInputStream(mFile);
+            File file = mFile.chooseForRead();
+            if (!file.exists()) {
+                return;
             }
+            FileInputStream stream = new FileInputStream(file);
 
             byte[] raw = readFully(stream);
             Parcel in = Parcel.obtain();
@@ -3144,7 +3129,7 @@
 
             readSummaryFromParcel(in);
         } catch(java.io.IOException e) {
-            Log.e("BatteryStats", "Error reading battery statistics", e);
+            Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
     }
 
@@ -3155,7 +3140,7 @@
     private void readSummaryFromParcel(Parcel in) {
         final int version = in.readInt();
         if (version != VERSION) {
-            Log.w("BatteryStats", "readFromParcel: version got " + version
+            Slog.w("BatteryStats", "readFromParcel: version got " + version
                 + ", expected " + VERSION + "; erasing old stats");
             return;
         }
@@ -3197,6 +3182,10 @@
         mBluetoothOnTimer.readSummaryFromParcelLocked(in);
 
         int NKW = in.readInt();
+        if (NKW > 10000) {
+            Slog.w(TAG, "File corrupt: too many kernel wake locks " + NKW);
+            return;
+        }
         for (int ikw = 0; ikw < NKW; ikw++) {
             if (in.readInt() != 0) {
                 String kwltName = in.readString();
@@ -3207,6 +3196,10 @@
         sNumSpeedSteps = in.readInt();
 
         final int NU = in.readInt();
+        if (NU > 10000) {
+            Slog.w(TAG, "File corrupt: too many uids " + NU);
+            return;
+        }
         for (int iu = 0; iu < NU; iu++) {
             int uid = in.readInt();
             Uid u = new Uid(uid);
@@ -3235,6 +3228,10 @@
             }
             
             int NW = in.readInt();
+            if (NW > 10000) {
+                Slog.w(TAG, "File corrupt: too many wake locks " + NW);
+                return;
+            }
             for (int iw = 0; iw < NW; iw++) {
                 String wlName = in.readString();
                 if (in.readInt() != 0) {
@@ -3249,6 +3246,10 @@
             }
 
             int NP = in.readInt();
+            if (NP > 10000) {
+                Slog.w(TAG, "File corrupt: too many sensors " + NP);
+                return;
+            }
             for (int is = 0; is < NP; is++) {
                 int seNumber = in.readInt();
                 if (in.readInt() != 0) {
@@ -3258,6 +3259,10 @@
             }
 
             NP = in.readInt();
+            if (NP > 10000) {
+                Slog.w(TAG, "File corrupt: too many processes " + NP);
+                return;
+            }
             for (int ip = 0; ip < NP; ip++) {
                 String procName = in.readString();
                 Uid.Proc p = u.getProcessStatsLocked(procName);
@@ -3270,6 +3275,10 @@
             }
 
             NP = in.readInt();
+            if (NP > 10000) {
+                Slog.w(TAG, "File corrupt: too many packages " + NP);
+                return;
+            }
             for (int ip = 0; ip < NP; ip++) {
                 String pkgName = in.readString();
                 Uid.Pkg p = u.getPackageStatsLocked(pkgName);
diff --git a/core/java/com/android/internal/util/HierarchicalState.java b/core/java/com/android/internal/util/HierarchicalState.java
index 002338a..b37f46c 100644
--- a/core/java/com/android/internal/util/HierarchicalState.java
+++ b/core/java/com/android/internal/util/HierarchicalState.java
@@ -21,10 +21,9 @@
 /**
  * {@hide}
  *
- * The abstract class for implementing states in a
- * HierarchicalStateMachine and HandlerStateMachine.
+ * The class for implementing states in a HierarchicalStateMachine
  */
-public abstract class HierarchicalState {
+public class HierarchicalState {
 
     /**
      * Constructor
@@ -54,7 +53,9 @@
      *         if the parent state's processMessage should
      *         be invoked.
      */
-    abstract protected boolean processMessage(Message msg);
+    protected boolean processMessage(Message msg) {
+        return false;
+    }
 
     /**
      * Called when a state is exited.
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index 7d7f130..9911f48 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -574,23 +574,41 @@
             }
 
             /**
-             * Process the message abiding by the hierarchical semantics.
+             * Process the message abiding by the hierarchical semantics
+             * and perform any requested transitions.
              */
             processMsg(msg);
+            performTransitions();
 
+            if (mDbg) Log.d(TAG, "handleMessage: X");
+        }
+
+        /**
+         * Do any transitions
+         */
+        private void performTransitions() {
             /**
              * If transitionTo has been called, exit and then enter
-             * the appropriate states.
+             * the appropriate states. We loop on this to allow
+             * enter and exit methods to use transitionTo.
              */
-            if (mDestState != null) {
+            HierarchicalState destState = null;
+            while (mDestState != null) {
                 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
 
                 /**
+                 * Save mDestState locally and set to null
+                 * to know if enter/exit use transitionTo.
+                 */
+                destState = mDestState;
+                mDestState = null;
+
+                /**
                  * Determine the states to exit and enter and return the
                  * common ancestor state of the enter/exit states. Then
                  * invoke the exit methods then the enter methods.
                  */
-                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(mDestState);
+                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                 invokeExitMethods(commonStateInfo);
                 int stateStackEnteringIndex = moveTempStateStackToStateStack();
                 invokeEnterMethods(stateStackEnteringIndex);
@@ -603,25 +621,31 @@
                  * message queue.
                  */
                 moveDeferredMessageAtFrontOfQueue();
+            }
 
-                /**
-                 * Call halting() if we've transitioned to the halting
-                 * state. All subsequent messages will be processed in
-                 * in the halting state which invokes haltedProcessMessage(msg);
-                 */
-                if (mDestState == mQuittingState) {
+            /**
+             * After processing all transitions check and
+             * see if the last transition was to quit or halt.
+             */
+            if (destState != null) {
+                if (destState == mQuittingState) {
+                    /**
+                     * We are quitting so ignore all messages.
+                     */
                     mHsm.quitting();
                     if (mHsm.mHsmThread != null) {
                         // If we made the thread then quit looper
                         getLooper().quit();
                     }
-                } else if (mDestState == mHaltingState) {
+                } else if (destState == mHaltingState) {
+                    /**
+                     * Call halting() if we've transitioned to the halting
+                     * state. All subsequent messages will be processed in
+                     * in the halting state which invokes haltedProcessMessage(msg);
+                     */
                     mHsm.halting();
                 }
-                mDestState = null;
             }
-
-            if (mDbg) Log.d(TAG, "handleMessage: X");
         }
 
         /**
@@ -657,6 +681,11 @@
             mIsConstructionCompleted = true;
             invokeEnterMethods(0);
 
+            /**
+             * Perform any transitions requested by the enter methods
+             */
+            performTransitions();
+
             if (mDbg) Log.d(TAG, "completeConstruction: X");
         }
 
@@ -1167,7 +1196,6 @@
         return Message.obtain(mHsmHandler, what, obj);
     }
 
-
     /**
      * Enqueue a message to this state machine.
      */
diff --git a/services/java/com/android/server/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
similarity index 98%
rename from services/java/com/android/server/JournaledFile.java
rename to core/java/com/android/internal/util/JournaledFile.java
index 3d1f52d..af0c6c6 100644
--- a/services/java/com/android/server/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.internal.util;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index ee2fc12..f487a16 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -60,6 +60,7 @@
     
     public PointerLocationView(Context c) {
         super(c);
+        setFocusable(true);
         mVC = ViewConfiguration.get(c);
         mTextPaint = new Paint();
         mTextPaint.setAntiAlias(true);
@@ -350,4 +351,11 @@
         addTouchEvent(event);
         return true;
     }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent event) {
+        Log.i("Pointer", "Trackball: " + event);
+        return super.onTrackballEvent(event);
+    }
+    
 }
diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp
index 3afe5f5..94e4409 100644
--- a/core/jni/android_bluetooth_ScoSocket.cpp
+++ b/core/jni/android_bluetooth_ScoSocket.cpp
@@ -37,6 +37,23 @@
 #ifdef HAVE_BLUETOOTH
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+
+#define MAX_LINE 255
+
+/*
+ * Defines the module strings used in the blacklist file.
+ * These are used by consumers of the blacklist file to see if the line is
+ * used by that module.
+ */
+#define SCO_BLACKLIST_MODULE_NAME "scoSocket"
+
+
+/* Define the type strings used in the blacklist file. */
+#define BLACKLIST_BY_NAME "name"
+#define BLACKLIST_BY_PARTIAL_NAME "partial_name"
+#define BLACKLIST_BY_OUI "vendor_oui"
+
 #endif
 
 /* Ideally, blocking I/O on a SCO socket would return when another thread
@@ -67,11 +84,28 @@
 
 struct thread_data_t;
 static void *work_thread(void *arg);
-static int connect_work(const char *address);
+static int connect_work(const char *address, uint16_t sco_pkt_type);
 static int accept_work(int signal_sk);
 static void wait_for_close(int sk, int signal_sk);
 static void closeNative(JNIEnv *env, jobject object);
 
+static void parseBlacklist(void);
+static uint16_t getScoType(char *address, const char *name);
+
+#define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
+
+/* Blacklist data */
+typedef struct scoBlacklist {
+    int fieldType;
+    char *value;
+    uint16_t scoType;
+    struct scoBlacklist *next;
+} scoBlacklist_t;
+
+#define BL_TYPE_NAME 1   // Field type is name string
+
+static scoBlacklist_t *blacklist = NULL;
+
 /* shared native data - protected by mutex */
 typedef struct {
     pthread_mutex_t mutex;
@@ -87,11 +121,144 @@
     bool is_accept;        // accept (listening) or connect (outgoing) thread
     int signal_sk;         // socket for thread to listen for unblock signal
     char address[BTADDR_SIZE];  // BT addres as string
+    uint16_t sco_pkt_type;   // SCO packet types supported
 };
 
 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
     return (native_data_t *)(env->GetIntField(object, field_mNativeData));
 }
+
+static uint16_t str2scoType (char *key) {
+    LOGV("%s: key = %s", __FUNCTION__, key);
+    if (COMPARE_STRING(key, "ESCO_HV1"))
+        return ESCO_HV1;
+    if (COMPARE_STRING(key, "ESCO_HV2"))
+        return ESCO_HV2;
+    if (COMPARE_STRING(key, "ESCO_HV3"))
+        return ESCO_HV3;
+    if (COMPARE_STRING(key, "ESCO_EV3"))
+        return ESCO_EV3;
+    if (COMPARE_STRING(key, "ESCO_EV4"))
+        return ESCO_EV4;
+    if (COMPARE_STRING(key, "ESCO_EV5"))
+        return ESCO_EV5;
+    if (COMPARE_STRING(key, "ESCO_2EV3"))
+        return ESCO_2EV3;
+    if (COMPARE_STRING(key, "ESCO_3EV3"))
+        return ESCO_3EV3;
+    if (COMPARE_STRING(key, "ESCO_2EV5"))
+        return ESCO_2EV5;
+    if (COMPARE_STRING(key, "ESCO_3EV5"))
+        return ESCO_3EV5;
+    if (COMPARE_STRING(key, "SCO_ESCO_MASK"))
+        return SCO_ESCO_MASK;
+    if (COMPARE_STRING(key, "EDR_ESCO_MASK"))
+        return EDR_ESCO_MASK;
+    if (COMPARE_STRING(key, "ALL_ESCO_MASK"))
+        return ALL_ESCO_MASK;
+    LOGE("Unknown SCO Type (%s) skipping",key);
+    return 0;
+}
+
+static void parseBlacklist(void) {
+    const char *filename = "/etc/bluetooth/blacklist.conf";
+    char line[MAX_LINE];
+    scoBlacklist_t *list = NULL;
+    scoBlacklist_t *newelem;
+
+    LOGV(__FUNCTION__);
+
+    /* Open file */
+    FILE *fp = fopen(filename, "r");
+    if(!fp) {
+        LOGE("Error(%s)opening blacklist file", strerror(errno));
+        return;
+    }
+
+    while (fgets(line, MAX_LINE, fp) != NULL) {
+        if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
+            continue;
+        char *module = strtok(line,":");
+        if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) {
+            newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t));
+            if (newelem == NULL) {
+                LOGE("%s: out of memory!", __FUNCTION__);
+                return;
+            }
+            // parse line
+            char *type = strtok(NULL, ",");
+            char *valueList = strtok(NULL, ",");
+            char *paramList = strtok(NULL, ",");
+            if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) {
+                // Extract Name from Value list
+                newelem->fieldType = BL_TYPE_NAME;
+                newelem->value = (char *)calloc(1, strlen(valueList));
+                if (newelem->value == NULL) {
+                    LOGE("%s: out of memory!", __FUNCTION__);
+                    continue;
+                }
+                valueList++;  // Skip open quote
+                strncpy(newelem->value, valueList, strlen(valueList) - 1);
+
+                // Get Sco Settings from Parameters
+                char *param = strtok(paramList, ";");
+                uint16_t scoTypes = 0;
+                while (param != NULL) {
+                    uint16_t sco;
+                    if (param[0] == '-') {
+                        param++;
+                        sco = str2scoType(param);
+                        if (sco != 0)
+                            scoTypes &= ~sco;
+                    } else if (param[0] == '+') {
+                        param++;
+                        sco = str2scoType(param);
+                        if (sco != 0)
+                            scoTypes |= sco;
+                    } else if (param[0] == '=') {
+                        param++;
+                        sco = str2scoType(param);
+                        if (sco != 0)
+                            scoTypes = sco;
+                    } else {
+                        LOGE("Invalid SCO type must be =, + or -");
+                    }
+                    param = strtok(NULL, ";");
+                }
+                newelem->scoType = scoTypes;
+            } else {
+                LOGE("Unknown SCO type entry in Blacklist file");
+                continue;
+            }
+            if (list) {
+                list->next = newelem;
+                list = newelem;
+            } else {
+                blacklist = list = newelem;
+            }
+            LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
+                 newelem->scoType);
+        }
+    }
+    fclose(fp);
+    return;
+}
+static uint16_t getScoType(char *address, const char *name) {
+    uint16_t ret = 0;
+    scoBlacklist_t *list = blacklist;
+
+    while (list != NULL) {
+        if (list->fieldType == BL_TYPE_NAME) {
+            if (COMPARE_STRING(name, list->value)) {
+                ret = list->scoType;
+                break;
+            }
+        }
+        list = list->next;
+    }
+    LOGI("%s %s - 0x%x",  __FUNCTION__, name, ret);
+    return ret;
+}
 #endif
 
 static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -104,6 +271,9 @@
     method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
     method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
     method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
+
+    /* Read the blacklist file in here */
+    parseBlacklist();
 #endif
 }
 
@@ -192,7 +362,9 @@
     return JNI_FALSE;
 }
 
-static jboolean connectNative(JNIEnv *env, jobject object, jstring address) {
+static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
+        jstring name) {
+
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
@@ -200,6 +372,7 @@
     pthread_t thread;
     struct thread_data_t *data;
     const char *c_address;
+    const char *c_name;
 
     pthread_mutex_lock(&nat->mutex);
     if (nat->signal_sk != -1) {
@@ -231,6 +404,15 @@
     env->ReleaseStringUTFChars(address, c_address);
     data->is_accept = false;
 
+    if (name == NULL) {
+        LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
+        data->sco_pkt_type = 0;
+    } else {
+        c_name = env->GetStringUTFChars(name, NULL);
+        /* See if this device is in the black list */
+        data->sco_pkt_type = getScoType(data->address, c_name);
+        env->ReleaseStringUTFChars(name, c_name);
+    }
     if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
         LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
         return JNI_FALSE;
@@ -282,7 +464,7 @@
         sk = accept_work(data->signal_sk);
         LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
     } else {
-        sk = connect_work(data->address);
+        sk = connect_work(data->address, data->sco_pkt_type);
     }
 
     /* callback with connection result */
@@ -426,7 +608,7 @@
     return -1;
 }
 
-static int connect_work(const char *address) {
+static int connect_work(const char *address, uint16_t sco_pkt_type) {
     LOGV(__FUNCTION__);
     struct sockaddr_sco addr;
     int sk = -1;
@@ -449,6 +631,7 @@
     memset(&addr, 0, sizeof(addr));
     addr.sco_family = AF_BLUETOOTH;
     get_bdaddr(address, &addr.sco_bdaddr);
+    addr.sco_pkt_type = sco_pkt_type;
     LOGI("Connecting to socket");
     while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
         if (errno != EINTR) {
@@ -493,7 +676,7 @@
     {"classInitNative", "()V", (void*)classInitNative},
     {"initNative", "()V", (void *)initNative},
     {"destroyNative", "()V", (void *)destroyNative},
-    {"connectNative", "(Ljava/lang/String;)Z", (void *)connectNative},
+    {"connectNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectNative},
     {"acceptNative", "()Z", (void *)acceptNative},
     {"closeNative", "()V", (void *)closeNative},
 };
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
deleted file mode 100644
index 651212f..0000000
--- a/core/res/res/drawable/pattern_underwear.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/values-en-rUS/donottranslate-names.xml b/core/res/res/values-en-rUS/donottranslate-names.xml
index f8ec765..aa0abe3 100644
--- a/core/res/res/values-en-rUS/donottranslate-names.xml
+++ b/core/res/res/values-en-rUS/donottranslate-names.xml
@@ -4,35 +4,51 @@
     
     <!-- various string resources for Contacts -->
     <string-array name="common_nicknames">
-        <item>Abigail, Abbie, Gail, Gayle</item>
+        <item>Abigail, Abbie</item>
+        <item>Abigail, Gail, Gayle</item>
         <item>Abe, Abraham</item>
-        <item>Aggie, Agatha, Agnes</item>
-        <item>Albert, Al, Bert, Bertie</item>
-        <item>Alexander, Al, Alec, Alex, Lex, Sasha</item>
-        <item>Alexandra, Al, Allie, Ally, Lex, Lexie, Sandra, Sandy, Sasha</item>
+        <item>Agatha, Aggie</item>
+        <item>Agatha, Agnes</item>
+        <item>Albert, Al</item>
+        <item>Albert, Bert, Bertie</item>
+        <item>Alexander, Al, Alex</item>
+        <item>Alexander, Al, Alec</item>
+        <item>Alexander, Alex, Lex</item>
+        <item>Alexander, Alex, Sasha</item>
+        <item>Alexandra, Al, Allie, Ally</item>
+        <item>Alexandra, Lex, Lexie</item>
+        <item>Alexandra, Sandra, Sandy</item>
+        <item>Alexandra, Sasha</item>
         <item>Alf, Alfred, Alfredo, Alfie</item>
         <item>Alice, Allie, Ally</item>
         <item>Alison, Allie, Ally</item>
         <item>Allison, Allie, Ally</item>
         <item>Amanda, Mandi, Mandy</item>
         <item>Andrea, Andie</item>
-        <item>Andrew, Andy, Drew</item>
+        <item>Andrew, Andy</item>
+        <item>Andrew, Drew</item>
         <item>Anne, Annie, Annette</item>
         <item>Anthony, Tony, Toni, Tone</item>
         <item>Arthur, Art, Arty</item>
-        <item>Barbara, Babs, Barb, Barbie</item>
-        <item>Benjamin, Ben, Benji, Benny</item>
-        <item>Bernard, Bern, Bernie, Barnie</item>
+        <item>Barbara, Barb, Barbie</item>
+        <item>Benjamin, Ben, Benny</item>
+        <item>Benjamin, Benji</item>
+        <item>Bernard, Barnie</item>
+        <item>Bernard, Bern, Bernie</item>
         <item>Bertram, Bert, Bertie</item>
         <item>Bradly, Brad</item>
         <item>Calvin, Cal</item>
         <item>Catherine, Cat, Cate, Cath, Catie, Cathy, Kat, Kate, Katie, Kathy</item>
         <item>Carrie, Caroline, Carolyn</item>
-        <item>Charles, Chuck, Chaz, Charlie, Buck</item>
+        <item>Charles, Charlie</item>
+        <item>Charles, Chuck</item>
+        <item>Charles, Chaz</item>
+        <item>Charles, Buck</item>
         <item>Christine, Chrissy, Chrissie</item>
         <item>Christopher, Chris</item>
         <item>Clinton, Clint</item>
-        <item>Cynthia, Cindy, Cynth</item>
+        <item>Cynthia, Cindy</item>
+        <item>Cynthia, Cynth</item>
         <item>Daniel, Dan, Danny</item>
         <item>David, Dave</item>
         <item>Deborah, Deb, Debbie</item>
@@ -43,30 +59,46 @@
         <item>Dorothea, Dot, Dotty</item>
         <item>Dorothy, Dot, Dotty</item>
         <item>Douglas, Doug</item>
-        <item>Edward, Ed, Eddie, Ned, Neddie, Neddy, Ted, Teddy, Teddie</item>
-        <item>Eleanor, Ella, Ellie, Elle</item>
+        <item>Edward, Ed, Eddie</item>
+        <item>Edward, Ned, Neddie, Neddy</item>
+        <item>Edward, Ted, Teddy, Teddie</item>
+        <item>Eleanor, Ella</item>
+        <item>Eleanor, Ellie, Elle</item>
         <item>Elisabetta, Betta</item>
-        <item>Elizabeth, Beth, Bess, Bessie, Betsy, Betty, Bette, Eliza, Lisa, Liza, Liz</item>
+        <item>Elizabeth, Beth</item>
+        <item>Elizabeth, Bess, Bessie</item>
+        <item>Elizabeth, Betsy</item>
+        <item>Elizabeth, Betty, Bette</item>
+        <item>Elizabeth, Eliza</item>
+        <item>Elizabeth, Lisa, Liza, Liz</item>
         <item>Emily, Em, Ems, Emmy</item>
         <item>Emma, Em, Ems, Emmy</item>
         <item>Eugene, Gene</item>
         <item>Fannie, Fanny</item>
         <item>Florence, Flo</item>
-        <item>Frances, Fran, Francie</item>
-        <item>Francis, Fran, Frank, Frankie</item>
+        <item>Frances, Fran</item>
+        <item>Frances, Francie</item>
+        <item>Francis, Fran</item>
+        <item>Francis, Frank, Frankie</item>
         <item>Frederick, Fred, Freddy</item>
         <item>Gabriel, Gabe</item>
         <item>Gerald, Gerry</item>
         <item>Gerard, Gerry</item>
         <item>Gregory, Greg, Gregg</item>
-        <item>Harold, Hal, Harry</item>
-        <item>Henry, Hal, Hank, Harry</item>
+        <item>Harold, Hal</item>
+        <item>Harold, Harry</item>
+        <item>Henry, Hal</item>
+        <item>Henry, Hank</item>
+        <item>Henry, Harry</item>
         <item>Herbert, Bert, Bertie</item>
         <item>Irving, Irv</item>
-        <item>Isabella, Isa, Izzy, Bella</item>
+        <item>Isabella, Isa</item>
+        <item>Isabella, Izzy</item>
+        <item>Isabella, Bella</item>
         <item>Jacob, Jake</item>
         <item>Jacqueline, Jackie</item>
-        <item>James, Jim, Jimmy, Jamie, Jock</item>
+        <item>James, Jim, Jimmy</item>
+        <item>James, Jamie</item>
         <item>Janet, Jan</item>
         <item>Janice, Jan</item>
         <item>Jason, Jay</item>
@@ -90,10 +122,15 @@
         <item>Laura, Lauri, Laurie</item>
         <item>Lauren, Lauri, Laurie</item>
         <item>Lawrence, Larry</item>
-        <item>Leonard, Leo, Len, Lenny</item>
-        <item>Leopold, Leo, Len, Lenny</item>
+        <item>Leonard, Leo</item>
+        <item>Leonard, Len, Lenny</item>
+        <item>Leopold, Leo</item>
         <item>Madeline, Maddie, Maddy</item>
-        <item>Margaret, Marge, Marg, Maggie, Mags, Meg, Peggy, Greta, Gretchen</item>
+        <item>Margaret, Marge, Marg</item>
+        <item>Margaret, Maggie, Mags</item>
+        <item>Margaret, Meg</item>
+        <item>Margaret, Peggy</item>
+        <item>Margaret, Greta, Gretchen</item>
         <item>Martin, Martie, Marty</item>
         <item>Matthew, Matt, Mattie</item>
         <item>Maureen, Mo</item>
@@ -101,27 +138,36 @@
         <item>Maxwell, Max</item>
         <item>Maximilian, Maxim, Max</item>
         <item>Megan, Meg</item>
-        <item>Michael, Mickey, Mick, Mike, Mikey</item>
+        <item>Michael, Mickey, Mick</item>
+        <item>Michael, Mike, Mikey</item>
         <item>Morris, Mo</item>
         <item>Nancy, Nan</item>
-        <item>Nathan, Nat, Nate</item>
-        <item>Nathaniel, Nat, Nate</item>
+        <item>Nathan, Nat</item>
+        <item>Nathan, Nate</item>
+        <item>Nathaniel, Nat</item>
+        <item>Nathaniel, Nate</item>
         <item>Nicholas, Nick</item>
         <item>Nicole, Nicky, Nickie, Nikky</item>
         <item>Pamela, Pam</item>
-        <item>Patricia, Pat, Patsy, Patty, Trish, Tricia</item>
-        <item>Patrick, Pat, Patter</item>
+        <item>Patricia, Pat, Patty</item>
+        <item>Patricia, Patsy</item>
+        <item>Patricia, Trish, Tricia</item>
+        <item>Patrick, Pat</item>
         <item>Penelope, Penny</item>
         <item>Peter, Pete</item>
         <item>Raymond, Ray</item>
         <item>Philip, Phil</item>
         <item>Rebecca, Becca, Becky</item>
-        <item>Richard, Rick, Rich, Dick</item>
-        <item>Robert, Bob, Rob, Robbie, Bobby, Rab</item>
+        <item>Richard, Rick</item>
+        <item>Richard, Rich</item>
+        <item>Richard, Dick</item>
+        <item>Robert, Bob, Bobby</item>
+        <item>Robert, Rob, Robbie</item>
         <item>Rodney. Rod</item>
         <item>Ronald, Ron, Ronnie</item>
         <item>Rosemary, Rosie, Rose</item>
-        <item>Russell, Russ, Rusty</item>
+        <item>Russell, Russ</item>
+        <item>Russell, Rusty</item>
         <item>Ryan, Ry</item>
         <item>Samantha, Sam</item>
         <item>Samuel, Sam, Sammy</item>
@@ -130,23 +176,34 @@
         <item>Stephen, Steve</item>
         <item>Steven, Steve</item>
         <item>Stuart, Stu</item>
-        <item>Susan, Sue, Susie, Suzie</item>
-        <item>Suzanne, Sue, Susie, Suzie</item>
+        <item>Susan, Sue</item>
+        <item>Susan, Susie, Suzie</item>
+        <item>Suzanne, Sue</item>
+        <item>Suzanne, Susie, Suzie</item>
         <item>Tamara, Tammy</item>
         <item>Theresa, Teresa</item>
-        <item>Theodora, Teddie, Thea, Theo</item>
-        <item>Theodore, Ted, Teddy, Theo</item>
+        <item>Theodora, Teddie</item>
+        <item>Theodora, Thea</item>
+        <item>Theodore, Ted, Teddy</item>
+        <item>Theodore, Theo</item>
         <item>Thomas, Tom, Thom, Tommy</item>
         <item>Timothy, Tim, Timmy</item>
         <item>Valerie, Val</item>
-        <item>Veronica, Ronnie, Roni, Nica, Nikki, Nikka</item>
+        <item>Veronica, Ronnie, Roni</item>
+        <item>Veronica, Nica, Nikki, Nikka</item>
         <item>Victor, Vic</item>
-        <item>Victoria, Vicky, Vicki, Vickie, Tori</item>
-        <item>Vincent, Vince, Vin, Vinnie</item>
+        <item>Victoria, Vicky, Vicki, Vickie</item>
+        <item>Victoria, Tori</item>
+        <item>Vincent, Vince</item>
+        <item>Vincent, Vin, Vinnie</item>
         <item>Vivian, Vivi</item>
-        <item>Walter, Walt, Wally</item>
-        <item>Wendy, Wen, Wendel</item>
-        <item>William, Bill, Billy, Will, Willy, Liam</item>
+        <item>Walter, Wally</item>
+        <item>Walter, Walt</item>
+        <item>Wendy, Wen</item>
+        <item>Wendy, Wendel</item>
+        <item>William, Bill, Billy</item>
+        <item>William, Will, Willy</item>
+        <item>William, Liam</item>
         <item>Yvonna, Vonna</item>
         <item>Zachary, Zach, Zack, Zac</item>
     </string-array>
diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
index c51fecc..89b3fb6 100644
--- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
+++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
@@ -48,7 +48,7 @@
     private static final int TEST_CMD_6 = 6;
 
     private static final boolean DBG = true;
-    private static final boolean WAIT_FOR_DEBUGGER = false;
+    private static final boolean WAIT_FOR_DEBUGGER = true;
     private static final String TAG = "HierarchicalStateMachineTest";
 
     /**
@@ -152,6 +152,154 @@
     }
 
     /**
+     * Test enter/exit can use transitionTo
+     */
+    class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
+        StateMachineEnterExitTransitionToTest(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+            addState(mS2);
+            addState(mS3);
+            addState(mS4);
+
+            // Set the initial state
+            setInitialState(mS1);
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected void enter() {
+                // Test that a transition in enter and the initial state works
+                mS1EnterCount += 1;
+                transitionTo(mS2);
+                Log.d(TAG, "S1.enter");
+            }
+            @Override protected void exit() {
+                mS1ExitCount += 1;
+                Log.d(TAG, "S1.exit");
+            }
+        }
+
+        class S2 extends HierarchicalState {
+            @Override protected void enter() {
+                mS2EnterCount += 1;
+                Log.d(TAG, "S2.enter");
+            }
+            @Override protected void exit() {
+                // Test transition in exit work
+                mS2ExitCount += 1;
+                transitionTo(mS4);
+                Log.d(TAG, "S2.exit");
+            }
+            @Override protected boolean processMessage(Message message) {
+                // Start a transition to S3 but it will be
+                // changed to a transition to S4
+                transitionTo(mS3);
+                Log.d(TAG, "S2.processMessage");
+                return true;
+            }
+        }
+
+        class S3 extends HierarchicalState {
+            @Override protected void enter() {
+                // Test that we can do halting in an enter/exit
+                transitionToHaltingState();
+                mS3EnterCount += 1;
+                Log.d(TAG, "S3.enter");
+            }
+            @Override protected void exit() {
+                mS3ExitCount += 1;
+                Log.d(TAG, "S3.exit");
+            }
+        }
+
+
+        class S4 extends HierarchicalState {
+            @Override protected void enter() {
+                // Test that we can do halting in an enter/exit
+                transitionToHaltingState();
+                mS4EnterCount += 1;
+                Log.d(TAG, "S4.enter");
+            }
+            @Override protected void exit() {
+                mS4ExitCount += 1;
+                Log.d(TAG, "S4.exit");
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachineEnterExitTransitionToTest mThisSm;
+        private S1 mS1 = new S1();
+        private S2 mS2 = new S2();
+        private S3 mS3 = new S3();
+        private S4 mS4 = new S4();
+        private int mS1EnterCount = 0;
+        private int mS1ExitCount = 0;
+        private int mS2EnterCount = 0;
+        private int mS2ExitCount = 0;
+        private int mS3EnterCount = 0;
+        private int mS3ExitCount = 0;
+        private int mS4EnterCount = 0;
+        private int mS4ExitCount = 0;
+    }
+
+    @SmallTest
+    public void testStateMachineEnterExitTransitionToTest() throws Exception {
+        //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+        StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
+            new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
+        smEnterExitTranstionToTest.start();
+        if (smEnterExitTranstionToTest.isDbg()) {
+            Log.d(TAG, "testStateMachineEnterExitTransitionToTest E");
+        }
+
+        synchronized (smEnterExitTranstionToTest) {
+            smEnterExitTranstionToTest.sendMessage(1);
+
+            try {
+                // wait for the messages to be handled
+                smEnterExitTranstionToTest.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting "
+                    + e.getMessage());
+            }
+        }
+
+        assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
+
+        ProcessedMessages.Info pmi;
+
+        // Message should be handled by mS2.
+        pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
+        assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
+
+        assertEquals(smEnterExitTranstionToTest.mS1EnterCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS1ExitCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS2EnterCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS2ExitCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
+        assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
+
+        if (smEnterExitTranstionToTest.isDbg()) {
+            Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
+        }
+    }
+
+    /**
      * Tests that ProcessedMessage works as a circular buffer.
      */
     class StateMachine0 extends HierarchicalStateMachine {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 473f580..0016503 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -935,17 +935,27 @@
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
             
-            bool doBackground = !gDisableBackgroundScheduling &&
-                    getpriority(PRIO_PROCESS, mMyThreadId)
-                            >= ANDROID_PRIORITY_BACKGROUND;
-            if (doBackground) {
-                // We have inherited a background priority from the caller.
-                // Ensure this thread is in the background scheduling class,
-                // since the driver won't modify scheduling classes for us.
-                androidSetThreadSchedulingGroup(mMyThreadId,
-                        ANDROID_TGROUP_BG_NONINTERACT);
+            int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
+            if (gDisableBackgroundScheduling) {
+                if (curPrio > ANDROID_PRIORITY_NORMAL) {
+                    // We have inherited a reduced priority from the caller, but do not
+                    // want to run in that state in this process.  The driver set our
+                    // priority already (though not our scheduling class), so bounce
+                    // it back to the default before invoking the transaction.
+                    setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
+                }
+            } else {
+                if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
+                    // We want to use the inherited priority from the caller.
+                    // Ensure this thread is in the background scheduling class,
+                    // since the driver won't modify scheduling classes for us.
+                    // The scheduling group is reset to default by the caller
+                    // once this method returns after the transaction is complete.
+                    androidSetThreadSchedulingGroup(mMyThreadId,
+                                                    ANDROID_TGROUP_BG_NONINTERACT);
+                }
             }
-            
+
             //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
             
             Parcel reply;
@@ -982,14 +992,7 @@
             
             mCallingPid = origPid;
             mCallingUid = origUid;
-            
-            if (doBackground) {
-                // We moved to the background scheduling group to execute
-                // this transaction, so now that we are done go back in the
-                // foreground.
-                androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
-            }
-            
+
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
                 alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3a3c66b..68b351b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -693,10 +693,8 @@
     //====================================================================
     // Bluetooth SCO control
     /**
-     * @hide
-     * TODO unhide for SDK
      * Sticky broadcast intent action indicating that the bluetoooth SCO audio
-     * connection state has changed. The intent contains on extra {@link EXTRA_SCO_AUDIO_STATE}
+     * connection state has changed. The intent contains on extra {@link #EXTRA_SCO_AUDIO_STATE}
      * indicating the new state which is either {@link #SCO_AUDIO_STATE_DISCONNECTED}
      * or {@link #SCO_AUDIO_STATE_CONNECTED}
      *
@@ -706,8 +704,6 @@
     public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
             "android.media.SCO_AUDIO_STATE_CHANGED";
     /**
-     * @hide
-     * TODO unhide for SDK
      * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new
      * bluetooth SCO connection state.
      */
@@ -715,22 +711,16 @@
             "android.media.extra.SCO_AUDIO_STATE";
 
     /**
-     * @hide
-     * TODO unhide for SDK
      * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
      * SCO audio channel is not established
      */
     public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
     /**
-     * @hide
-     * TODO unhide for SDK
      * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
      * SCO audio channel is established
      */
     public static final int SCO_AUDIO_STATE_CONNECTED = 1;
     /**
-     * @hide
-     * TODO unhide for SDK
      * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that
      * there was an error trying to obtain the state
      */
@@ -738,8 +728,6 @@
 
 
     /**
-     * @hide
-     * TODO unhide for SDK
      * Indicates if current platform supports use of SCO for off call use cases.
      * Application wanted to use bluetooth SCO audio when the phone is not in call
      * must first call thsi method to make sure that the platform supports this
@@ -754,8 +742,6 @@
     }
 
     /**
-     * @hide
-     * TODO unhide for SDK
      * Start bluetooth SCO audio connection.
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
@@ -772,7 +758,7 @@
      * down the bluetooth connection.
      * <p>Even if a SCO connection is established, the following restrictions apply on audio
      * output streams so that they can be routed to SCO headset:
-     * - the stream type must be {@link #STREAM_VOICE_CALL} or {@link #STREAM_BLUETOOTH_SCO}
+     * - the stream type must be {@link #STREAM_VOICE_CALL}
      * - the format must be mono
      * - the sampling must be 16kHz or 8kHz
      * <p>The following restrictions apply on input streams:
@@ -797,8 +783,6 @@
     }
 
     /**
-     * @hide
-     * TODO unhide for SDK
      * Stop bluetooth SCO audio connection.
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
@@ -1260,16 +1244,6 @@
     }
 
     /**
-     * Used to indicate a loss of audio focus of unknown duration.
-     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
-     */
-    public static final int AUDIOFOCUS_LOSS = -1;
-    /**
-     * Used to indicate a transient loss of audio focus.
-     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
-     */
-    public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
-    /**
      * Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
      * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
      * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
@@ -1283,6 +1257,34 @@
      * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
      */
     public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
+    /**
+     * Used to indicate a temporary request of audio focus, anticipated to last a short
+     * amount of time, and where it is acceptable for other audio applications to keep playing
+     * after having lowered their output level (also referred to as "ducking").
+     * Examples of temporary changes are the playback of driving directions where playback of music
+     * in the background is acceptable.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
+     */
+    public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
+    /**
+     * Used to indicate a loss of audio focus of unknown duration.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
+    /**
+     * Used to indicate a transient loss of audio focus.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
+    /**
+     * Used to indicate a transient loss of audio focus where the loser of the audio focus can
+     * lower its output volume if it wants to continue playing (also referred to as "ducking"), as
+     * the new focus owner doesn't require others to be silent.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
+            -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
 
     /**
      * Interface definition for a callback to be invoked when the audio focus of the system is
@@ -1294,11 +1296,12 @@
          * The focusChange value indicates whether the focus was gained,
          * whether the focus was lost, and whether that loss is transient, or whether the new focus
          * holder will hold it for an unknown amount of time.
-         * When losing focus, listeners can use the duration hint to decide what
-         * behavior to adopt when losing focus. A music player could for instance elect to duck its
-         * music stream for transient focus losses, and pause otherwise.
-         * @param focusChange one of {@link AudioManager#AUDIOFOCUS_GAIN},
-         *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}.
+         * When losing focus, listeners can use the focus change information to decide what
+         * behavior to adopt when losing focus. A music player could for instance elect to lower
+         * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
+         * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
+         *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
+         *   and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
          */
         public void onAudioFocusChanged(int focusChange);
     }
@@ -1324,14 +1327,7 @@
      */
     private FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
             new FocusEventHandlerDelegate();
-    /**
-     * Event id denotes a loss of focus
-     */
-    private static final int AUDIOFOCUS_EVENT_LOSS  = 0;
-    /**
-     * Event id denotes a gain of focus
-     */
-    private static final int AUDIOFOCUS_EVENT_GAIN  = 1;
+
     /**
      * Helper class to handle the forwarding of audio focus events to the appropriate listener
      */
@@ -1432,8 +1428,10 @@
      *  @param streamType the main audio stream type affected by the focus request
      *  @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
      *      is temporary, and focus will be abandonned shortly. Examples of transient requests are
-     *      for the playback of driving directions, or notifications sounds. Use
-     *      {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
+     *      for the playback of driving directions, or notifications sounds.
+     *      Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
+     *      the previous focus owner to keep playing if it ducks its audio output.
+     *      Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
      *      as the playback of a song or a video.
      *  @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
      */
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 75e51f9..1992c93 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1822,7 +1822,7 @@
         public IAudioFocusDispatcher mFocusDispatcher = null;
         public IBinder mSourceRef = null;
         public String mClientId;
-        public int mDurationHint;
+        public int mFocusChangeType;
 
         public FocusStackEntry() {
         }
@@ -1834,7 +1834,7 @@
             mFocusDispatcher = afl;
             mSourceRef = source;
             mClientId = id;
-            mDurationHint = duration;
+            mFocusChangeType = duration;
         }
     }
 
@@ -1851,7 +1851,7 @@
             while(stackIterator.hasNext()) {
                 FocusStackEntry fse = stackIterator.next();
                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
-                        + " -- duration: " +fse.mDurationHint);
+                        + " -- duration: " +fse.mFocusChangeType);
             }
         }
     }
@@ -1953,7 +1953,7 @@
 
 
     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
-    public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId) {
         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
         // the main stream type for the audio focus request is currently not used. It may
@@ -1970,7 +1970,7 @@
 
         synchronized(mFocusStack) {
             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
-                mFocusStack.peek().mDurationHint = durationHint;
+                mFocusStack.peek().mFocusChangeType = focusChangeHint;
                 // if focus is already owned by this client, don't do anything
                 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
             }
@@ -1979,9 +1979,7 @@
             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
                 try {
                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                            (durationHint == AudioManager.AUDIOFOCUS_GAIN) ?
-                                    AudioManager.AUDIOFOCUS_LOSS :
-                                        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
+                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
                             mFocusStack.peek().mClientId);
                 } catch (RemoteException e) {
                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
@@ -1990,7 +1988,7 @@
             }
 
             // push focus requester at the top of the audio focus stack
-            mFocusStack.push(new FocusStackEntry(mainStreamType, durationHint, false, fd, cb,
+            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
                     clientId));
         }//synchronized(mFocusStack)
 
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 1047fa4..f845fec1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -634,8 +634,12 @@
             } else if (MediaFile.isImageFileType(mFileType)) {
                 // FIXME - add DESCRIPTION
             } else if (MediaFile.isAudioFileType(mFileType)) {
-                map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING));
-                map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING));
+                map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
+                        mArtist : MediaStore.UNKNOWN_STRING);
+                map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
+                        mAlbumArtist.length() > 0) ? mAlbumArtist : null);
+                map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
+                        mAlbum : MediaStore.UNKNOWN_STRING);
                 map.put(Audio.Media.COMPOSER, mComposer);
                 if (mYear != 0) {
                     map.put(Audio.Media.YEAR, mYear);
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index a555244..1d71577 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 8cd9578..f30346b 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -839,8 +839,6 @@
                                         addLocked(di, curTimeNano, ev.flags,
                                                 RawInputEvent.CLASS_TRACKBALL, me);
                                     }
-                                    
-                                    ms.finish();
                                 }
                             }
                         }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 5be919d..a3f2e09 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -483,8 +483,8 @@
              * argv7 - Preamble
              * argv8 - Max SCB
              */
-            String str = String.format("softap set " + wlanIface + " " + softapIface + " %s %s %s",
-                                       wifiConfig.SSID,
+            String str = String.format("softap set " + wlanIface + " " + softapIface +
+                                       " \"%s\" %s %s", wifiConfig.SSID,
                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
                                        "wpa2-psk" : "open",
                                        wifiConfig.preSharedKey);
@@ -511,7 +511,7 @@
             mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
         } else {
             String str = String.format("softap set " + wlanIface + " " + softapIface +
-                                       " %s %s %s", wifiConfig.SSID,
+                                       " \"%s\" %s %s", wifiConfig.SSID,
                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
                                        "wpa2-psk" : "open",
                                        wifiConfig.preSharedKey);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 87a744e..48b3fbb 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -20,8 +20,8 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
-import com.android.server.JournaledFile;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -851,7 +851,8 @@
             mFrameworkInstallObserver = new AppDirObserver(
                 mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
             mFrameworkInstallObserver.startWatching();
-            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
+            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                     scanMode | SCAN_NO_DEX);
             
             // Collect all system packages.
@@ -859,7 +860,8 @@
             mSystemInstallObserver = new AppDirObserver(
                 mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
             mSystemInstallObserver.startWatching();
-            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
+            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
             
             if (mInstaller != null) {
                 if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
@@ -2711,8 +2713,6 @@
         SharedUserSetting suid = null;
         PackageSetting pkgSetting = null;
 
-        boolean removeExisting = false;
-
         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             // Only system apps can use these features.
             pkg.mOriginalPackages = null;
@@ -2903,7 +2903,7 @@
 
             if (!verifySignaturesLP(pkgSetting, pkg, parseFlags,
                     (scanMode&SCAN_UPDATE_SIGNATURE) != 0)) {
-                if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) {
+                if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
                     return null;
                 }
@@ -2922,7 +2922,10 @@
                         return null;
                     }
                 }
-                removeExisting = true;
+                // File a report about this.
+                String msg = "System package " + pkg.packageName
+                        + " signature changed; retaining data.";
+                reportSettingsProblem(Log.WARN, msg);
             }
 
             // Verify that this new package doesn't have any content providers
@@ -2955,23 +2958,6 @@
 
         final String pkgName = pkg.packageName;
         
-        if (removeExisting) {
-            boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
-            if (mInstaller != null) {
-                int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
-                if (ret != 0) {
-                    String msg = "System package " + pkg.packageName
-                            + " could not have data directory erased after signature change.";
-                    reportSettingsProblem(Log.WARN, msg);
-                    mLastScanError = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
-                    return null;
-                }
-            }
-            Slog.w(TAG, "System package " + pkg.packageName
-                    + " signature changed: existing data removed.");
-            mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-        }
-
         if (pkg.mAdoptPermissions != null) {
             // This package wants to adopt ownership of permissions from
             // another package.
@@ -4500,7 +4486,8 @@
                 if ((event&ADD_EVENTS) != 0) {
                     if (p == null) {
                         p = scanPackageLI(fullPath,
-                                (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
+                                (mIsRom ? PackageParser.PARSE_IS_SYSTEM
+                                        | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
                                 PackageParser.PARSE_CHATTY |
                                 PackageParser.PARSE_MUST_BE_APK,
                                 SCAN_MONITOR | SCAN_NO_PATHS);
@@ -4701,23 +4688,22 @@
                             }
                             return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                         } else {
-                            // When replacing apps make sure we honour
-                            // the existing app location if not overwritten by other options
-                            boolean prevOnSd = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
                             if (onSd) {
                                 // Install flag overrides everything.
                                 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
                             }
-                            // If current upgrade does not specify install location.
+                            // If current upgrade specifies particular preference
                             if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                                 // Application explicitly specified internal.
                                 return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                             } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
                                 // App explictly prefers external. Let policy decide
-                            } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+                            } else {
                                 // Prefer previous location
-                                return prevOnSd ? PackageHelper.RECOMMEND_INSTALL_EXTERNAL:
-                                    PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+                                if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                                    return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+                                }
+                                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                             }
                         }
                     } else {
@@ -8294,6 +8280,7 @@
                         return;
                     }
                 } else {
+                    mSettingsFilename.delete();
                     Slog.w(TAG, "Preserving older settings backup");
                 }
             }
@@ -9566,6 +9553,8 @@
                    // Scan the package
                    if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
                        synchronized (mPackages) {
+                           updatePermissionsLP(pkg.packageName, pkg,
+                                   pkg.permissions.size() > 0, false);
                            retCode = PackageManager.INSTALL_SUCCEEDED;
                            pkgList.add(pkg.packageName);
                            // Post process args
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index f4bdd1f..124da4e 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -68,6 +68,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.service.wallpaper.ImageWallpaper;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
 import com.android.server.DevicePolicyManagerService.ActiveAdmin;
 import com.android.server.DevicePolicyManagerService.MyPackageMonitor;
 
@@ -804,6 +805,9 @@
                     }
 
                     res = r.openRawResource(resId);
+                    if (WALLPAPER_FILE.exists()) {
+                        WALLPAPER_FILE.delete();
+                    }
                     fos = new FileOutputStream(WALLPAPER_FILE);
 
                     byte[] buffer = new byte[32768];
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 90529e5..35b250e 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1897,6 +1897,7 @@
                     setWifiApEnabledBlocking(true,
                                              msg.arg1,
                                              (WifiConfiguration) msg.obj);
+                    sWakeLock.release();
                     break;
 
                 case MESSAGE_STOP_ACCESS_POINT:
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index cdf4e95..33bbc13 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -158,9 +158,7 @@
 
     public void noteInputEvent() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteInputEventLocked();
-        }
+        mStats.noteInputEventAtomic();
     }
     
     public void noteUserActivity(int uid, int event) {
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index d170b02..1b9e1c7 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -383,9 +383,13 @@
             File backupFile = null;
             if (mFile != null && mFile.exists()) {
                 backupFile = new File(mFile.getPath() + ".bak");
-                if (!mFile.renameTo(backupFile)) {
-                    Slog.w(TAG, "Failed to persist new stats");
-                    return;
+                if (!backupFile.exists()) {
+                    if (!mFile.renameTo(backupFile)) {
+                        Slog.w(TAG, "Failed to persist new stats");
+                        return;
+                    }
+                } else {
+                    mFile.delete();
                 }
             }
 
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index fcb39c9..166b6b6 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -365,6 +365,13 @@
             return;
         }
 
+        if (mTetheredNotification != null) {
+            if (mTetheredNotification.icon == icon) {
+                return;
+            }
+            notificationManager.cancel(mTetheredNotification.icon);
+        }
+
         Intent intent = new Intent();
         intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
         intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
@@ -613,10 +620,8 @@
         static final int CMD_STOP_TETHERING_ERROR        = 10;
         // notification from the master SM that it had trouble setting the DNS forwarders
         static final int CMD_SET_DNS_FORWARDERS_ERROR    = 11;
-        // a mechanism to transition self to another state from an enter function
-        static final int CMD_TRANSITION_TO_STATE         = 12;
         // the upstream connection has changed
-        static final int CMD_TETHER_CONNECTION_CHANGED   = 13;
+        static final int CMD_TETHER_CONNECTION_CHANGED   = 12;
 
         private HierarchicalState mDefaultState;
 
@@ -746,13 +751,14 @@
                                 TetherInterfaceSM.this);
                         setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
 
-                        sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE, mInitialState);
+                        transitionTo(mInitialState);
                         return;
                     }
                 }
                 sendTetherStateChangedBroadcast();
 
-                sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE, mTetheredState);
+                // Skipping StartingState
+                transitionTo(mTetheredState);
             }
             @Override
             public boolean processMessage(Message message) {
@@ -786,10 +792,6 @@
                                 TetherInterfaceSM.this);
                         transitionTo(mUnavailableState);
                         break;
-                   case CMD_TRANSITION_TO_STATE:
-                       HierarchicalState s = (HierarchicalState)(message.obj);
-                       transitionTo(s);
-                       break;
                     default:
                         retValue = false;
                 }
@@ -808,7 +810,7 @@
                 } catch (Exception e) {
                     setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
 
-                    sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE, mInitialState);
+                    transitionTo(mInitialState);
                     return;
                 }
                 if (mUsb) Tethering.this.enableUsbRndis(true);
@@ -945,10 +947,6 @@
                         }
                         transitionTo(mInitialState);
                         break;
-                    case CMD_TRANSITION_TO_STATE:
-                        HierarchicalState s = (HierarchicalState)(message.obj);
-                        transitionTo(s);
-                        break;
                     default:
                         retValue = false;
                         break;
@@ -996,10 +994,8 @@
         static final int CMD_UPSTREAM_CHANGED        = 3;
         // we received notice that the cellular DUN connection is up
         static final int CMD_CELL_CONNECTION_RENEW   = 4;
-        // need to do delayed transition from enter/exit
-        static final int CMD_TRANSITION_TO_STATE     = 5;
         // we don't have a valid upstream conn, check again after a delay
-        static final int CMD_RETRY_UPSTREAM          = 6;
+        static final int CMD_RETRY_UPSTREAM          = 5;
 
         // This indicates what a timeout event relates to.  A state that
         // sends itself a delayed timeout event and handles incoming timeout events
@@ -1103,21 +1099,19 @@
                 try {
                     service.setIpForwardingEnabled(true);
                 } catch (Exception e) {
-                    sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE,
-                            mSetIpForwardingEnabledErrorState);
+                    transitionTo(mSetIpForwardingEnabledErrorState);
                     return false;
                 }
                 try {
                     service.startTethering(mDhcpRange[0], mDhcpRange[1]);
                 } catch (Exception e) {
-                    sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE, mStartTetheringErrorState);
+                    transitionTo(mStartTetheringErrorState);
                     return false;
                 }
                 try {
                     service.setDnsForwarders(mDnsServers);
                 } catch (Exception e) {
-                    sendMessageAtFrontOfQueue(CMD_TRANSITION_TO_STATE,
-                            mSetDnsForwardersErrorState);
+                    transitionTo(mSetDnsForwardersErrorState);
                     return false;
                 }
                 return true;
@@ -1328,10 +1322,6 @@
                        chooseUpstreamType(mTryCell);
                        mTryCell = !mTryCell;
                        break;
-                   case CMD_TRANSITION_TO_STATE:
-                       HierarchicalState s = (HierarchicalState)(message.obj);
-                       transitionTo(s);
-                       break;
                    default:
                        retValue = false;
                        break;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 6c20204..95cb1c6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -20,6 +20,8 @@
 import android.util.Log;
 
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.gsm.ApnSetting;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 
 /**
@@ -71,12 +73,20 @@
         createTime = -1;
         lastFailTime = -1;
         lastFailCause = FailCause.NONE;
+        int dataProfile;
+        if ((cp.apn != null) && (cp.apn.types.length > 0) && (cp.apn.types[0] != null) &&
+                (cp.apn.types[0].equals(Phone.APN_TYPE_DUN))) {
+            if (DBG) log("CdmaDataConnection using DUN");
+            dataProfile = RILConstants.DATA_PROFILE_TETHERED;
+        } else {
+            dataProfile = RILConstants.DATA_PROFILE_DEFAULT;
+        }
 
         // msg.obj will be returned in AsyncResult.userObj;
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
         phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_CDMA),
-                Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), null, null,
+                Integer.toString(dataProfile), null, null,
                 null, Integer.toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP), msg);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index af9c652..2f801ccc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -48,6 +48,7 @@
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.gsm.ApnSetting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.ServiceStateTracker;
@@ -77,9 +78,6 @@
     /** Currently active CdmaDataConnection */
     private CdmaDataConnection mActiveDataConnection;
 
-    /** mimic of GSM's mActiveApn */
-    private boolean mIsApnActive = false;
-
     private boolean mPendingRestartRadio = false;
     private static final int TIME_DELAYED_TO_RESTART_RADIO =
             SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000);
@@ -108,6 +106,14 @@
             Phone.APN_TYPE_DUN,
             Phone.APN_TYPE_HIPRI };
 
+    private static final String[] mDefaultApnTypes = {
+            Phone.APN_TYPE_DEFAULT,
+            Phone.APN_TYPE_MMS,
+            Phone.APN_TYPE_HIPRI };
+
+    // if we have no active Apn this is null
+    protected ApnSetting mActiveApn;
+
     // Possibly promoate to base class, the only difference is
     // the INTENT_RECONNECT_ALARM action is a different string.
     // Do consider technology changes if it is promoted.
@@ -250,7 +256,7 @@
 
     @Override
     protected boolean isApnTypeActive(String type) {
-        return (mIsApnActive && isApnTypeAvailable(type));
+        return mActiveApn != null && mActiveApn.canHandleType(type);
     }
 
     @Override
@@ -265,10 +271,9 @@
 
     protected String[] getActiveApnTypes() {
         String[] result;
-        if (mIsApnActive) {
-            result = mSupportedApnTypes.clone();
+        if (mActiveApn != null) {
+            result = mActiveApn.types;
         } else {
-            // TODO - should this return an empty array?  See GSM too.
             result = new String[1];
             result[0] = Phone.APN_TYPE_DEFAULT;
         }
@@ -414,7 +419,6 @@
     }
 
     private boolean setupData(String reason) {
-
         CdmaDataConnection conn = findFreeDataConnection();
 
         if (conn == null) {
@@ -423,12 +427,19 @@
         }
 
         mActiveDataConnection = conn;
-        mIsApnActive = true;
+        String[] types;
+        if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
+            types = new String[1];
+            types[0] = Phone.APN_TYPE_DUN;
+        } else {
+            types = mDefaultApnTypes;
+        }
+        mActiveApn = new ApnSetting(0, "", "", "", "", "", "", "", "", "", "", 0, types);
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
         msg.obj = reason;
-        conn.connect(msg);
+        conn.connect(msg, mActiveApn);
 
         setState(State.INITING);
         phone.notifyDataConnection(reason);
@@ -627,7 +638,7 @@
         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
         setState(State.IDLE);
         phone.notifyDataConnection(reason);
-        mIsApnActive = false;
+        mActiveApn = null;
     }
 
     protected void onRecordsLoaded() {
@@ -649,8 +660,7 @@
      */
     @Override
     protected void onEnableNewApn() {
-        // for cdma we only use this when default data is enabled..
-        onTrySetupData(Phone.REASON_DATA_ENABLED);
+          cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
     }
 
     /**
@@ -763,7 +773,7 @@
         }
 
         phone.notifyDataConnection(reason);
-        mIsApnActive = false;
+        mActiveApn = null;
         if (retryAfterDisconnected(reason)) {
           trySetupData(reason);
       }
diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 8d807fd..4cbfc87 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -32,12 +32,12 @@
     String user;
     String password;
     int authType;
-    String[] types;
+    public String[] types;
     int id;
     String numeric;
 
 
-    ApnSetting(int id, String numeric, String carrier, String apn, String proxy, String port,
+    public ApnSetting(int id, String numeric, String carrier, String apn, String proxy, String port,
             String mmsc, String mmsProxy, String mmsPort,
             String user, String password, int authType, String[] types) {
         this.id = id;
@@ -73,7 +73,7 @@
         return sb.toString();
     }
 
-    boolean canHandleType(String type) {
+    public boolean canHandleType(String type) {
         for (String t : types) {
             // DEFAULT handles all, and HIPRI is handled by DEFAULT
             if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL) ||
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index ee6b89c..63d50c7 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -121,6 +121,10 @@
  * -e class com.android.foo.FooTest,com.android.foo.TooTest
  * com.android.foo/android.test.InstrumentationTestRunner
  * <p/>
+ * <b>Running all tests in a java package:</b> adb shell am instrument -w
+ * -e package com.android.foo.subpkg
+ *  com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
  * <b>Including performance tests:</b> adb shell am instrument -w
  * -e perf true
  * com.android.foo/android.test.InstrumentationTestRunner
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 009c0f2d93..dd7a169 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -86,8 +86,7 @@
         fail(errMsg);
     }
     void failStr(Exception e) {
-        Log.w(TAG, "e.getMessage="+e.getMessage());
-        Log.w(TAG, "e="+e);
+        failStr(e.getMessage());
     }
 
     @Override
@@ -297,6 +296,17 @@
     private static final int INSTALL_LOC_INT = 1;
     private static final int INSTALL_LOC_SD = 2;
     private static final int INSTALL_LOC_ERR = -1;
+    private int checkDefaultPolicy(long pkgLen) {
+        // Check for free memory internally
+        if (checkInt(pkgLen)) {
+            return INSTALL_LOC_INT;
+        }
+        // Check for free memory externally
+        if (checkSd(pkgLen)) {
+            return INSTALL_LOC_SD;
+        }
+        return INSTALL_LOC_ERR;
+    }
     private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
         // Flags explicitly over ride everything else.
         if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) {
@@ -324,15 +334,7 @@
             return INSTALL_LOC_ERR;
         }
         if (expInstallLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
-            // Check for free memory internally
-            if (checkInt(pkgLen)) {
-                return INSTALL_LOC_INT;
-            }
-            // Check for free memory externally
-            if (checkSd(pkgLen)) {
-                return INSTALL_LOC_SD;
-            }
-            return INSTALL_LOC_ERR;
+            return checkDefaultPolicy(pkgLen);
         }
         // Check for settings preference.
         boolean checkSd = false;
@@ -359,19 +361,9 @@
                     return INSTALL_LOC_SD;
                 }
                 return INSTALL_LOC_ERR;
-            } else if (userPref == APP_INSTALL_AUTO) {
-                if (checkInt(pkgLen)) {
-                    return INSTALL_LOC_INT;
-                }
-                // Check for free memory externally
-                if (checkSd(pkgLen)) {
-                    return INSTALL_LOC_SD;
-                }
-                return INSTALL_LOC_ERR;
-                
             }
-        } 
-        return INSTALL_LOC_ERR;
+        }
+        return checkDefaultPolicy(pkgLen);
     }
     
     private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) {
@@ -434,7 +426,7 @@
 
     private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) {
         return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
-                false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+                false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
     static final String PERM_PACKAGE = "package";
@@ -1114,7 +1106,7 @@
 
     public void testManifestInstallLocationUnspecified() {
         installFromRawResource("install.apk", R.raw.install_loc_unspecified,
-                0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
     public void testManifestInstallLocationFwdLockedFlagSdcard() {
@@ -1122,7 +1114,7 @@
                 PackageManager.INSTALL_FORWARD_LOCK |
                 PackageManager.INSTALL_EXTERNAL, true, true,
                 PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
-                PackageInfo.INSTALL_LOCATION_AUTO);
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
     public void testManifestInstallLocationFwdLockedSdcard() {
@@ -1178,12 +1170,12 @@
 
     public void testManifestInstallLocationReplaceInternalSdcard() {
         int iFlags = 0;
-        int iApk = R.raw.install_loc_unspecified;
+        int iApk = R.raw.install_loc_internal;
         int rFlags = 0;
         int rApk = R.raw.install_loc_sdcard;
         InstallParams ip = installFromRawResource("install.apk", iApk,
                 iFlags, false,
-                false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+                false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
         GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
         int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
         try {
@@ -1855,21 +1847,28 @@
     * The following set of tests check install location for existing
     * application based on user setting.
     */
-   private void setExistingXUserX(int userSetting, int iFlags) {
+   private int getExpectedInstallLocation(int userSetting) {
+       int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+       boolean enable = getUserSettingSetInstallLocation();
+       if (enable) {
+           if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
+               iloc = PackageInfo.INSTALL_LOCATION_AUTO;
+           } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
+               iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
+           } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
+               iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+           }
+       }
+       return iloc;
+   }
+   private void setExistingXUserX(int userSetting, int iFlags, int iloc) {
        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
        // First install.
        installFromRawResource("install.apk", R.raw.install,
                iFlags,
                false,
                false, -1,
-               -1);
-       // Watch out for this.
-       int iloc = PackageInfo.INSTALL_LOCATION_AUTO;
-       if ((iFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-           iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
-       } else if ((iFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-           iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
-       }
+               PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
        int origSetting = getInstallLoc();
        try {
            // Set user setting
@@ -1887,49 +1886,56 @@
    public void testExistingIUserI() {
        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
        int iFlags = PackageManager.INSTALL_INTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
    }
    public void testExistingIUserE() {
        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
        int iFlags = PackageManager.INSTALL_INTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
    }
    public void testExistingIUserA() {
        int userSetting = PackageHelper.APP_INSTALL_AUTO;
        int iFlags = PackageManager.INSTALL_INTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
    }
    public void testExistingEUserI() {
        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
        int iFlags = PackageManager.INSTALL_EXTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
    }
    public void testExistingEUserE() {
        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
        int iFlags = PackageManager.INSTALL_EXTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
    }
    public void testExistingEUserA() {
        int userSetting = PackageHelper.APP_INSTALL_AUTO;
        int iFlags = PackageManager.INSTALL_EXTERNAL;
-       setExistingXUserX(userSetting, iFlags);
+       setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
    }
    /*
     * The following set of tests verify that the user setting defines
     * the install location.
     * 
     */
-   private void setUserX(int userSetting) {
-       int origSetting = getInstallLoc();
-       int iloc = PackageInfo.INSTALL_LOCATION_AUTO;
-       if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
-           iloc = PackageInfo.INSTALL_LOCATION_AUTO;
-       } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
-           iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
-       } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
-           iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
-       }
+   private boolean getUserSettingSetInstallLocation() {
        try {
+           return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION) != 0;
+           
+       } catch (SettingNotFoundException e1) {
+       }
+       return false;
+   }
+
+   private void setUserSettingSetInstallLocation(boolean value) {
+       Settings.System.putInt(mContext.getContentResolver(),
+               Settings.System.SET_INSTALL_LOCATION, value ? 1 : 0);
+   }
+   private void setUserX(boolean enable, int userSetting, int iloc) {
+       boolean origUserSetting = getUserSettingSetInstallLocation();
+       int origSetting = getInstallLoc();
+       try {
+           setUserSettingSetInstallLocation(enable);
            // Set user setting
            setInstallLoc(userSetting);
            // Replace now
@@ -1939,20 +1945,44 @@
                    false, -1,
                    iloc);
        } finally {
+           // Restore original setting
+           setUserSettingSetInstallLocation(origUserSetting);
            setInstallLoc(origSetting);
        }
    }
    public void testUserI() {
        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-       setUserX(userSetting);
+       int iloc = getExpectedInstallLocation(userSetting);
+       setUserX(true, userSetting, iloc);
    }
    public void testUserE() {
        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-       setUserX(userSetting);
+       int iloc = getExpectedInstallLocation(userSetting);
+       setUserX(true, userSetting, iloc);
    }
    public void testUserA() {
        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-       setUserX(userSetting);
+       int iloc = getExpectedInstallLocation(userSetting);
+       setUserX(true, userSetting, iloc);
+   }
+   /*
+    * The following set of tests turn on/off the basic
+    * user setting for turning on install location.
+    */
+   public void testUserPrefOffUserI() {
+       int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+       int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+       setUserX(false, userSetting, iloc);
+   }
+   public void testUserPrefOffUserE() {
+       int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+       int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+       setUserX(false, userSetting, iloc);
+   }
+   public void testUserPrefOffA() {
+       int userSetting = PackageHelper.APP_INSTALL_AUTO;
+       int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+       setUserX(false, userSetting, iloc);
    }
    
     static final String BASE_PERMISSIONS_DEFINED[] = new String[] {
@@ -2126,6 +2156,33 @@
         }
     }
 
+    /*
+     * Ensure that permissions are properly declared.
+     */
+    public void testInstallOnSdPermissionsUnmount() {
+        InstallParams ip = null;
+        boolean origMediaState = getMediaState();
+        try {
+            // **: Upon installing a package, are its declared permissions published?
+            int iFlags = PackageManager.INSTALL_INTERNAL;
+            int iApk = R.raw.install_decl_perm;
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            // Unmount media here
+            assertTrue(unmountMedia());
+            // Mount media again
+            mountMedia();
+            //Check permissions now
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+        } finally {
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+        }
+    }
     /*---------- Recommended install location tests ----*/
     /*
      * TODO's
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index b668bd0..d51d17a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -91,7 +91,6 @@
         ignoreResultList.add("http/tests/appcache/disabled.html"); // not found
         ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
         ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
-        ignoreResultList.add("http/tests/appcache/local-content.html"); // text diff
         ignoreResultList.add("http/tests/appcache/max-size.html"); // no layoutTestController.setAppCacheMaximumSize
         ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
         ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 87f2420..a8ac2ec 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -611,7 +611,8 @@
     const String16 attr(attr8);
     
     if (node->getAttribute(ns, attr) != NULL) {
-        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n",
+        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
+                        " using existing value in manifest.\n",
                 String8(attr).string(), String8(ns).string());
         return;
     }