Merge "Advance encodedKeySearchIndex if encodedKey matches a suffix of a parameter."
diff --git a/api/current.xml b/api/current.xml
index e982dfcb..58ce3ee 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -11732,6 +11732,17 @@
  visibility="public"
 >
 </field>
+<field name="custom"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16908331"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="cut"
  type="int"
  transient="false"
@@ -26862,11 +26873,22 @@
  visibility="public"
 >
 </field>
+<field name="PASSWORD_QUALITY_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PASSWORD_QUALITY_ALPHANUMERIC"
  type="int"
  transient="false"
  volatile="false"
- value="196608"
+ value="327680"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -80529,6 +80551,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"
@@ -80636,6 +80669,19 @@
 <parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener">
 </parameter>
 </method>
+<method name="registerMediaButtonEventReceiver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventReceiver" type="android.content.ComponentName">
+</parameter>
+</method>
 <method name="requestAudioFocus"
  return="int"
  abstract="false"
@@ -80849,6 +80895,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"
@@ -80873,6 +80941,19 @@
 <parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener">
 </parameter>
 </method>
+<method name="unregisterMediaButtonEventReceiver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventReceiver" type="android.content.ComponentName">
+</parameter>
+</method>
 <field name="ACTION_AUDIO_BECOMING_NOISY"
  type="java.lang.String"
  transient="false"
@@ -80884,6 +80965,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"
@@ -80939,6 +81031,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"
@@ -80961,6 +81064,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"
@@ -80994,6 +81108,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"
@@ -81357,6 +81482,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"
@@ -121973,7 +122131,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="message" type="java.lang.String">
+<parameter name="text" type="java.lang.String">
 </parameter>
 </method>
 </class>
@@ -193614,7 +193772,7 @@
  visibility="public"
 >
 </method>
-<method name="getUseSystemOverscrollBackground"
+<method name="getUseWebViewBackgroundForOverscrollBackground"
  return="boolean"
  abstract="false"
  native="false"
@@ -194204,7 +194362,7 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
-<method name="setUseSystemOverscrollBackground"
+<method name="setUseWebViewBackgroundForOverscrollBackground"
  return="void"
  abstract="false"
  native="false"
@@ -194214,7 +194372,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="system" type="boolean">
+<parameter name="view" type="boolean">
 </parameter>
 </method>
 <method name="setUseWideViewPort"
@@ -199599,6 +199757,8 @@
 >
 <implements name="android.widget.ExpandableListAdapter">
 </implements>
+<implements name="android.widget.HeterogeneousExpandableList">
+</implements>
 <constructor name="BaseExpandableListAdapter"
  type="android.widget.BaseExpandableListAdapter"
  static="false"
@@ -199618,6 +199778,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"
@@ -199646,6 +199832,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"
@@ -203388,6 +203598,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/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index e9b21f1..e8b5eaf 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -105,7 +105,7 @@
 
 #ifdef FWDUMP_bcm4329
     run_command("DUMP WIFI FIRMWARE LOG", 60,
-            "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
+            "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
 #endif
 
     print_properties();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index cfc2e75..659d70f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,11 +92,6 @@
             return;
         }
 
-        if ("mountsd".equals(op)) {
-            runMountSd();
-            return;
-        }
-
         if ("uninstall".equals(op)) {
             runUninstall();
             return;
@@ -646,37 +641,6 @@
         }
     }
 
-    private void runMountSd() {
-        String opt;
-        boolean mount = false;
-        while ((opt=nextOption()) != null) {
-            if (opt.equals("-m")) {
-                String mountStr = nextOptionData();
-                if (mountStr == null) {
-                    System.err.println("Error: no value specified for -m");
-                    showUsage();
-                    return;
-                }
-                if ("true".equalsIgnoreCase(mountStr)) {
-                    mount = true;
-                } else if ("false".equalsIgnoreCase(mountStr)) {
-                    mount = false;
-                } else {
-                    System.err.println("Error: no value specified for -m");
-                    showUsage();
-                    return;
-                }
-            }
-        }
-
-        try {
-            mPm.updateExternalMediaStatus(mount);
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-        }
-    }
-
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -866,7 +830,6 @@
         System.err.println("       pm path PACKAGE");
         System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
-        System.err.println("       pm mountsd [-m true/false]");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("");
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/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6d5686a..596ca9d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -980,10 +980,11 @@
             return true;
         }
 
-        case KILL_PIDS_FOR_MEMORY_TRANSACTION: {
+        case KILL_PIDS_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int[] pids = data.createIntArray();
-            boolean res = killPidsForMemory(pids);
+            String reason = data.readString();
+            boolean res = killPids(pids, reason);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -2393,12 +2394,13 @@
         mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
         data.recycle();
     }
-    public boolean killPidsForMemory(int[] pids) throws RemoteException {
+    public boolean killPids(int[] pids, String reason) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeIntArray(pids);
-        mRemote.transact(KILL_PIDS_FOR_MEMORY_TRANSACTION, data, reply, 0);
+        data.writeString(reason);
+        mRemote.transact(KILL_PIDS_TRANSACTION, data, reply, 0);
         boolean res = reply.readInt() != 0;
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1c980e3..fa6abec 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3908,16 +3908,16 @@
         }
     }
 
-    final void applyConfigurationToResourcesLocked(Configuration config) {
+    final boolean applyConfigurationToResourcesLocked(Configuration config) {
         if (mResConfiguration == null) {
             mResConfiguration = new Configuration();
         }
         if (!mResConfiguration.isOtherSeqNewer(config)) {
             if (DEBUG_CONFIGURATION) Log.v(TAG, "Skipping new config: curSeq="
                     + mResConfiguration.seq + ", newSeq=" + config.seq);
-            return;
+            return false;
         }
-        mResConfiguration.updateFrom(config);
+        int changes = mResConfiguration.updateFrom(config);
         DisplayMetrics dm = getDisplayMetricsLocked(true);
 
         // set it for java, this also affects newly created Resources
@@ -3948,6 +3948,8 @@
                 it.remove();
             }
         }
+        
+        return changes != 0;
     }
     
     final void handleConfigurationChanged(Configuration config) {
@@ -4522,17 +4524,20 @@
         ViewRoot.addConfigCallback(new ComponentCallbacks() {
             public void onConfigurationChanged(Configuration newConfig) {
                 synchronized (mPackages) {
-                    if (mPendingConfiguration == null ||
-                            mPendingConfiguration.isOtherSeqNewer(newConfig)) {
-                        mPendingConfiguration = newConfig;
-                        
-                        // We need to apply this change to the resources
-                        // immediately, because upon returning the view
-                        // hierarchy will be informed about it.
-                        applyConfigurationToResourcesLocked(newConfig);
+                    // We need to apply this change to the resources
+                    // immediately, because upon returning the view
+                    // hierarchy will be informed about it.
+                    if (applyConfigurationToResourcesLocked(newConfig)) {
+                        // This actually changed the resources!  Tell
+                        // everyone about it.
+                        if (mPendingConfiguration == null ||
+                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {
+                            mPendingConfiguration = newConfig;
+                            
+                            queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
+                        }
                     }
                 }
-                queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
             }
             public void onLowMemory() {
             }
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 2603579..2714de5 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -35,12 +35,12 @@
 /**
  * A subclass of Dialog that can display one, two or three buttons. If you only want to
  * display a String in this dialog box, use the setMessage() method.  If you
- * want to display a more complex view, look up the FrameLayout called "body"
+ * want to display a more complex view, look up the FrameLayout called "custom"
  * and add your view to it:
  *
  * <pre>
- * FrameLayout fl = (FrameLayout) findViewById(R.id.body);
- * fl.add(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ * FrameLayout fl = (FrameLayout) findViewById(android.R.id.custom);
+ * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
  * </pre>
  * 
  * <p>The AlertDialog class takes care of automatically setting
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/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 14571de..30feae1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -241,7 +241,7 @@
     
     public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
     
-    public boolean killPidsForMemory(int[] pids) throws RemoteException;
+    public boolean killPids(int[] pids, String reason) throws RemoteException;
     
     public void reportPss(IApplicationThread caller, int pss) throws RemoteException;
     
@@ -476,7 +476,7 @@
     int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76;
     int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77;
     int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
-    int KILL_PIDS_FOR_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
+    int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
     int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
     int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
     int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2d9b415..35e7ee6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,7 +20,6 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.app.admin.IDevicePolicyManager.Stub;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -192,11 +191,19 @@
     
     /**
      * Constant for {@link #setPasswordQuality}: the user must have entered a
+     * password containing at least alphabetic (or other symbol) characters.
+     * Note that quality constants are ordered so that higher values are more
+     * restrictive.
+     */
+    public static final int PASSWORD_QUALITY_ALPHABETIC = 0x40000;
+    
+    /**
+     * Constant for {@link #setPasswordQuality}: the user must have entered a
      * password containing at least <em>both></em> numeric <em>and</em>
-     * alphabeter (or other symbol) characters.  Note that quality constants are
+     * alphabetic (or other symbol) characters.  Note that quality constants are
      * ordered so that higher values are more restrictive.
      */
-    public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x30000;
+    public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000;
     
     /**
      * Called by an application that is administering the device to set the
@@ -219,7 +226,8 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param quality The new desired quality.  One of
      * {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING},
-     * {@link #PASSWORD_QUALITY_NUMERIC}, or {@link #PASSWORD_QUALITY_ALPHANUMERIC}.
+     * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
+     * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}.
      */
     public void setPasswordQuality(ComponentName admin, int quality) {
         if (mService != null) {
@@ -257,7 +265,8 @@
      * take place immediately.  To prompt the user for a new password, use
      * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.  This
      * constraint is only imposed if the administrator has also requested either
-     * {@link #PASSWORD_QUALITY_NUMERIC} or {@link #PASSWORD_QUALITY_ALPHANUMERIC}
+     * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
+     * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}
      * with {@link #setPasswordQuality}.
      * 
      * <p>The calling device admin must have requested
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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 399a87d..c638d04 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -309,7 +309,7 @@
      * MountService uses this to call into the package manager to update
      * status of sdcard.
      */
-    boolean updateExternalMediaStatus(boolean mounted);
+    void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
 
     String nextPackageToClean(String lastPackage);
 
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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bbf4ca1..0318b6c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -233,7 +233,7 @@
     /**
      * Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
      * indicate that this package should be installed as forward locked, i.e. only the app itself
-     * should have access to it's code and non-resource assets.
+     * should have access to its code and non-resource assets.
      * @hide
      */
     public static final int INSTALL_FORWARD_LOCK = 0x00000001;
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/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 7e91159..722d707 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -185,6 +185,7 @@
                 mCursors[i].deactivate();
             }
         }
+        super.deactivate();
     }
 
     @Override
@@ -194,6 +195,7 @@
             if (mCursors[i] == null) continue;
             mCursors[i].close();
         }
+        super.close();
     }
 
     @Override
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/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index b4c04b1..96fbe77 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -26,8 +26,9 @@
  * <p>This uses the World Magnetic Model produced by the United States National
  * Geospatial-Intelligence Agency.  More details about the model can be found at
  * <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml</a>.
- * This class currently uses WMM-2005 which is valid until 2010, but should
- * produce acceptable results for several years after that.
+ * This class currently uses WMM-2010 which is valid until 2015, but should
+ * produce acceptable results for several years after that. Future versions of
+ * Android may use a newer version of the model.
  */
 public class GeomagneticField {
     // The magnetic field at a given point, in nonoteslas in geodetic
@@ -43,75 +44,73 @@
 
     // Constants from WGS84 (the coordinate system used by GPS)
     static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f;
-    static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f;
+    static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523142f;
     static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f;
 
     // These coefficients and the formulae used below are from:
-    // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010
+    // NOAA Technical Report: The US/UK World Magnetic Model for 2010-2015
     static private final float[][] G_COEFF = new float[][] {
-        { 0f },
-        { -29556.8f, -1671.7f },
-        { -2340.6f, 3046.9f, 1657.0f },
-        { 1335.4f, -2305.1f, 1246.7f, 674.0f },
-        { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f },
-        { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f },
-        { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f },
-        { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f },
-        { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f },
-        { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f },
-        { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f },
-        { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f },
-        { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f,
-          -0.3f, -0.1f } };
+        { 0.0f },
+        { -29496.6f, -1586.3f },
+        { -2396.6f, 3026.1f, 1668.6f },
+        { 1340.1f, -2326.2f, 1231.9f, 634.0f },
+        { 912.6f, 808.9f, 166.7f, -357.1f, 89.4f },
+        { -230.9f, 357.2f, 200.3f, -141.1f, -163.0f, -7.8f },
+        { 72.8f, 68.6f, 76.0f, -141.4f, -22.8f, 13.2f, -77.9f },
+        { 80.5f, -75.1f, -4.7f, 45.3f, 13.9f, 10.4f, 1.7f, 4.9f },
+        { 24.4f, 8.1f, -14.5f, -5.6f, -19.3f, 11.5f, 10.9f, -14.1f, -3.7f },
+        { 5.4f, 9.4f, 3.4f, -5.2f, 3.1f, -12.4f, -0.7f, 8.4f, -8.5f, -10.1f },
+        { -2.0f, -6.3f, 0.9f, -1.1f, -0.2f, 2.5f, -0.3f, 2.2f, 3.1f, -1.0f, -2.8f },
+        { 3.0f, -1.5f, -2.1f, 1.7f, -0.5f, 0.5f, -0.8f, 0.4f, 1.8f, 0.1f, 0.7f, 3.8f },
+        { -2.2f, -0.2f, 0.3f, 1.0f, -0.6f, 0.9f, -0.1f, 0.5f, -0.4f, -0.4f, 0.2f, -0.8f, 0.0f } };
 
     static private final float[][] H_COEFF = new float[][] {
-        { 0f },
-        { 0.0f, 5079.8f },
-        { 0.0f, -2594.7f, -516.7f },
-        { 0.0f, -199.9f, 269.3f, -524.2f },
-        { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f },
-        { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f },
-        { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f },
-        { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f },
-        { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f },
-        { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f },
-        { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f },
-        { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f },
-        { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f,
-          0.8f } };
+        { 0.0f },
+        { 0.0f, 4944.4f },
+        { 0.0f, -2707.7f, -576.1f },
+        { 0.0f, -160.2f, 251.9f, -536.6f },
+        { 0.0f, 286.4f, -211.2f, 164.3f, -309.1f },
+        { 0.0f, 44.6f, 188.9f, -118.2f, 0.0f, 100.9f },
+        { 0.0f, -20.8f, 44.1f, 61.5f, -66.3f, 3.1f, 55.0f },
+        { 0.0f, -57.9f, -21.1f, 6.5f, 24.9f, 7.0f, -27.7f, -3.3f },
+        { 0.0f, 11.0f, -20.0f, 11.9f, -17.4f, 16.7f, 7.0f, -10.8f, 1.7f },
+        { 0.0f, -20.5f, 11.5f, 12.8f, -7.2f, -7.4f, 8.0f, 2.1f, -6.1f, 7.0f },
+        { 0.0f, 2.8f, -0.1f, 4.7f, 4.4f, -7.2f, -1.0f, -3.9f, -2.0f, -2.0f, -8.3f },
+        { 0.0f, 0.2f, 1.7f, -0.6f, -1.8f, 0.9f, -0.4f, -2.5f, -1.3f, -2.1f, -1.9f, -1.8f },
+        { 0.0f, -0.9f, 0.3f, 2.1f, -2.5f, 0.5f, 0.6f, 0.0f, 0.1f, 0.3f, -0.9f, -0.2f, 0.9f } };
 
     static private final float[][] DELTA_G = new float[][] {
-        { 0f },
-        { 8.0f, 10.6f },
-        { -15.1f, -7.8f, -0.8f },
-        { 0.4f, -2.6f, -1.2f, -6.5f },
-        { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f },
-        { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f },
-        { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f },
-        { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f },
-        { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+        { 0.0f },
+        { 11.6f, 16.5f },
+        { -12.1f, -4.4f, 1.9f },
+        { 0.4f, -4.1f, -2.9f, -7.7f },
+        { -1.8f, 2.3f, -8.7f, 4.6f, -2.1f },
+        { -1.0f, 0.6f, -1.8f, -1.0f, 0.9f, 1.0f },
+        { -0.2f, -0.2f, -0.1f, 2.0f, -1.7f, -0.3f, 1.7f },
+        { 0.1f, -0.1f, -0.6f, 1.3f, 0.4f, 0.3f, -0.7f, 0.6f },
+        { -0.1f, 0.1f, -0.6f, 0.2f, -0.2f, 0.3f, 0.3f, -0.6f, 0.2f },
+        { 0.0f, -0.1f, 0.0f, 0.3f, -0.4f, -0.3f, 0.1f, -0.1f, -0.4f, -0.2f },
+        { 0.0f, 0.0f, -0.1f, 0.2f, 0.0f, -0.1f, -0.2f, 0.0f, -0.1f, -0.2f, -0.2f },
+        { 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f, 0.0f },
+        { 0.0f, 0.0f, 0.1f, 0.1f, -0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f, 0.1f } };
 
     static private final float[][] DELTA_H = new float[][] {
-        { 0f },
-        { 0.0f, -20.9f },
-        { 0.0f, -23.2f, -14.6f },
-        { 0.0f, 5.0f, -7.0f, -0.6f },
-        { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f },
-        { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f },
-        { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f },
-        { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f },
-        { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
-        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+        { 0.0f },
+        { 0.0f, -25.9f },
+        { 0.0f, -22.5f, -11.8f },
+        { 0.0f, 7.3f, -3.9f, -2.6f },
+        { 0.0f, 1.1f, 2.7f, 3.9f, -0.8f },
+        { 0.0f, 0.4f, 1.8f, 1.2f, 4.0f, -0.6f },
+        { 0.0f, -0.2f, -2.1f, -0.4f, -0.6f, 0.5f, 0.9f },
+        { 0.0f, 0.7f, 0.3f, -0.1f, -0.1f, -0.8f, -0.3f, 0.3f },
+        { 0.0f, -0.1f, 0.2f, 0.4f, 0.4f, 0.1f, -0.1f, 0.4f, 0.3f },
+        { 0.0f, 0.0f, -0.2f, 0.0f, -0.1f, 0.1f, 0.0f, -0.2f, 0.3f, 0.2f },
+        { 0.0f, 0.1f, -0.1f, 0.0f, -0.1f, -0.1f, 0.0f, -0.1f, -0.2f, 0.0f, -0.1f },
+        { 0.0f, 0.0f, 0.1f, 0.0f, 0.1f, 0.0f, 0.1f, 0.0f, -0.1f, -0.1f, 0.0f, -0.1f },
+        { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
 
     static private final long BASE_TIME =
-        new GregorianCalendar(2005, 1, 1).getTimeInMillis();
+        new GregorianCalendar(2010, 1, 1).getTimeInMillis();
 
     // The ratio between the Gauss-normalized associated Legendre functions and
     // the Schmid quasi-normalized ones. Compute these once staticly since they
@@ -191,7 +190,7 @@
         // We now compute the magnetic field strength given the geocentric
         // location. The magnetic field is the derivative of the potential
         // function defined by the model. See NOAA Technical Report: The US/UK
-        // World Magnetic Model for 2005-2010 for the derivation.
+        // World Magnetic Model for 2010-2015 for the derivation.
         float gcX = 0.0f;  // Geocentric northwards component.
         float gcY = 0.0f;  // Geocentric eastwards component.
         float gcZ = 0.0f;  // Geocentric downwards component.
@@ -406,4 +405,4 @@
         }
         return schmidtQuasiNorm;
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9201e3b..4b48409 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -175,6 +175,7 @@
     private boolean mShowTouchPoints = true;
     private int mPopupPreviewX;
     private int mPopupPreviewY;
+    private int mWindowY;
 
     private int mLastX;
     private int mLastY;
@@ -909,20 +910,36 @@
             getLocationInWindow(mOffsetInWindow);
             mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
             mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+            int[] mWindowLocation = new int[2];
+            getLocationOnScreen(mWindowLocation);
+            mWindowY = mWindowLocation[1];
         }
         // Set the preview background state
         mPreviewText.getBackground().setState(
                 key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+        mPopupPreviewX += mOffsetInWindow[0];
+        mPopupPreviewY += mOffsetInWindow[1];
+
+        // If the popup cannot be shown above the key, put it on the side
+        if (mPopupPreviewY + mWindowY < 0) {
+            // If the key you're pressing is on the left side of the keyboard, show the popup on
+            // the right, offset by enough to see at least one key to the left/right.
+            if (key.x + key.width <= getWidth() / 2) {
+                mPopupPreviewX += (int) (key.width * 2.5);
+            } else {
+                mPopupPreviewX -= (int) (key.width * 2.5);
+            }
+            mPopupPreviewY += popupHeight;
+        }
+
         if (previewPopup.isShowing()) {
-            previewPopup.update(mPopupPreviewX + mOffsetInWindow[0],
-                    mPopupPreviewY + mOffsetInWindow[1], 
+            previewPopup.update(mPopupPreviewX, mPopupPreviewY,
                     popupWidth, popupHeight);
         } else {
             previewPopup.setWidth(popupWidth);
             previewPopup.setHeight(popupHeight);
             previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, 
-                    mPopupPreviewX + mOffsetInWindow[0], 
-                    mPopupPreviewY + mOffsetInWindow[1]);
+                    mPopupPreviewX, mPopupPreviewY);
         }
         mPreviewText.setVisibility(VISIBLE);
     }
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 22e4325..98f32b3 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -369,7 +369,11 @@
                 }
                 // else fall through
             case Phone.APN_TYPE_NOT_AVAILABLE:
-                mEnabled = false;
+                // Default is always available, but may be off due to
+                // AirplaneMode or E-Call or whatever..
+                if (mApnType != Phone.APN_TYPE_DEFAULT) {
+                    mEnabled = false;
+                }
                 break;
             default:
                 Log.e(TAG, "Error in reconnect - unexpected response.");
diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java
index b8e17da..43fb5f1 100644
--- a/core/java/android/net/http/Connection.java
+++ b/core/java/android/net/http/Connection.java
@@ -222,6 +222,12 @@
                         }
                     }
 
+                    /* we have a connection, let the event handler
+                     * know of any associated certificate,
+                     * potentially none.
+                     */
+                    req.mEventHandler.certificate(mCertificate);
+
                     try {
                         /* FIXME: don't increment failure count if old
                            connection?  There should not be a penalty for
diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java
index a035c19..2aa05eb 100644
--- a/core/java/android/net/http/EventHandler.java
+++ b/core/java/android/net/http/EventHandler.java
@@ -125,8 +125,8 @@
     public void endData();
 
     /**
-     * SSL certificate callback called every time a resource is
-     * loaded via a secure connection
+     * SSL certificate callback called before resource request is
+     * made, which will be null for insecure connection.
      */
     public void certificate(SslCertificate certificate);
 
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index a67fd9a..e512a1df 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -308,12 +308,6 @@
         SslError error = CertificateChainValidator.getInstance().
             doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName());
 
-        EventHandler eventHandler = req.getEventHandler();
-
-        // Update the certificate info (to be consistent, it is better to do it
-        // here, before we start handling SSL errors, if any)
-        eventHandler.certificate(mCertificate);
-
         // Inform the user if there is a problem
         if (error != null) {
             // handleSslErrorRequest may immediately unsuspend if it wants to
@@ -325,7 +319,7 @@
                 mSuspended = true;
             }
             // don't hold the lock while calling out to the event handler
-            boolean canHandle = eventHandler.handleSslErrorRequest(error);
+            boolean canHandle = req.getEventHandler().handleSslErrorRequest(error);
             if(!canHandle) {
                 throw new IOException("failed to handle "+ error);
             }
diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java
index 1b6568e..6dbf325 100644
--- a/core/java/android/net/http/Request.java
+++ b/core/java/android/net/http/Request.java
@@ -82,6 +82,9 @@
     /* Used to synchronize waitUntilComplete() requests */
     private final Object mClientResource = new Object();
 
+    /** True if loading should be paused **/
+    private boolean mLoadingPaused = false;
+
     /**
      * Processor used to set content-length and transfer-encoding
      * headers.
@@ -133,6 +136,18 @@
     }
 
     /**
+     * @param pause True if the load should be paused.
+     */
+    synchronized void setLoadingPaused(boolean pause) {
+        mLoadingPaused = pause;
+
+        // Wake up the paused thread if we're unpausing the load.
+        if (!mLoadingPaused) {
+            notify();
+        }
+    }
+
+    /**
      * @param connection Request served by this connection
      */
     void setConnection(Connection connection) {
@@ -271,7 +286,24 @@
                 int len = 0;
                 int lowWater = buf.length / 2;
                 while (len != -1) {
+                    synchronized(this) {
+                        while (mLoadingPaused) {
+                            // Put this (network loading) thread to sleep if WebCore
+                            // has asked us to. This can happen with plugins for
+                            // example, if we are streaming data but the plugin has
+                            // filled its internal buffers.
+                            try {
+                                wait();
+                            } catch (InterruptedException e) {
+                                HttpLog.e("Interrupted exception whilst "
+                                    + "network thread paused at WebCore's request."
+                                    + " " + e.getMessage());
+                            }
+                        }
+                    }
+
                     len = nis.read(buf, count, buf.length - count);
+
                     if (len != -1) {
                         count += len;
                     }
@@ -316,10 +348,16 @@
      *
      * Called by RequestHandle from non-network thread
      */
-    void cancel() {
+    synchronized void cancel() {
         if (HttpLog.LOGV) {
             HttpLog.v("Request.cancel(): " + getUri());
         }
+
+        // Ensure that the network thread is not blocked by a hanging request from WebCore to
+        // pause the load.
+        mLoadingPaused = false;
+        notify();
+
         mCancelled = true;
         if (mConnection != null) {
             mConnection.cancel();
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 77cd544..1a3a289 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -101,6 +101,16 @@
     }
 
     /**
+     * Pauses the loading of this request. For example, called from the WebCore thread
+     * when the plugin can take no more data.
+     */
+    public void pauseRequest(boolean pause) {
+        if (mRequest != null) {
+            mRequest.setLoadingPaused(pause);
+        }
+    }
+
+    /**
      * Handles SSL error(s) on the way down from the user (the user
      * has already provided their feedback).
      */
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/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index afe4191..1254782 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -167,11 +167,15 @@
     /**
      * Start Wifi Access Point
      */
-    void startAccessPoint(in WifiConfiguration wifiConfig, String intf);
+    void startAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface);
 
     /**
      * Stop Wifi Access Point
      */
     void stopAccessPoint();
 
+    /**
+     * Set Access Point config
+     */
+    void setAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e4eaf45b..f4ca8bc 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -361,6 +361,9 @@
      */
     public WakeLock newWakeLock(int flags, String tag)
     {
+        if (tag == null) {
+            throw new NullPointerException("tag is null in PowerManager.newWakeLock");
+        }
         return new WakeLock(flags, tag);
     }
 
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index 75455ab..4862f80 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -84,7 +84,7 @@
     int[] getStorageUsers(String path);
 
     /**
-     * Gets the state of an volume via it's mountpoint.
+     * Gets the state of a volume via its mountpoint.
      */
     String getVolumeState(String mountPoint);
 
@@ -146,4 +146,10 @@
      * Invokes call back once the shutdown is complete.
      */
     void shutdown(IMountShutdownObserver observer);
+
+    /**
+     * Call into MountService by PackageManager to notify that its done
+     * processing the media status update request.
+     */
+    void finishMediaUpdate();
 }
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 61012c9..e40a7ba 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -1055,6 +1055,8 @@
     public Uri pushIntoContentResolver(ContentResolver resolver) {
         ArrayList<ContentProviderOperation> operationList =
             new ArrayList<ContentProviderOperation>();
+        // After applying the batch the first result's Uri is returned so it is important that
+        // the RawContact is the first operation that gets inserted into the list
         ContentProviderOperation.Builder builder =
             ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
         String myGroupsId = null;
@@ -1290,8 +1292,11 @@
             ContentProviderResult[] results = resolver.applyBatch(
                         ContactsContract.AUTHORITY, operationList);
             // the first result is always the raw_contact. return it's uri so
-            // that it can be found later
-            return results[0].uri;
+            // that it can be found later. do null checking for badly behaving
+            // ContentResolvers
+            return (results == null || results.length == 0 || results[0] == null)
+                ? null
+                : results[0].uri;
         } catch (RemoteException e) {
             Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
             return null;
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/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d8010bc..c44854a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -124,6 +124,13 @@
         }
     };
     
+    final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+            = new ViewTreeObserver.OnScrollChangedListener() {
+                    public void onScrollChanged() {
+                        updateWindow(false);
+                    }
+            };
+            
     boolean mRequestedVisible = false;
     boolean mWindowVisibility = false;
     boolean mViewVisibility = false;
@@ -180,6 +187,7 @@
         mLayout.token = getWindowToken();
         mLayout.setTitle("SurfaceView");
         mViewVisibility = getVisibility() == VISIBLE;
+        getViewTreeObserver().addOnScrollChangedListener(mScrollChangedListener);
     }
 
     @Override
@@ -200,6 +208,7 @@
     
     @Override
     protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener);
         mRequestedVisible = false;
         updateWindow(false);
         mHaveFrame = false;
@@ -224,12 +233,6 @@
     }
     
     @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        updateWindow(false);
-    }
-
-    @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
         updateWindow(false);
@@ -503,6 +506,17 @@
         updateWindow(false);
     }
 
+    /**
+     * Check to see if the surface has fixed size dimensions or if the surface's
+     * dimensions are dimensions are dependent on its current layout.
+     *
+     * @return true if the surface has dimensions that are fixed in size
+     * @hide
+     */
+    public boolean isFixedSize() {
+        return (mRequestedWidth != -1 || mRequestedHeight != -1);
+    }
+
     private static class MyWindow extends BaseIWindow {
         private final WeakReference<SurfaceView> mSurfaceView;
 
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..d2563a8 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);
 
@@ -1037,7 +1040,7 @@
             clientStream.writeInt(outRect.width());
             clientStream.writeInt(outRect.height());
     
-            captureViewLayer(root, clientStream);
+            captureViewLayer(root, clientStream, true);
             
             clientStream.write(2);
         } finally {
@@ -1045,9 +1048,11 @@
         }
     }
 
-    private static void captureViewLayer(View view, DataOutputStream clientStream)
+    private static void captureViewLayer(View view, DataOutputStream clientStream, boolean visible)
             throws IOException {
 
+        final boolean localVisible = view.getVisibility() == View.VISIBLE && visible;
+
         if ((view.mPrivateFlags & View.SKIP_DRAW) != View.SKIP_DRAW) {
             final int id = view.getId();
             String name = view.getClass().getSimpleName();
@@ -1057,7 +1062,7 @@
     
             clientStream.write(1);
             clientStream.writeUTF(name);
-            clientStream.writeByte(view.getVisibility() == View.VISIBLE ? 1 : 0);
+            clientStream.writeByte(localVisible ? 1 : 0);
     
             int[] position = new int[2];
             // XXX: Should happen on the UI thread
@@ -1083,7 +1088,7 @@
             int count = group.getChildCount();
 
             for (int i = 0; i < count; i++) {
-                captureViewLayer(group.getChildAt(i), clientStream);
+                captureViewLayer(group.getChildAt(i), clientStream, localVisible);
             }
         }
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 597d583..006aff8 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1274,13 +1274,27 @@
 
     @Override
     Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
-        int oldCount = mChildrenCount;
+        int count = mChildrenCount;
+        int[] visibilities = null;
+
         if (skipChildren) {
-            mChildrenCount = 0;
+            visibilities = new int[count];
+            for (int i = 0; i < count; i++) {
+                View child = getChildAt(i);
+                visibilities[i] = child.getVisibility();
+                if (visibilities[i] == View.VISIBLE) {
+                    child.setVisibility(INVISIBLE);
+                }
+            }
         }
 
         Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
-        mChildrenCount = oldCount;
+
+        if (skipChildren) {
+            for (int i = 0; i < count; i++) {
+                getChildAt(i).setVisibility(visibilities[i]);
+            }        
+        }
 
         return b;
     }
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/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index af42ce0..344b390 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -527,20 +527,12 @@
      */
     private int getFileSize(String uri) {
         int size = 0;
-        Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
-                new String[] { OpenableColumns.SIZE },
-                null,
-                null,
-                null);
-        if (cursor != null) {
-            try {
-                if (cursor.moveToNext()) {
-                    size = cursor.getInt(0);
-                }
-            } finally {
-                cursor.close();
-            }
-        }
+        try {
+            InputStream stream = mContext.getContentResolver()
+                            .openInputStream(Uri.parse(uri));
+            size = stream.available();
+            stream.close();
+        } catch (Exception e) {}
         return size;
     }
 
@@ -586,7 +578,10 @@
      * @param headers The http headers.
      * @param postData If the method is "POST" postData is sent as the request
      *                 body. Is null when empty.
-     * @param cacheMode The cache mode to use when loading this resource.
+     * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
+     * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
+     * @param mainResource True if the this resource is the main request, not a supporting resource
+     * @param userGesture
      * @param synchronous True if the load is synchronous.
      * @return A newly created LoadListener object.
      */
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 1c59c10..4f680e5 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -733,11 +733,15 @@
             ret.contentdisposition = contentDisposition;
         }
 
+        // lastModified and etag may be set back to http header. So they can't
+        // be empty string.
         String lastModified = headers.getLastModified();
-        if (lastModified != null) ret.lastModified = lastModified;
+        if (lastModified != null && lastModified.length() > 0) {
+            ret.lastModified = lastModified;
+        }
 
         String etag = headers.getEtag();
-        if (etag != null) ret.etag = etag;
+        if (etag != null && etag.length() > 0) ret.etag = etag;
 
         String cacheControl = headers.getCacheControl();
         if (cacheControl != null) {
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 4f2830b..1eb3fa4 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -397,6 +397,7 @@
                             notify();
                         }
                     }
+                    mWebView.dismissZoomControl();
                 }
                 break;
 
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 427ce76..74a648e 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -230,14 +230,8 @@
                 return 1;
             }
 
-            diff = cookie2.name.hashCode() - cookie1.name.hashCode();
-            if (diff != 0) return diff;
-
-            // cookie1 and cookie2 both have non-null values so we emit a
-            // warning and treat them as the same.
-            Log.w(LOGTAG, "Found two cookies with the same value."
-                    + "cookie1=" + cookie1 + " , cookie2=" + cookie2);
-            return 0;
+            // Fallback to comparing the name to ensure consistent order.
+            return cookie1.name.compareTo(cookie2.name);
         }
     }
 
@@ -255,7 +249,7 @@
      * first.
      * 
      * @return CookieManager
-=     */
+     */
     public static synchronized CookieManager getInstance() {
         if (sRef == null) {
             sRef = new CookieManager();
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/LoadListener.java b/core/java/android/webkit/LoadListener.java
index a23a4ce..2c24757 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -121,6 +121,7 @@
 
     // Does this loader correspond to the main-frame top-level page?
     private boolean mIsMainPageLoader;
+    // Does this loader correspond to the main content (as opposed to a supporting resource)
     private final boolean mIsMainResourceLoader;
     private final boolean mUserGesture;
 
@@ -520,23 +521,28 @@
     }
 
     /**
-     * Implementation of certificate handler for EventHandler.
-     * Called every time a resource is loaded via a secure
-     * connection. In this context, can be called multiple
-     * times if we have redirects
-     * @param certificate The SSL certifcate
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
+     * Implementation of certificate handler for EventHandler. Called
+     * before a resource is requested. In this context, can be called
+     * multiple times if we have redirects
+     *
+     * IMPORTANT: as this is called from network thread, can't call
+     * native directly
+     *
+     * @param certificate The SSL certifcate or null if the request
+     * was not secure
      */
     public void certificate(SslCertificate certificate) {
+        if (DebugFlags.LOAD_LISTENER) {
+            Log.v(LOGTAG, "LoadListener.certificate: " + certificate);
+        }
         sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate));
     }
 
     // Handle the certificate on the WebCore thread.
     private void handleCertificate(SslCertificate certificate) {
-        // if this is the top-most main-frame page loader
-        if (mIsMainPageLoader) {
-            // update the browser frame (ie, the main frame)
+        // if this is main resource of the top frame
+        if (mIsMainPageLoader && mIsMainResourceLoader) {
+            // update the browser frame with certificate
             mBrowserFrame.certificate(certificate);
         }
     }
@@ -1192,6 +1198,16 @@
     }
 
     /**
+     * Pause the load. For example, if a plugin is unable to accept more data,
+     * we pause reading from the request. Called directly from the WebCore thread.
+     */
+    void pauseLoad(boolean pause) {
+        if (mRequestHandle != null) {
+            mRequestHandle.pauseRequest(pause);
+        }
+    }
+
+    /**
      * Cancel a request.
      * FIXME: This will only work if the request has yet to be handled. This
      * is in no way guarenteed if requests are served in a separate thread.
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/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 9314d7b..5345879 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -340,6 +340,15 @@
     }
 
     /**
+     * Sets the maximum size of the ApplicationCache.
+     * This should only ever be called on the WebKit thread.
+     * @hide Pending API council approval
+     */
+    public void setAppCacheMaximumSize(long size) {
+        nativeSetAppCacheMaximumSize(size);
+    }
+
+    /**
      * Utility function to send a message to our handler
      */
     private synchronized void postMessage(Message msg) {
@@ -402,4 +411,5 @@
     private static native void nativeSetQuotaForOrigin(String origin, long quota);
     private static native void nativeDeleteOrigin(String origin);
     private static native void nativeDeleteAllData();
+    private static native void nativeSetAppCacheMaximumSize(long size);
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 622d22d..b5eb5d5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1088,6 +1088,9 @@
      * Sets the SSL certificate for the main top-level page.
      */
     public void setCertificate(SslCertificate certificate) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "setCertificate=" + certificate);
+        }
         // here, the certificate can be null (if the site is not secure)
         mCertificate = certificate;
     }
@@ -1903,15 +1906,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());
     }
 
     /**
@@ -2301,20 +2297,13 @@
         scrollBar.draw(canvas);
     }
 
-    private boolean canOverscrollHorizontally() {
-        return (Math.abs(mMinZoomScale - mMaxZoomScale) >= MINIMUM_SCALE_INCREMENT)
-                && getSettings().supportZoom()
-                && getSettings().getUseWideViewPort();
-    }
-
     @Override
     protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
             boolean clampedY) {
         mInOverScrollMode = false;
         int maxX = computeMaxScrollX();
-        if (maxX == 0 && !canOverscrollHorizontally()) {
-            // do not over scroll x if the page just fits the screen and it
-            // can't zoom or the view doesn't use wide viewport
+        if (maxX == 0) {
+            // do not over scroll x if the page just fits the screen
             scrollX = pinLocX(scrollX);
         } else if (scrollX < 0 || scrollX > maxX) {
             mInOverScrollMode = true;
@@ -2629,6 +2618,9 @@
      * @hide
      */
     public void notifyFindDialogDismissed() {
+        if (mWebViewCore == null) {
+            return;
+        }
         clearMatches();
         setFindIsUp(false);
         recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
@@ -3127,13 +3119,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));
             }
@@ -3990,8 +3982,7 @@
     protected void onDetachedFromWindow() {
         clearTextEntry(false);
         super.onDetachedFromWindow();
-        // Clean up the zoom controller
-        mZoomButtonsController.setVisible(false);
+        dismissZoomControl();
     }
 
     /**
@@ -4190,6 +4181,8 @@
             }
         }
 
+        dismissZoomControl();
+
         // onSizeChanged() is called during WebView layout. And any
         // requestLayout() is blocked during layout. As setNewZoomScale() will
         // call its child View to reposition itself through ViewManager's
@@ -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
@@ -4457,9 +4444,7 @@
         public boolean onScaleBegin(ScaleGestureDetector detector) {
             // cancel the single touch handling
             cancelTouch();
-            if (mZoomButtonsController.isVisible()) {
-                mZoomButtonsController.setVisible(false);
-            }
+            dismissZoomControl();
             // reset the zoom overview mode so that the page won't auto grow
             mInZoomOverview = false;
             // If it is in password mode, turn it off so it does not draw
@@ -5371,7 +5356,7 @@
 
     private int computeMaxScrollY() {
         return Math.max(computeVerticalScrollRange() + getTitleHeight()
-                - getViewHeightWithTitle(), getTitleHeight());
+                - getViewHeightWithTitle(), 0);
     }
 
     public void flingScroll(int vx, int vy) {
@@ -5398,9 +5383,6 @@
                 vx = 0;
             }
         }
-        if (maxX == 0 && !canOverscrollHorizontally()) {
-            vx = 0;
-        }
         if (true /* EMG release: make our fling more like Maps' */) {
             // maps cuts their velocity in half
             vx = vx * 3 / 4;
@@ -5439,8 +5421,9 @@
         mLastVelY = vy;
         mLastVelocity = (float) Math.hypot(vx, vy);
 
+        // no horizontal overscroll if the content just fits
         mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
-                getViewWidth() / 3, getViewHeight() / 3);
+                maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3);
         // TODO: duration is calculated based on velocity, if the range is
         // small, the animation will stop before duration is up. We may
         // want to calculate how long the animation is going to run to precisely
@@ -5792,6 +5775,31 @@
         }
     }
 
+    void dismissZoomControl() {
+        if (mWebViewCore == null) {
+            // maybe called after WebView's destroy(). As we can't get settings,
+            // just hide zoom control for both styles.
+            mZoomButtonsController.setVisible(false);
+            if (mZoomControls != null) {
+                mZoomControls.hide();
+            }
+            return;
+        }
+        WebSettings settings = getSettings();
+        if (settings.getBuiltInZoomControls()) {
+            if (mZoomButtonsController.isVisible()) {
+                mZoomButtonsController.setVisible(false);
+            }
+        } else {
+            if (mZoomControlRunnable != null) {
+                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+            }
+            if (mZoomControls != null) {
+                mZoomControls.hide();
+            }
+        }
+    }
+
     // Rule for double tap:
     // 1. if the current scale is not same as the text wrap scale and layout
     //    algorithm is NARROW_COLUMNS, fit to column;
@@ -5806,20 +5814,9 @@
         mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
         mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
         WebSettings settings = getSettings();
-        // remove the zoom control after double tap
-        if (settings.getBuiltInZoomControls()) {
-            if (mZoomButtonsController.isVisible()) {
-                mZoomButtonsController.setVisible(false);
-            }
-        } else {
-            if (mZoomControlRunnable != null) {
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-            }
-            if (mZoomControls != null) {
-                mZoomControls.hide();
-            }
-        }
         settings.setDoubleTapToastCount(0);
+        // remove the zoom control after double tap
+        dismissZoomControl();
         ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
         if (plugin != null) {
             if (isPluginFitOnScreen(plugin)) {
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index a870931..a75e6f0 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -389,10 +389,17 @@
             return false;
         }
 
-        Cursor cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
-                null, null, null, null, null);
-        boolean ret = cursor.moveToFirst() == true;
-        cursor.close();
+        Cursor cursor = null;
+        boolean ret = false;
+        try {
+            cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
+                    null, null, null, null, null);
+            ret = cursor.moveToFirst() == true;
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "hasEntries", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
         return ret;
     }
 
@@ -420,33 +427,39 @@
             };
             final String selection = "(" + COOKIES_DOMAIN_COL
                     + " GLOB '*' || ?)";
-            Cursor cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
-                    columns, selection, new String[] { domain }, null, null,
-                    null);
-            if (cursor.moveToFirst()) {
-                int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
-                int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
-                int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
-                int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
-                int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
-                int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
-                do {
-                    Cookie cookie = new Cookie();
-                    cookie.domain = cursor.getString(domainCol);
-                    cookie.path = cursor.getString(pathCol);
-                    cookie.name = cursor.getString(nameCol);
-                    cookie.value = cursor.getString(valueCol);
-                    if (cursor.isNull(expiresCol)) {
-                        cookie.expires = -1;
-                    } else {
-                        cookie.expires = cursor.getLong(expiresCol);
-                    }
-                    cookie.secure = cursor.getShort(secureCol) != 0;
-                    cookie.mode = Cookie.MODE_NORMAL;
-                    list.add(cookie);
-                } while (cursor.moveToNext());
+            Cursor cursor = null;
+            try {
+                cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
+                        columns, selection, new String[] { domain }, null, null,
+                        null);
+                if (cursor.moveToFirst()) {
+                    int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
+                    int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
+                    int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
+                    int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
+                    int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
+                    int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
+                    do {
+                        Cookie cookie = new Cookie();
+                        cookie.domain = cursor.getString(domainCol);
+                        cookie.path = cursor.getString(pathCol);
+                        cookie.name = cursor.getString(nameCol);
+                        cookie.value = cursor.getString(valueCol);
+                        if (cursor.isNull(expiresCol)) {
+                            cookie.expires = -1;
+                        } else {
+                            cookie.expires = cursor.getLong(expiresCol);
+                        }
+                        cookie.secure = cursor.getShort(secureCol) != 0;
+                        cookie.mode = Cookie.MODE_NORMAL;
+                        list.add(cookie);
+                    } while (cursor.moveToNext());
+                }
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "getCookiesForDomain", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.close();
             return list;
         }
     }
@@ -604,12 +617,12 @@
             return null;
         }
 
-        Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
-                    + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
-                    + "contentdisposition FROM cache WHERE url = ?",
-                new String[] { url });
-
+        Cursor cursor = null;
+        final String query = "SELECT filepath, lastmodify, etag, expires, "
+                + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
+                + "contentdisposition FROM cache WHERE url = ?";
         try {
+            cursor = mCacheDatabase.rawQuery(query, new String[] { url });
             if (cursor.moveToFirst()) {
                 CacheResult ret = new CacheResult();
                 ret.localPath = cursor.getString(0);
@@ -625,6 +638,8 @@
                 ret.contentdisposition = cursor.getString(10);
                 return ret;
             }
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "getCache", e);
         } finally {
             if (cursor != null) cursor.close();
         }
@@ -688,78 +703,108 @@
             return false;
         }
 
-        Cursor cursor = mCacheDatabase.query("cache", ID_PROJECTION,
-                null, null, null, null, null);
-        boolean ret = cursor.moveToFirst() == true;
-        cursor.close();
+        Cursor cursor = null;
+        boolean ret = false;
+        try {
+            cursor = mCacheDatabase.query("cache", ID_PROJECTION,
+                    null, null, null, null, null);
+            ret = cursor.moveToFirst() == true;
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "hasCache", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
         return ret;
     }
 
     long getCacheTotalSize() {
         long size = 0;
-        Cursor cursor = mCacheDatabase.rawQuery(
-                "SELECT SUM(contentlength) as sum FROM cache", null);
-        if (cursor.moveToFirst()) {
-            size = cursor.getLong(0);
+        Cursor cursor = null;
+        final String query = "SELECT SUM(contentlength) as sum FROM cache";
+        try {
+            cursor = mCacheDatabase.rawQuery(query, null);
+            if (cursor.moveToFirst()) {
+                size = cursor.getLong(0);
+            }
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "getCacheTotalSize", e);
+        } finally {
+            if (cursor != null) cursor.close();
         }
-        cursor.close();
         return size;
     }
 
     List<String> trimCache(long amount) {
         ArrayList<String> pathList = new ArrayList<String>(100);
-        Cursor cursor = mCacheDatabase.rawQuery(
-                "SELECT contentlength, filepath FROM cache ORDER BY expires ASC",
-                null);
-        if (cursor.moveToFirst()) {
-            int batchSize = 100;
-            StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
-            pathStr.append("DELETE FROM cache WHERE filepath IN (?");
-            for (int i = 1; i < batchSize; i++) {
-                pathStr.append(", ?");
-            }
-            pathStr.append(")");
-            SQLiteStatement statement = mCacheDatabase.compileStatement(pathStr
-                    .toString());
-            // as bindString() uses 1-based index, initialize index to 1
-            int index = 1;
-            do {
-                long length = cursor.getLong(0);
-                if (length == 0) {
-                    continue;
+        Cursor cursor = null;
+        final String query = "SELECT contentlength, filepath FROM cache ORDER BY expires ASC";
+        try {
+            cursor = mCacheDatabase.rawQuery(query, null);
+            if (cursor.moveToFirst()) {
+                int batchSize = 100;
+                StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
+                pathStr.append("DELETE FROM cache WHERE filepath IN (?");
+                for (int i = 1; i < batchSize; i++) {
+                    pathStr.append(", ?");
                 }
-                amount -= length;
-                String filePath = cursor.getString(1);
-                statement.bindString(index, filePath);
-                pathList.add(filePath);
-                if (index++ == batchSize) {
-                    statement.execute();
-                    statement.clearBindings();
-                    index = 1;
+                pathStr.append(")");
+                SQLiteStatement statement = null;
+                try {
+                    statement = mCacheDatabase.compileStatement(
+                            pathStr.toString());
+                    // as bindString() uses 1-based index, initialize index to 1
+                    int index = 1;
+                    do {
+                        long length = cursor.getLong(0);
+                        if (length == 0) {
+                            continue;
+                        }
+                        amount -= length;
+                        String filePath = cursor.getString(1);
+                        statement.bindString(index, filePath);
+                        pathList.add(filePath);
+                        if (index++ == batchSize) {
+                            statement.execute();
+                            statement.clearBindings();
+                            index = 1;
+                        }
+                    } while (cursor.moveToNext() && amount > 0);
+                    if (index > 1) {
+                        // there may be old bindings from the previous statement
+                        // if index is less than batchSize, which is Ok.
+                        statement.execute();
+                    }
+                } catch (IllegalStateException e) {
+                    Log.e(LOGTAG, "trimCache SQLiteStatement", e);
+                } finally {
+                    if (statement != null) statement.close();
                 }
-            } while (cursor.moveToNext() && amount > 0);
-            if (index > 1) {
-                // there may be old bindings from the previous statement if
-                // index is less than batchSize, which is Ok.
-                statement.execute();
             }
-            statement.close();
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "trimCache Cursor", e);
+        } finally {
+            if (cursor != null) cursor.close();
         }
-        cursor.close();
         return pathList;
     }
 
     List<String> getAllCacheFileNames() {
         ArrayList<String> pathList = null;
-        Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
-                null);
-        if (cursor != null && cursor.moveToFirst()) {
-            pathList = new ArrayList<String>(cursor.getCount());
-            do {
-                pathList.add(cursor.getString(0));
-            } while (cursor.moveToNext());
+        Cursor cursor = null;
+        try {
+            cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
+                    null);
+            if (cursor != null && cursor.moveToFirst()) {
+                pathList = new ArrayList<String>(cursor.getCount());
+                do {
+                    pathList.add(cursor.getString(0));
+                } while (cursor.moveToNext());
+            }
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "getAllCacheFileNames", e);
+        } finally {
+            if (cursor != null) cursor.close();
         }
-        cursor.close();
         return pathList;
     }
 
@@ -809,17 +854,23 @@
         final String selection = "(" + PASSWORD_HOST_COL + " == ?)";
         synchronized (mPasswordLock) {
             String[] ret = null;
-            Cursor cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID],
-                    columns, selection, new String[] { schemePlusHost }, null,
-                    null, null);
-            if (cursor.moveToFirst()) {
-                ret = new String[2];
-                ret[0] = cursor.getString(
-                        cursor.getColumnIndex(PASSWORD_USERNAME_COL));
-                ret[1] = cursor.getString(
-                        cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
+            Cursor cursor = null;
+            try {
+                cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID],
+                        columns, selection, new String[] { schemePlusHost }, null,
+                        null, null);
+                if (cursor.moveToFirst()) {
+                    ret = new String[2];
+                    ret[0] = cursor.getString(
+                            cursor.getColumnIndex(PASSWORD_USERNAME_COL));
+                    ret[1] = cursor.getString(
+                            cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
+                }
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "getUsernamePassword", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.close();
             return ret;
         }
     }
@@ -900,17 +951,23 @@
                 + HTTPAUTH_REALM_COL + " == ?)";
         synchronized (mHttpAuthLock) {
             String[] ret = null;
-            Cursor cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
-                    columns, selection, new String[] { host, realm }, null,
-                    null, null);
-            if (cursor.moveToFirst()) {
-                ret = new String[2];
-                ret[0] = cursor.getString(
-                        cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
-                ret[1] = cursor.getString(
-                        cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
+            Cursor cursor = null;
+            try {
+                cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
+                        columns, selection, new String[] { host, realm }, null,
+                        null, null);
+                if (cursor.moveToFirst()) {
+                    ret = new String[2];
+                    ret[0] = cursor.getString(
+                            cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
+                    ret[1] = cursor.getString(
+                            cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
+                }
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.close();
             return ret;
         }
     }
@@ -958,18 +1015,24 @@
         final String selection = "(" + FORMURL_URL_COL + " == ?)";
         synchronized (mFormLock) {
             long urlid = -1;
-            Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
-                    ID_PROJECTION, selection, new String[] { url }, null, null,
-                    null);
-            if (cursor.moveToFirst()) {
-                urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
-            } else {
-                ContentValues c = new ContentValues();
-                c.put(FORMURL_URL_COL, url);
-                urlid = mDatabase.insert(
-                        mTableNames[TABLE_FORMURL_ID], null, c);
+            Cursor cursor = null;
+            try {
+                cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
+                        ID_PROJECTION, selection, new String[] { url }, null, null,
+                        null);
+                if (cursor.moveToFirst()) {
+                    urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
+                } else {
+                    ContentValues c = new ContentValues();
+                    c.put(FORMURL_URL_COL, url);
+                    urlid = mDatabase.insert(
+                            mTableNames[TABLE_FORMURL_ID], null, c);
+                }
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "setFormData", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.close();
             if (urlid >= 0) {
                 Set<Entry<String, String>> set = formdata.entrySet();
                 Iterator<Entry<String, String>> iter = set.iterator();
@@ -1002,27 +1065,39 @@
         final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND ("
                 + FORMDATA_NAME_COL + " == ?)";
         synchronized (mFormLock) {
-            Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
-                    ID_PROJECTION, urlSelection, new String[] { url }, null,
-                    null, null);
-            if (cursor.moveToFirst()) {
-                long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
-                Cursor dataCursor = mDatabase.query(
-                        mTableNames[TABLE_FORMDATA_ID],
-                        new String[] { ID_COL, FORMDATA_VALUE_COL },
-                        dataSelection,
-                        new String[] { Long.toString(urlid), name }, null,
+            Cursor cursor = null;
+            try {
+                cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
+                        ID_PROJECTION, urlSelection, new String[] { url }, null,
                         null, null);
-                if (dataCursor.moveToFirst()) {
-                    int valueCol =
-                            dataCursor.getColumnIndex(FORMDATA_VALUE_COL);
-                    do {
-                        values.add(dataCursor.getString(valueCol));
-                    } while (dataCursor.moveToNext());
+                if (cursor.moveToFirst()) {
+                    long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
+                    Cursor dataCursor = null;
+                    try {
+                        dataCursor = mDatabase.query(
+                                mTableNames[TABLE_FORMDATA_ID],
+                                new String[] { ID_COL, FORMDATA_VALUE_COL },
+                                dataSelection,
+                                new String[] { Long.toString(urlid), name },
+                                null, null, null);
+                        if (dataCursor.moveToFirst()) {
+                            int valueCol = dataCursor.getColumnIndex(
+                                    FORMDATA_VALUE_COL);
+                            do {
+                                values.add(dataCursor.getString(valueCol));
+                            } while (dataCursor.moveToNext());
+                        }
+                    } catch (IllegalStateException e) {
+                        Log.e(LOGTAG, "getFormData dataCursor", e);
+                    } finally {
+                        if (dataCursor != null) dataCursor.close();
+                    }
                 }
-                dataCursor.close();
+            } catch (IllegalStateException e) {
+                Log.e(LOGTAG, "getFormData cursor", e);
+            } finally {
+                if (cursor != null) cursor.close();
             }
-            cursor.close();
             return values;
         }
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a78429e..a30059c 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;
@@ -1834,9 +1835,11 @@
                     mSelectedPosition < mAdapter.getCount()) {
 
                 final View view = getChildAt(mSelectedPosition - mFirstPosition);
-                performItemClick(view, mSelectedPosition, mSelectedRowId);
+                if (view != null) {
+                    performItemClick(view, mSelectedPosition, mSelectedRowId);
+                    view.setPressed(false);
+                }
                 setPressed(false);
-                if (view != null) view.setPressed(false);
                 return true;
             }
             break;
@@ -2388,6 +2391,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 +2565,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 +2602,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 +2696,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 +2753,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 +3992,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 +4020,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/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 3328c13..52a560c 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -216,23 +216,23 @@
     }
 
     /**
-     * Sets the drawable to use as the left part of the strip below the
+     * Sets the drawable to use as the right part of the strip below the
      * tab indicators.
-     * @param drawable the left strip drawable
+     * @param drawable the right strip drawable
      */
     public void setRightStripDrawable(Drawable drawable) {
-        mBottomLeftStrip = drawable;
+        mBottomRightStrip = drawable;
         requestLayout();
         invalidate();    }
 
     /**
-     * Sets the drawable to use as the left part of the strip below the
+     * Sets the drawable to use as the right part of the strip below the
      * tab indicators.
      * @param resId the resource identifier of the drawable to use as the
-     * left strip drawable
+     * right strip drawable
      */
     public void setRightStripDrawable(int resId) {
-        mBottomLeftStrip = mContext.getResources().getDrawable(resId);
+        mBottomRightStrip = mContext.getResources().getDrawable(resId);
         requestLayout();
         invalidate();
     }
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ded0559..531d9fe 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -64,6 +64,7 @@
     private static final int STATE_PLAYBACK_COMPLETED = 5;
     private static final int STATE_SUSPEND            = 6;
     private static final int STATE_RESUME             = 7;
+    private static final int STATE_SUSPEND_UNSUPPORTED = 8;
 
     // mCurrentState is a VideoView object's current state.
     // mTargetState is the state that a method caller intends to reach.
@@ -586,8 +587,9 @@
                 mCurrentState = STATE_SUSPEND;
                 mTargetState = STATE_SUSPEND;
             } else {
-                Log.w(TAG, "Unable to suspend video");
-                mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+                release(false);
+                mCurrentState = STATE_SUSPEND_UNSUPPORTED;
+                Log.w(TAG, "Unable to suspend video. Release MediaPlayer.");
             }
         }
     }
@@ -603,8 +605,11 @@
                 mTargetState = mStateWhenSuspended;
             } else {
                 Log.w(TAG, "Unable to resume video");
-                mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
             }
+            return;
+        }
+        if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) {
+            openVideo();
         }
     }
 
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/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9b7f487..37cd412 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -89,7 +89,7 @@
     public static final int MODE_UNSPECIFIED = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
     public static final int MODE_PATTERN = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
     public static final int MODE_PIN = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-    public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+    public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 
     /**
      * The minimum number of dots the user must include in a wrong pattern
@@ -144,12 +144,11 @@
     /**
      * Gets the device policy password mode. If the mode is non-specific, returns
      * MODE_PATTERN which allows the user to choose anything.
-     *
-     * @return
      */
     public int getRequestedPasswordMode() {
         int policyMode = getDevicePolicyManager().getPasswordQuality(null);
         switch (policyMode) {
+            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
                 return MODE_PASSWORD;
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
@@ -270,9 +269,41 @@
     }
 
     /**
+     * Used by device policy manager to validate the current password
+     * information it has.
+     */
+    public int getActivePasswordQuality() {
+        switch (getPasswordMode()) {
+            case MODE_UNSPECIFIED:
+                return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            case MODE_PATTERN:
+                if (isLockPatternEnabled()) {
+                    return DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+                } else {
+                    return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+                }
+            case MODE_PIN:
+                if (isLockPasswordEnabled()) {
+                    return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+                } else {
+                    return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+                }
+            case MODE_PASSWORD:
+                if (isLockPasswordEnabled()) {
+                    return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+                } else {
+                    return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+                }
+        }
+        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+    }
+    
+    /**
      * Clear any lock pattern or password.
      */
     public void clearLock() {
+        getDevicePolicyManager().setActivePasswordState(
+                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
         saveLockPassword(null, LockPatternUtils.MODE_PATTERN);
         setLockPatternEnabled(false);
         saveLockPattern(null);
@@ -316,15 +347,9 @@
     }
 
     /**
-     * Compare the given password and mode, ensuring that the password meets
-     * the mode and returning the minimum mode needed for the given password.
-     * @param password The password to be used.
-     * @param reqMode The desired password mode.
-     * @return Returns {@link #MODE_UNSPECIFIED} if the password is not
-     * good enough for the given mode.  Otherwise, returns either the original
-     * reqMode or something better if that is needed for the given password.
+     * Compute the password quality from the given password string.
      */
-    static public int adjustPasswordMode(String password, int reqMode) {
+    static public int computePasswordQuality(String password) {
         boolean hasDigit = false;
         boolean hasNonDigit = false;
         final int len = password.length();
@@ -336,39 +361,16 @@
             }
         }
 
-        // First check if it is sufficient.
-        switch (reqMode) {
-            case MODE_PASSWORD: {
-                if (!hasDigit || !hasNonDigit) {
-                    return MODE_UNSPECIFIED;
-                }
-            } break;
-
-            case MODE_PIN:
-            case MODE_PATTERN: {
-                // Whatever we have is acceptable; we may need to promote the
-                // mode later.
-            } break;
-
-            default:
-                // If it isn't a mode we specifically know, then fail fast.
-                Log.w(TAG, "adjustPasswordMode: unknown mode " + reqMode);
-                return MODE_UNSPECIFIED;
+        if (hasNonDigit && hasDigit) {
+            return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
         }
-
-        // Do we need to promote?
         if (hasNonDigit) {
-            if (reqMode < MODE_PASSWORD) {
-                reqMode = MODE_PASSWORD;
-            }
+            return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
         }
         if (hasDigit) {
-            if (reqMode < MODE_PIN) {
-                reqMode = MODE_PIN;
-            }
+            return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
         }
-
-        return reqMode;
+        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
     }
 
     /**
@@ -392,12 +394,15 @@
             raf.close();
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (password != null) {
-                int finalMode = adjustPasswordMode(password, mode);
-                if (mode < finalMode) {
-                    mode = finalMode;
-                }
                 setLong(PASSWORD_TYPE_KEY, mode);
-                dpm.setActivePasswordState(mode, password.length());
+                int quality = computePasswordQuality(password);
+                if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                    dpm.setActivePasswordState(quality, password.length());
+                } else {
+                    // The password is not anything.
+                    dpm.setActivePasswordState(
+                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
+                }
             } else {
                 dpm.setActivePasswordState(
                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
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/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 2a1f23a..d4ac24a 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -424,7 +424,8 @@
                     // faster.
                     if ("text/plain".equals(type) || "application/smil".equals(type)) {
                         String text = c.getString(PART_COLUMN_TEXT);
-                        byte [] blob = new EncodedStringValue(text).getTextString();
+                        byte [] blob = new EncodedStringValue(text != null ? text : "")
+                            .getTextString();
                         baos.write(blob, 0, blob.length);
                     } else {
 
@@ -858,7 +859,7 @@
         } else {
             values.put(Mms.SUBJECT, "");
         }
-        
+
         long messageSize = sendReq.getMessageSize();
         if (messageSize > 0) {
             values.put(Mms.MESSAGE_SIZE, messageSize);
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/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 4e8d05b..36234a9 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -72,8 +72,9 @@
 }
 
 static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
-    LOGI("sqlite returned: database = %s, error code = %d, msg = %s\n",
-            (char *)databaseName, iErrCode, zMsg);
+    // skip printing this message if it is due to certain types of errors
+    if (iErrCode == SQLITE_CONSTRAINT) return;
+    LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
 }
 
 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 5432efb..0e7fd66 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -564,7 +564,10 @@
                                 NULL, NULL, NULL, NULL, NULL);
                         tearDownEventLoop(nat);
                         nat->vm->DetachCurrentThread();
-                        shutdown(nat->controlFdR,SHUT_RDWR);
+
+                        int fd = nat->controlFdR;
+                        nat->controlFdR = 0;
+                        close(fd);
                         return NULL;
                     }
                     case EVENT_LOOP_ADD:
@@ -653,9 +656,12 @@
 
 done:
     if (JNI_FALSE == result) {
-        if (nat->controlFdW || nat->controlFdR) {
-            shutdown(nat->controlFdW, SHUT_RDWR);
+        if (nat->controlFdW) {
+            close(nat->controlFdW);
             nat->controlFdW = 0;
+        }
+        if (nat->controlFdR) {
+            close(nat->controlFdR);
             nat->controlFdR = 0;
         }
         if (nat->me) env->DeleteGlobalRef(nat->me);
@@ -692,9 +698,10 @@
         nat->watchData = NULL;
         nat->pollDataSize = 0;
         nat->pollMemberCount = 0;
-        shutdown(nat->controlFdW, SHUT_RDWR);
+
+        int fd = nat->controlFdW;
         nat->controlFdW = 0;
-        nat->controlFdR = 0;
+        close(fd);
     }
     pthread_mutex_unlock(&(nat->thread_mutex));
 #endif // HAVE_BLUETOOTH
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e2caa0..a3c73d8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -732,7 +732,7 @@
          @hide  -->
     <permission android:name="android.permission.ASEC_ACCESS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
+        android:protectionLevel="signature"
         android:label="@string/permlab_asec_access"
         android:description="@string/permdesc_asec_access" />
 
@@ -740,7 +740,7 @@
          @hide  -->
     <permission android:name="android.permission.ASEC_CREATE"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
+        android:protectionLevel="signature"
         android:label="@string/permlab_asec_create"
         android:description="@string/permdesc_asec_create" />
 
@@ -748,7 +748,7 @@
          @hide  -->
     <permission android:name="android.permission.ASEC_DESTROY"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
+        android:protectionLevel="signature"
         android:label="@string/permlab_asec_destroy"
         android:description="@string/permdesc_asec_destroy" />
 
@@ -756,7 +756,7 @@
          @hide  -->
     <permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
+        android:protectionLevel="signature"
         android:label="@string/permlab_asec_mount_unmount"
         android:description="@string/permdesc_asec_mount_unmount" />
 
@@ -764,7 +764,7 @@
          @hide  -->
     <permission android:name="android.permission.ASEC_RENAME"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
+        android:protectionLevel="signature"
         android:label="@string/permlab_asec_rename"
         android:description="@string/permdesc_asec_rename" />
 
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/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f00572d..4e2f9c3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -531,8 +531,8 @@
         <!-- The screen orientation has changed, that is the user has
              rotated the device. -->
         <flag name="orientation" value="0x0080" />
-        <!-- The screen orientation has changed, that is the user has
-             rotated the device. -->
+        <!-- The screen layout has changed.  This might be caused by a
+             different display being activated. -->
         <flag name="screenLayout" value="0x0100" />
         <!-- The font scaling factor has changed, that is the user has
              selected a new global font size. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index cac26b9..8b6af71 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -67,4 +67,5 @@
   <item type="id" name="addToDictionary" />
   <item type="id" name="accountPreferences" />
   <item type="id" name="smallIcon" />
+  <item type="id" name="custom" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8c00884..a7336fc 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1238,6 +1238,8 @@
   <public type="attr" name="overscrollHeader" id="0x010102bf" />
   <public type="attr" name="overscrollFooter" id="0x010102c0" />
 
-  <public type="anim" name="cycle_interpolator" id="0x010a000c" />
+  <public type="id" name="custom" id="0x0102002b" />
     
+  <public type="anim" name="cycle_interpolator" id="0x010a000c" />
+
 </resources>
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/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
index 019b9e3..c3f6f79 100644
--- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
@@ -215,7 +215,7 @@
                 mTestCase.fail("Unexpected Uri has come: " + uri);
             }
         }  // for (int i = 0; i < size; i++) {
-        return null;
+        return fakeResultArray;
     }
 
     public void verify() {
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 28b0d2f..998e353 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -26,6 +26,12 @@
 
 namespace android {
 
+#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
+#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
+#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
+#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
+
+
 enum tts_synth_status {
     TTS_SYNTH_DONE              = 0,
     TTS_SYNTH_PENDING           = 1
@@ -85,7 +91,7 @@
     // Initialize the TTS engine and returns whether initialization succeeded.
     // @param synthDoneCBPtr synthesis callback function pointer
     // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result init(synthDoneCB_t synthDoneCBPtr);
+    virtual tts_result init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig);
 
     // Shut down the TTS engine and releases all associated resources.
     // @return TTS_SUCCESS, or TTS_FAILURE
@@ -122,7 +128,7 @@
     // @param variant pointer to the variant code
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
-    
+
     // Load the resources associated with the specified language, country and Locale variant.
     // The loaded language will only be used once a call to setLanguageFromLocale() with the same
     // language value is issued. Language and country values are coded according to the ISO three
@@ -220,19 +226,6 @@
     virtual tts_result synthesizeText(const char *text, int8_t *buffer,
             size_t bufferSize, void *userdata);
 
-    // Synthesize IPA text.
-    // As the synthesis is performed, the engine invokes the callback to notify
-    // the TTS framework that it has filled the given buffer, and indicates how
-    // many bytes it wrote. The callback is called repeatedly until the engine
-    // has generated all the audio data corresponding to the IPA data.
-    // @param ipa      the IPA data to synthesize
-    // @param userdata  pointer to be returned when the call is invoked
-    // @param buffer    the location where the synthesized data must be written
-    // @param bufferSize the number of bytes that can be written in buffer
-    // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported,
-    //         otherwise TTS_SUCCESS or TTS_FAILURE
-    virtual tts_result synthesizeIpa(const char *ipa, int8_t *buffer,
-            size_t bufferSize, void *userdata);
 };
 
 } // namespace android
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 130d83c..5ac0c5e 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -209,7 +209,7 @@
 class Mutex {
 public:
     enum {
-        NORMAL = 0,
+        PRIVATE = 0,
         SHARED = 1
     };
     
@@ -305,7 +305,13 @@
  */
 class Condition {
 public:
+    enum {
+        PRIVATE = 0,
+        SHARED = 1
+    };
+
     Condition();
+    Condition(int type);
     ~Condition();
     // Wait on the condition variable.  Lock the mutex before calling.
     status_t wait(Mutex& mutex);
@@ -329,6 +335,17 @@
 inline Condition::Condition() {
     pthread_cond_init(&mCond, NULL);
 }
+inline Condition::Condition(int type) {
+    if (type == SHARED) {
+        pthread_condattr_t attr;
+        pthread_condattr_init(&attr);
+        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+        pthread_cond_init(&mCond, &attr);
+        pthread_condattr_destroy(&attr);
+    } else {
+        pthread_cond_init(&mCond, NULL);
+    }
+}
 inline Condition::~Condition() {
     pthread_cond_destroy(&mCond);
 }
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/libs/rs/java/Fountain/res/raw/fountain2.rs b/libs/rs/java/Fountain/res/raw/fountain2.rs
new file mode 100644
index 0000000..3301140
--- /dev/null
+++ b/libs/rs/java/Fountain/res/raw/fountain2.rs
@@ -0,0 +1,73 @@
+// Fountain test script
+#pragma version(1)
+
+#include "rs_types.rsh"
+#include "rs_math.rsh"
+#include "rs_graphics.rsh"
+
+static int newPart = 0;
+
+typedef struct Control_s {
+    int x, y;
+    int rate;
+    int count;
+    float r, g, b;
+    rs_allocation partBuffer;
+    rs_mesh partMesh;
+} Control_t;
+Control_t *Control;
+
+typedef struct Point_s{
+    float2 delta;
+    float2 position;
+    unsigned int color;
+} Point_t;
+Point_t *point;
+
+int main(int launchID) {
+    int ct;
+    int count = Control->count;
+    int rate = Control->rate;
+    float height = getHeight();
+    Point_t * p = point;
+
+    if (rate) {
+        float rMax = ((float)rate) * 0.005f;
+        int x = Control->x;
+        int y = Control->y;
+        int color = ((int)(Control->r * 255.f)) |
+                    ((int)(Control->g * 255.f)) << 8 |
+                    ((int)(Control->b * 255.f)) << 16 |
+                    (0xf0 << 24);
+        Point_t * np = &p[newPart];
+
+        while (rate--) {
+            np->delta = vec2Rand(rMax);
+            np->position.x = x;
+            np->position.y = y;
+            np->color = color;
+            newPart++;
+            np++;
+            if (newPart >= count) {
+                newPart = 0;
+                np = &p[newPart];
+            }
+        }
+    }
+
+    for (ct=0; ct < count; ct++) {
+        float dy = p->delta.y + 0.15f;
+        float posy = p->position.y + dy;
+        if ((posy > height) && (dy > 0)) {
+            dy *= -0.3f;
+        }
+        p->delta.y = dy;
+        p->position.x += p->delta.x;
+        p->position.y = posy;
+        p++;
+    }
+
+    uploadToBufferObject(Control->partBuffer);
+    drawSimpleMesh(Control->partMesh);
+    return 1;
+}
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
new file mode 100644
index 0000000..70cd562
--- /dev/null
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -0,0 +1,65 @@
+
+
+extern float2 vec2Rand(float len);
+
+extern float3 float3Norm(float3);
+extern float float3Length(float3);
+extern float3 float3Add(float3 lhs, float3 rhs);
+extern float3 float3Sub(float3 lhs, float3 rhs);
+extern float3 float3Cross(float3 lhs, float3 rhs);
+extern float float3Dot(float3 lhs, float3 rhs);
+extern float3 float3Scale(float3 v, float scale);
+
+extern float4 float4Add(float4 lhs, float4 rhs);
+extern float4 float4Sub(float4 lhs, float4 rhs);
+extern float4 float4Cross(float4 lhs, float4 rhs);
+extern float float4Dot(float4 lhs, float4 rhs);
+extern float4 float4Scale(float4 v, float scale);
+
+    // context
+extern void bindProgramFragment(rs_program_fragment);
+extern void bindProgramStore(rs_program_store);
+extern void bindProgramVertex(rs_program_vertex);
+
+extern void bindSampler(rs_program_fragment, int slot, rs_sampler);
+extern void bindSampler(rs_program_fragment, int slot, rs_allocation);
+
+extern void vpLoadModelMatrix(const float *);
+extern void vpLoadTextureMatrix(const float *);
+
+
+// drawing
+extern void drawRect(float x1, float y1, float x2, float y2, float z);
+extern void drawQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4);
+extern void drawQuadTexCoords(float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, float x4, float y4, float z4, float u4, float v4);
+extern void drawSprite(float x, float y, float z, float w, float h);
+extern void drawSpriteScreenspace(float x, float y, float z, float w, float h);
+extern void drawLine(float x1, float y1, float z1, float x2, float y2, float z2);
+extern void drawPoint(float x1, float y1, float z1);
+extern void drawSimpleMesh(int ism);
+extern void drawSimpleMeshRange(int ism, int start, int len);
+
+// misc
+extern void pfClearColor(float, float, float, float);
+extern void color(float, float, float, float);
+extern void hsb(float, float, float, float);
+extern void hsbToRgb(float, float, float, float*);
+extern int hsbToAbgr(float, float, float, float);
+
+extern void uploadToTexture(int, int);
+extern void uploadToBufferObject(int);
+
+extern int colorFloatRGBAtoUNorm8(float, float, float, float);
+extern int colorFloatRGBto565(float, float, float);
+
+extern int getWidth();
+extern int getHeight();
+
+extern int sendToClient(void *data, int cmdID, int len, int waitForSpace);
+
+extern void debugF(const char *, float);
+extern void debugI32(const char *, int);
+extern void debugHexI32(const char *, int);
+
+
+
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index ae1acdb..4198a74 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -14,16 +14,16 @@
 typedef uint32_t uint;
 //typedef uint64_t ulong;
 
-typedef void * rs_element;
-typedef void * rs_type;
-typedef void * rs_allocation;
-typedef void * rs_sampler;
-typedef void * rs_script;
-typedef void * rs_mesh;
-typedef void * rs_program_fragment;
-typedef void * rs_program_vertex;
-typedef void * rs_program_raster;
-typedef void * rs_program_store;
+typedef int rs_element;
+typedef int rs_type;
+typedef int rs_allocation;
+typedef int rs_sampler;
+typedef int rs_script;
+typedef int rs_mesh;
+typedef int rs_program_fragment;
+typedef int rs_program_vertex;
+typedef int rs_program_raster;
+typedef int rs_program_store;
 
 typedef float float2 __attribute__((ext_vector_type(2)));
 typedef float float3 __attribute__((ext_vector_type(3)));
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 65ce1c1..a17e8ac 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -34,7 +34,7 @@
 // ----------------------------------------------------------------------------
 
 SharedClient::SharedClient()
-    : lock(Mutex::SHARED)
+    : lock(Mutex::SHARED), cv(Condition::SHARED)
 {
 }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3a3c66b..b56e638 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}
      */
@@ -1474,8 +1472,6 @@
     //====================================================================
     // Remote Control
     /**
-     * @hide
-     * TODO unhide for SDK
      * TODO document for SDK
      * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
      *      that will receive the media button intent. This broadcast receiver must be declared
@@ -1492,21 +1488,6 @@
     }
 
     /**
-     * @hide
-     * TODO unhide for SDK
-     * TODO document for SDK
-     * @param eventReceiverClass class of a {@link android.content.BroadcastReceiver} that will
-     *     receive the media button intent. This broadcast receiver must be declared in the
-     *     application manifest.
-     */
-    public void registerMediaButtonEventReceiver(Class<?> eventReceiverClass) {
-        registerMediaButtonEventReceiver(new ComponentName(
-                eventReceiverClass.getPackage().getName(), eventReceiverClass.getName()));
-    }
-
-    /**
-     * @hide
-     * TODO unhide for SDK
      * TODO document for SDK
      */
     public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
@@ -1519,16 +1500,6 @@
     }
 
     /**
-     * @hide
-     * TODO unhide for SDK
-     * TODO document for SDK
-     */
-    public void unregisterMediaButtonEventReceiver(Class<?> eventReceiverClass) {
-        unregisterMediaButtonEventReceiver(new ComponentName(
-                eventReceiverClass.getPackage().getName(), eventReceiverClass.getName()));
-    }
-
-    /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
      *  agent when audio settings are restored and causes the AudioService
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 75e51f9..74e6157 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -208,6 +208,9 @@
     /** @see System#MODE_RINGER_STREAMS_AFFECTED */
     private int mRingerModeAffectedStreams;
 
+    // Streams currently muted by ringer mode
+    private int mRingerModeMutedStreams;
+
     /** @see System#MUTE_STREAMS_AFFECTED */
     private int mMuteAffectedStreams;
 
@@ -404,7 +407,7 @@
 
 
         VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
-        final int oldIndex = streamState.mIndex;
+        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
         boolean adjustVolume = true;
 
         // If either the client forces allowing ringer modes for this adjustment,
@@ -416,30 +419,43 @@
             adjustVolume = checkForRingerModeChange(oldIndex, direction);
         }
 
-        if (adjustVolume && streamState.adjustIndex(direction)) {
-            // Post message to set system volume (it in turn will post a message
-            // to persist). Do not change volume if stream is muted.
-            if (streamState.muteCount() == 0) {
+        // If stream is muted, adjust last audible index only
+        int index;
+        if (streamState.muteCount() != 0) {
+            if (adjustVolume) {
+                streamState.adjustLastAudibleIndex(direction);
+                // Post a persist volume msg
+                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+            }
+            index = streamState.mLastAudibleIndex;
+        } else {
+            if (adjustVolume && streamState.adjustIndex(direction)) {
+                // Post message to set system volume (it in turn will post a message
+                // to persist). Do not change volume if stream is muted.
                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
                         streamState, 0);
             }
+            index = streamState.mIndex;
         }
-
         // UI
         mVolumePanel.postVolumeChanged(streamType, flags);
         // Broadcast Intent
-        sendVolumeUpdate(streamType, oldIndex, streamState.mIndex);
+        sendVolumeUpdate(streamType, oldIndex, index);
     }
 
     /** @see AudioManager#setStreamVolume(int, int, int) */
     public void setStreamVolume(int streamType, int index, int flags) {
         ensureValidStreamType(streamType);
+        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
 
-        final int oldIndex = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
+        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
 
         index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
         setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
 
+        index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
+
         // UI, etc.
         mVolumePanel.postVolumeChanged(streamType, flags);
         // Broadcast Intent
@@ -470,22 +486,30 @@
      */
     private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
         VolumeStreamState streamState = mStreamStates[streamType];
-        if (streamState.setIndex(index, lastAudible) || force) {
-            // Post message to set system volume (it in turn will post a message
-            // to persist).
-            // If stream is muted or we are in silent mode and stream is affected by ringer mode
-            // and the new volume is not 0, just persist the new volume but do not change
-            // current value
-            if (streamState.muteCount() == 0 &&
-                (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
-                !isStreamAffectedByRingerMode(streamType) ||
-                index == 0)) {
-                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
-                        streamState, 0);
-            } else {
-                // Post a persist volume msg
-                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
-                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+
+        // If stream is muted, set last audible index only
+        if (streamState.muteCount() != 0) {
+            streamState.setLastAudibleIndex(index);
+            // Post a persist volume msg
+            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+                    SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+        } else {
+            if (streamState.setIndex(index, lastAudible) || force) {
+                // Post message to set system volume (it in turn will post a message
+                // to persist).
+                // If we are in silent mode and stream is affected by ringer mode
+                // and the new volume is not 0, just persist the new volume but do not change
+                // current value
+                if (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
+                    !isStreamAffectedByRingerMode(streamType) ||
+                    index == 0) {
+                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
+                            streamState, 0);
+                } else {
+                    // Post a persist volume msg
+                    sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+                            SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+                }
             }
         }
     }
@@ -537,27 +561,24 @@
     private void setRingerModeInt(int ringerMode, boolean persist) {
         mRingerMode = ringerMode;
 
-        // Adjust volumes via posting message
+        // Mute stream if not previously muted by ringer mode and ringer mode
+        // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
+        // Unmute stream if previously muted by ringer mode and ringer mode
+        // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
         int numStreamTypes = AudioSystem.getNumStreamTypes();
-        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
-            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                if (!isStreamAffectedByRingerMode(streamType)) continue;
-                // Bring back last audible volume
-                setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
-                                   true, false);
-            }
-        } else {
-            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                if (isStreamAffectedByRingerMode(streamType)) {
-                    // Either silent or vibrate, either way volume is 0
-                    setStreamVolumeInt(streamType, 0, false, false);
-                } else {
-                    // restore stream volume in the case the stream changed from affected
-                    // to non affected by ringer mode. Does not arm to do it for streams that
-                    // are not affected as well.
-                    setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
-                            true, false);
+        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+            if (isStreamMutedByRingerMode(streamType)) {
+                if (!isStreamAffectedByRingerMode(streamType) ||
+                    mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+                    mStreamStates[streamType].mute(null, false);
+                    mRingerModeMutedStreams &= ~(1 << streamType);
                 }
+            } else {
+                if (isStreamAffectedByRingerMode(streamType) &&
+                    mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
+                   mStreamStates[streamType].mute(null, true);
+                   mRingerModeMutedStreams |= (1 << streamType);
+               }
             }
         }
 
@@ -687,6 +708,7 @@
             }
             if (mode != mMode) {
                 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
+                    checkForUndispatchedAudioFocusChange(mMode, mode);
                     mMode = mode;
 
                     synchronized(mSetModeDeathHandlers) {
@@ -727,7 +749,7 @@
             }
             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
             int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
-            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
+            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
         }
     }
 
@@ -861,7 +883,7 @@
             }
             streamState.mLastAudibleIndex = streamState.getValidIndex(index);
 
-            // unmute stream that whas muted but is not affect by mute anymore
+            // unmute stream that was muted but is not affect by mute anymore
             if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
                 int size = streamState.mDeathHandlers.size();
                 for (int i = 0; i < size; i++) {
@@ -1126,6 +1148,10 @@
         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
     }
 
+    private boolean isStreamMutedByRingerMode(int streamType) {
+        return (mRingerModeMutedStreams & (1 << streamType)) != 0;
+    }
+
     public boolean isStreamAffectedByMute(int streamType) {
         return (mMuteAffectedStreams & (1 << streamType)) != 0;
     }
@@ -1292,6 +1318,14 @@
             }
         }
 
+        public void setLastAudibleIndex(int index) {
+            mLastAudibleIndex = getValidIndex(index);
+        }
+
+        public void adjustLastAudibleIndex(int deltaIndex) {
+            setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
+        }
+
         public int getMaxIndex() {
             return mIndexMax;
         }
@@ -1329,7 +1363,10 @@
                         if (mMuteCount == 0) {
                             // Register for client death notification
                             try {
-                                mICallback.linkToDeath(this, 0);
+                                // mICallback can be 0 if muted by AudioService
+                                if (mICallback != null) {
+                                    mICallback.linkToDeath(this, 0);
+                                }
                                 mDeathHandlers.add(this);
                                 // If the stream is not yet muted by any client, set lvel to 0
                                 if (muteCount() == 0) {
@@ -1355,9 +1392,12 @@
                             if (mMuteCount == 0) {
                                 // Unregistr from client death notification
                                 mDeathHandlers.remove(this);
-                                mICallback.unlinkToDeath(this, 0);
+                                // mICallback can be 0 if muted by AudioService
+                                if (mICallback != null) {
+                                    mICallback.unlinkToDeath(this, 0);
+                                }
                                 if (muteCount() == 0) {
-                                    // If the stream is not mut any more, restore it's volume if
+                                    // If the stream is not muted any more, restore it's volume if
                                     // ringer mode allows it
                                     if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
                                         setIndex(mLastAudibleIndex, false);
@@ -1397,7 +1437,7 @@
                 int size = mDeathHandlers.size();
                 for (int i = 0; i < size; i++) {
                     handler = mDeathHandlers.get(i);
-                    if (cb.equals(handler.mICallback)) {
+                    if (cb == handler.mICallback) {
                         return handler;
                     }
                 }
@@ -1816,13 +1856,47 @@
     //==========================================================================================
     // AudioFocus
     //==========================================================================================
+    /**
+     * Flag to indicate that the top of the audio focus stack needs to recover focus
+     * but hasn't been signaled yet.
+     */
+    private boolean mHasUndispatchedAudioFocus = false;
+
+    private void checkForUndispatchedAudioFocusChange(int prevMode, int newMode) {
+        // when exiting a call
+        if ((prevMode == AudioSystem.MODE_IN_CALL) && (newMode != AudioSystem.MODE_IN_CALL)) {
+            // check for undispatched remote control focus gain
+            if (mHasUndispatchedAudioFocus) {
+                notifyTopOfAudioFocusStack();
+            }
+        }
+    }
+
+    private void notifyTopOfAudioFocusStack() {
+        // notify the top of the stack it gained focus
+        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+            if (canReassignAudioFocus()) {
+                try {
+                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
+                    mHasUndispatchedAudioFocus = false;
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
+                    e.printStackTrace();
+                }
+            } else {
+                mHasUndispatchedAudioFocus = true;
+            }
+        }
+    }
+
     private static class FocusStackEntry {
         public int mStreamType = -1;// no stream type
         public boolean mIsTransportControlReceiver = false;
         public IAudioFocusDispatcher mFocusDispatcher = null;
         public IBinder mSourceRef = null;
         public String mClientId;
-        public int mDurationHint;
+        public int mFocusChangeType;
 
         public FocusStackEntry() {
         }
@@ -1834,7 +1908,7 @@
             mFocusDispatcher = afl;
             mSourceRef = source;
             mClientId = id;
-            mDurationHint = duration;
+            mFocusChangeType = duration;
         }
     }
 
@@ -1851,7 +1925,7 @@
             while(stackIterator.hasNext()) {
                 FocusStackEntry fse = stackIterator.next();
                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
-                        + " -- duration: " +fse.mDurationHint);
+                        + " -- duration: " +fse.mFocusChangeType);
             }
         }
     }
@@ -1871,16 +1945,7 @@
             mFocusStack.pop();
             if (signal) {
                 // notify the new top of the stack it gained focus
-                if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)
-                        && canReassignAudioFocus()) {
-                    try {
-                        mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                                AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, " Failure to signal gain of focus due to "+ e);
-                        e.printStackTrace();
-                    }
-                }
+                notifyTopOfAudioFocusStack();
             }
         } else {
             // focus is abandoned by a client that's not at the top of the stack,
@@ -1902,8 +1967,9 @@
      * Remove focus listeners from the focus stack for a particular client.
      */
     private void removeFocusStackEntryForClient(IBinder cb) {
-        // focus is abandoned by a client that's not at the top of the stack,
-        // no need to update focus.
+        // is the owner of the audio focus part of the client to remove?
+        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
+                mFocusStack.peek().mSourceRef.equals(cb);
         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
         while(stackIterator.hasNext()) {
             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
@@ -1913,6 +1979,11 @@
                 mFocusStack.remove(fse);
             }
         }
+        if (isTopOfStackForClientToRemove) {
+            // we removed an entry at the top of the stack:
+            //  notify the new top of the stack it gained focus.
+            notifyTopOfAudioFocusStack();
+        }
     }
 
     /**
@@ -1953,7 +2024,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,18 +2041,21 @@
 
         synchronized(mFocusStack) {
             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
-                mFocusStack.peek().mDurationHint = durationHint;
-                // if focus is already owned by this client, don't do anything
-                return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                // if focus is already owned by this client and the reason for acquiring the focus
+                // hasn't changed, don't do anything
+                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                }
+                // the reason for the audio focus request has changed: remove the current top of
+                // stack and respond as if we had a new focus owner
+                mFocusStack.pop();
             }
 
             // notify current top of stack it is losing focus
             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 +2064,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/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2c5cbf6..5988d34 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -52,6 +52,7 @@
  *
  * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a>
  * documentation for additional help with using MediaRecorder.
+ * <p>Note: Currently, MediaRecorder does not work on the emulator.
  */
 public class MediaRecorder
 {
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/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 74852dc..cd7bcd5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -983,8 +983,10 @@
 // =========================================================================
 
 audio_track_cblk_t::audio_track_cblk_t()
-    : lock(Mutex::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
-    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0)
+    : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
+    userBase(0), serverBase(0), buffers(0), frameCount(0),
+    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+    flowControlFlag(1), forceReady(0)
 {
 }
 
diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
index fbb6598..553be87 100644
--- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
@@ -21,6 +21,7 @@
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 
 namespace android {
@@ -161,7 +162,14 @@
 
     buffer->set_range(0, kNumSamplesPerFrame * sizeof(int16_t));
 
-    CHECK(numBytesRead <= mInputBuffer->range_length());
+    if (numBytesRead > mInputBuffer->range_length()) {
+        // This is bad, should never have happened, but did. Abort now.
+
+        buffer->release();
+        buffer = NULL;
+
+        return ERROR_MALFORMED;
+    }
 
     mInputBuffer->set_range(
             mInputBuffer->range_offset() + numBytesRead,
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index 6d6e408..4dc96be 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -52,7 +52,7 @@
     mBufferGroup->add_buffer(new MediaBuffer(4608 * 2));
 
     mConfig->equalizerType = flat;
-    mConfig->crcEnabled = true;
+    mConfig->crcEnabled = false;
 
     uint32_t memRequirements = pvmp3_decoderMemRequirements();
     mDecoderBuf = malloc(memRequirements);
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index c05d90a..fcf506d 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -595,6 +595,9 @@
 
     static const int32_t kNumIterations = 5000;
 
+    // We are always going to seek beyond EOS in the first iteration (i == 0)
+    // followed by a linear read for the second iteration (i == 1).
+    // After that it's all random.
     for (int32_t i = 0; i < kNumIterations; ++i) {
         int64_t requestedSeekTimeUs;
         int64_t actualSeekTimeUs;
@@ -602,14 +605,14 @@
 
         double r = uniform_rand();
 
-        if (i > 0 && r < 0.5) {
+        if ((i == 1) || (i > 0 && r < 0.5)) {
             // 50% chance of just continuing to decode from last position.
 
             requestedSeekTimeUs = -1;
 
             LOGI("requesting linear read");
         } else {
-            if (i > 0 && r < 0.55) {
+            if (i == 0 || r < 0.55) {
                 // 5% chance of seeking beyond end of stream.
 
                 requestedSeekTimeUs = durationUs;
@@ -674,7 +677,15 @@
                 buffer = NULL;
             }
         } else if (actualSeekTimeUs < 0) {
-            CHECK(err != OK);
+            EXPECT(err != OK,
+                   "We attempted to seek beyond EOS and expected "
+                   "ERROR_END_OF_STREAM to be returned, but instead "
+                   "we got a valid buffer.");
+            EXPECT(err == ERROR_END_OF_STREAM,
+                   "We attempted to seek beyond EOS and expected "
+                   "ERROR_END_OF_STREAM to be returned, but instead "
+                   "we found some other error.");
+            CHECK_EQ(err, ERROR_END_OF_STREAM);
             CHECK_EQ(buffer, NULL);
         } else {
             EXPECT(err == OK,
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index b7acd96..1d69361 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -383,7 +383,7 @@
 // ----------------------------------------------------------------------------
 static int
 android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
-        jobject weak_this, jstring nativeSoLib)
+        jobject weak_this, jstring nativeSoLib, jstring engConfig)
 {
     int result = TTS_FAILURE;
 
@@ -395,6 +395,7 @@
             DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
 
     const char *nativeSoLibNativeString =  env->GetStringUTFChars(nativeSoLib, 0);
+    const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
 
     void *engine_lib_handle = dlopen(nativeSoLibNativeString,
             RTLD_NOW | RTLD_LOCAL);
@@ -409,7 +410,7 @@
 
         if (pJniStorage->mNativeSynthInterface) {
             Mutex::Autolock l(engineMutex);
-            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
+            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB, engConfigString);
         }
 
         result = TTS_SUCCESS;
@@ -422,6 +423,7 @@
     env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
 
     env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+    env->ReleaseStringUTFChars(engConfig, engConfigString);
 
     return result;
 }
@@ -482,6 +484,29 @@
     return result;
 }
 
+static int
+android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
+{
+    int result = TTS_FAILURE;
+
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
+        return result;
+    }
+
+    Mutex::Autolock l(engineMutex);
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
+
+    if (pSynthData->mNativeSynthInterface) {
+        result = pSynthData->mNativeSynthInterface->setProperty(ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
+                engineConfigNativeString, strlen(engineConfigNativeString));
+    }
+    env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
+
+    return result;
+}
 
 static int
 android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
@@ -867,6 +892,10 @@
         "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
         (void*)android_tts_SynthProxy_isLanguageAvailable
     },
+    {   "native_setConfig",
+            "(ILjava/lang/String;)I",
+            (void*)android_tts_SynthProxy_setConfig
+    },
     {   "native_setLanguage",
         "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
         (void*)android_tts_SynthProxy_setLanguage
@@ -896,7 +925,7 @@
         (void*)android_tts_SynthProxy_shutdown
     },
     {   "native_setup",
-        "(Ljava/lang/Object;Ljava/lang/String;)I",
+        "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
         (void*)android_tts_SynthProxy_native_setup
     },
     {   "native_setLowShelf",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 5f283e1..525a504 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -51,7 +51,7 @@
     public SynthProxy(String nativeSoLib, String engineConfig) {
         boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
         Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
-        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
+        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
         native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
                 PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
     }
@@ -105,10 +105,10 @@
     }
 
     /**
-     * Sets the engine configuration.
+     * Updates the engine configuration.
      */
     public int setConfig(String engineConfig) {
-        return android.speech.tts.TextToSpeech.SUCCESS;
+        return native_setConfig(mJniData, engineConfig);
     }
 
     /**
@@ -180,7 +180,8 @@
      */
     private int mJniData = 0;
 
-    private native final int native_setup(Object weak_this, String nativeSoLib);
+    private native final int native_setup(Object weak_this, String nativeSoLib,
+            String engineConfig);
 
     private native final int native_setLowShelf(boolean applyFilter, float filterGain,
             float attenuationInDb, float freqInHz, float slope);
@@ -204,6 +205,8 @@
     private native final int native_loadLanguage(int jniData, String language, String country,
             String variant);
 
+    private native final int native_setConfig(int jniData, String engineConfig);
+
     private native final int native_setSpeechRate(int jniData, int speechRate);
 
     private native final int native_setPitch(int jniData, int speechRate);
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index a555244..7fb7db0 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;
 
@@ -45,7 +46,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Xml;
@@ -149,7 +150,7 @@
                     maximumFailedPasswordsForWipe = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
                 } else {
-                    Log.w(TAG, "Unknown admin tag: " + tag);
+                    Slog.w(TAG, "Unknown admin tag: " + tag);
                 }
                 XmlUtils.skipCurrentTag(parser);
             }
@@ -164,8 +165,8 @@
                     pw.print(prefix); pw.print("  "); pw.println(pols.get(i).tag);
                 }
             }
-            pw.print(prefix); pw.print("passwordQuality=");
-                    pw.print(passwordQuality);
+            pw.print(prefix); pw.print("passwordQuality=0x");
+                    pw.print(Integer.toHexString(passwordQuality));
                     pw.print(" minimumPasswordLength=");
                     pw.println(minimumPasswordLength);
             pw.print(prefix); pw.print("maximumTimeToUnlock=");
@@ -184,7 +185,7 @@
                     int change = isPackageDisappearing(aa.info.getPackageName()); 
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
-                        Log.w(TAG, "Admin unexpectedly uninstalled: "
+                        Slog.w(TAG, "Admin unexpectedly uninstalled: "
                                 + aa.info.getComponent());
                         removed = true;
                         mAdminList.remove(i);
@@ -193,7 +194,7 @@
                             mContext.getPackageManager().getReceiverInfo(
                                     aa.info.getComponent(), 0);
                         } catch (NameNotFoundException e) {
-                            Log.w(TAG, "Admin package change removed component: "
+                            Slog.w(TAG, "Admin package change removed component: "
                                     + aa.info.getComponent());
                             removed = true;
                             mAdminList.remove(i);
@@ -307,10 +308,10 @@
         try {
             return new DeviceAdminInfo(mContext, infos.get(0));
         } catch (XmlPullParserException e) {
-            Log.w(TAG, "Bad device admin requested: " + adminName, e);
+            Slog.w(TAG, "Bad device admin requested: " + adminName, e);
             return null;
         } catch (IOException e) {
-            Log.w(TAG, "Bad device admin requested: " + adminName, e);
+            Slog.w(TAG, "Bad device admin requested: " + adminName, e);
             return null;
         }
     }
@@ -342,8 +343,6 @@
                 }
             }
             
-            out.endTag(null, "policies");
-
             if (mPasswordOwner >= 0) {
                 out.startTag(null, "password-owner");
                 out.attribute(null, "value", Integer.toString(mPasswordOwner));
@@ -356,6 +355,15 @@
                 out.endTag(null, "failed-password-attempts");
             }
             
+            if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) {
+                out.startTag(null, "active-password");
+                out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
+                out.attribute(null, "length", Integer.toString(mActivePasswordLength));
+                out.endTag(null, "active-password");
+            }
+            
+            out.endTag(null, "policies");
+
             out.endDocument();
             stream.close();
             journal.commit();
@@ -409,7 +417,7 @@
                             mAdminList.add(ap);
                         }
                     } catch (RuntimeException e) {
-                        Log.w(TAG, "Failed loading admin " + name, e);
+                        Slog.w(TAG, "Failed loading admin " + name, e);
                     }
                 } else if ("failed-password-attempts".equals(tag)) {
                     mFailedPasswordAttempts = Integer.parseInt(
@@ -419,21 +427,27 @@
                     mPasswordOwner = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("active-password".equals(tag)) {
+                    mActivePasswordQuality = Integer.parseInt(
+                            parser.getAttributeValue(null, "quality"));
+                    mActivePasswordLength = Integer.parseInt(
+                            parser.getAttributeValue(null, "length"));
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
-                    Log.w(TAG, "Unknown tag: " + tag);
+                    Slog.w(TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
         } catch (NullPointerException e) {
-            Log.w(TAG, "failed parsing " + file + " " + e);
+            Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (NumberFormatException e) {
-            Log.w(TAG, "failed parsing " + file + " " + e);
+            Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (XmlPullParserException e) {
-            Log.w(TAG, "failed parsing " + file + " " + e);
+            Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (IOException e) {
-            Log.w(TAG, "failed parsing " + file + " " + e);
+            Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (IndexOutOfBoundsException e) {
-            Log.w(TAG, "failed parsing " + file + " " + e);
+            Slog.w(TAG, "failed parsing " + file + " " + e);
         }
         try {
             if (stream != null) {
@@ -443,6 +457,20 @@
             // Ignore
         }
 
+        // Validate that what we stored for the password quality matches
+        // sufficiently what is currently set.  Note that this is only
+        // a sanity check in case the two get out of sync; this should
+        // never normally happen.
+        LockPatternUtils utils = new LockPatternUtils(mContext);
+        if (utils.getActivePasswordQuality() < mActivePasswordQuality) {
+            Slog.w(TAG, "Active password quality 0x"
+                    + Integer.toHexString(mActivePasswordQuality)
+                    + " does not match actual quality 0x"
+                    + Integer.toHexString(utils.getActivePasswordQuality()));
+            mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            mActivePasswordLength = 0;
+        }
+        
         validatePasswordOwnerLocked();
         
         long timeMs = getMaximumTimeToLock(null);
@@ -452,10 +480,23 @@
         try {
             getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
         } catch (RemoteException e) {
-            Log.w(TAG, "Failure talking with power manager", e);
+            Slog.w(TAG, "Failure talking with power manager", e);
         }
     }
 
+    static void validateQualityConstant(int quality) {
+        switch (quality) {
+            case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                return;
+        }
+        throw new IllegalArgumentException("Invalid quality constant: 0x"
+                + Integer.toHexString(quality));
+    }
+    
     void validatePasswordOwnerLocked() {
         if (mPasswordOwner >= 0) {
             boolean haveOwner = false;
@@ -466,7 +507,7 @@
                 }
             }
             if (!haveOwner) {
-                Log.w(TAG, "Previous password owner " + mPasswordOwner
+                Slog.w(TAG, "Previous password owner " + mPasswordOwner
                         + " no longer active; disabling");
                 mPasswordOwner = -1;
             }
@@ -556,15 +597,17 @@
         }
     }
     
-    public void setPasswordQuality(ComponentName who, int mode) {
+    public void setPasswordQuality(ComponentName who, int quality) {
+        validateQualityConstant(quality);
+        
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
             }
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
-            if (ap.passwordQuality != mode) {
-                ap.passwordQuality = mode;
+            if (ap.passwordQuality != quality) {
+                ap.passwordQuality = quality;
                 saveSettingsLocked();
             }
         }
@@ -692,23 +735,27 @@
                     DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
             quality = getPasswordQuality(null);
             if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                int adjQuality = LockPatternUtils.adjustPasswordMode(password, quality);
-                if (adjQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                    Log.w(TAG, "resetPassword: password does not meet quality " + quality);
+                int realQuality = LockPatternUtils.computePasswordQuality(password);
+                if (realQuality < quality) {
+                    Slog.w(TAG, "resetPassword: password quality 0x"
+                            + Integer.toHexString(quality)
+                            + " does not meet required quality 0x"
+                            + Integer.toHexString(quality));
                     return false;
                 }
-                quality = adjQuality;
+                quality = realQuality;
             }
             int length = getPasswordMinimumLength(null);
             if (password.length() < length) {
-                Log.w(TAG, "resetPassword: password does not meet length " + length);
+                Slog.w(TAG, "resetPassword: password length " + password.length()
+                        + " does not meet required length " + length);
                 return false;
             }
         }
         
         int callingUid = Binder.getCallingUid();
         if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
-            Log.w(TAG, "resetPassword: already set by another uid and not entered by user");
+            Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
             return false;
         }
         
@@ -718,11 +765,13 @@
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
             utils.saveLockPassword(password, quality);
-            int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
-                    != 0 ? callingUid : -1;
-            if (mPasswordOwner != newOwner) {
-                mPasswordOwner = newOwner;
-                saveSettingsLocked();
+            synchronized (this) {
+                int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
+                        != 0 ? callingUid : -1;
+                if (mPasswordOwner != newOwner) {
+                    mPasswordOwner = newOwner;
+                    saveSettingsLocked();
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -753,7 +802,7 @@
                     try {
                         getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
                     } catch (RemoteException e) {
-                        Log.w(TAG, "Failure talking with power manager", e);
+                        Slog.w(TAG, "Failure talking with power manager", e);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -806,7 +855,7 @@
         try {
             RecoverySystem.rebootWipeUserData(mContext);
         } catch (IOException e) {
-            Log.w(TAG, "Failed requesting data wipe", e);
+            Slog.w(TAG, "Failed requesting data wipe", e);
         }
     }
     
@@ -856,6 +905,8 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
         
+        validateQualityConstant(quality);
+        
         synchronized (this) {
             if (mActivePasswordQuality != quality || mActivePasswordLength != length
                     || mFailedPasswordAttempts != 0) {
@@ -863,10 +914,8 @@
                 try {
                     mActivePasswordQuality = quality;
                     mActivePasswordLength = length;
-                    if (mFailedPasswordAttempts != 0) {
-                        mFailedPasswordAttempts = 0;
-                        saveSettingsLocked();
-                    }
+                    mFailedPasswordAttempts = 0;
+                    saveSettingsLocked();
                     sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                             DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
                 } finally {
@@ -945,7 +994,8 @@
             }
             
             pw.println(" ");
-            pw.print("  mActivePasswordQuality="); pw.println(mActivePasswordQuality);
+            pw.print("  mActivePasswordQuality=0x");
+                    pw.println(Integer.toHexString(mActivePasswordQuality));
             pw.print("  mActivePasswordLength="); pw.println(mActivePasswordLength);
             pw.print("  mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
             pw.print("  mPasswordOwner="); pw.println(mPasswordOwner);
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 0a53e9c..81ae26f 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -48,14 +48,15 @@
  * instead of periodically.
  */
 public class EntropyService extends Binder {
-    private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat";
     private static final String TAG = "EntropyService";
     private static final int ENTROPY_WHAT = 1;
     private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000;  // 3 hrs
-    private static final String RANDOM_DEV = "/dev/urandom";
     private static final long START_TIME = System.currentTimeMillis();
     private static final long START_NANOTIME = System.nanoTime();
 
+    private final String randomDevice;
+    private final String entropyFile;
+
     /**
      * Handler that periodically updates the entropy on disk.
      */
@@ -72,6 +73,16 @@
     };
 
     public EntropyService() {
+        this(getSystemDir() + "/entropy.dat", "/dev/urandom");
+    }
+
+    /** Test only interface, not for public use */
+    public EntropyService(String entropyFile, String randomDevice) {
+        if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
+        if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
+
+        this.randomDevice = randomDevice;
+        this.entropyFile = entropyFile;
         loadInitialEntropy();
         addDeviceSpecificEntropy();
         writeEntropy();
@@ -85,7 +96,7 @@
 
     private void loadInitialEntropy() {
         try {
-            RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV);
+            RandomBlock.fromFile(entropyFile).toFile(randomDevice);
         } catch (IOException e) {
             Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
         }
@@ -93,7 +104,7 @@
 
     private void writeEntropy() {
         try {
-            RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME);
+            RandomBlock.fromFile(randomDevice).toFile(entropyFile);
         } catch (IOException e) {
             Slog.w(TAG, "unable to write entropy", e);
         }
@@ -116,7 +127,7 @@
     private void addDeviceSpecificEntropy() {
         PrintWriter out = null;
         try {
-            out = new PrintWriter(new FileOutputStream(RANDOM_DEV));
+            out = new PrintWriter(new FileOutputStream(randomDevice));
             out.println("Copyright (C) 2009 The Android Open Source Project");
             out.println("All Your Randomness Are Belong To Us");
             out.println(START_TIME);
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/MountService.java b/services/java/com/android/server/MountService.java
index d50f591..a4703de 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -135,17 +135,6 @@
     private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
     private static final int MAX_UNMOUNT_RETRIES = 4;
 
-    private IntentFilter mPmFilter = new IntentFilter(
-            Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-    private BroadcastReceiver mPmReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
-            }
-        }
-    };
-
     class UnmountCallBack {
         String path;
         int retries;
@@ -200,49 +189,35 @@
 
     class MountServiceHandler extends Handler {
         ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
-        boolean mRegistered = false;
+        boolean mUpdatingStatus = false;
 
         MountServiceHandler(Looper l) {
             super(l);
         }
 
-        void registerReceiver() {
-            mRegistered = true;
-            if (DEBUG_UNMOUNT) Log.i(TAG, "Registering receiver");
-            mContext.registerReceiver(mPmReceiver, mPmFilter);
-        }
-
-        void unregisterReceiver() {
-            mRegistered = false;
-            if (DEBUG_UNMOUNT) Log.i(TAG, "Unregistering receiver");
-            mContext.unregisterReceiver(mPmReceiver);
-        }
-
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case H_UNMOUNT_PM_UPDATE: {
                     if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_UPDATE");
                     UnmountCallBack ucb = (UnmountCallBack) msg.obj;
                     mForceUnmounts.add(ucb);
-                    if (DEBUG_UNMOUNT) Log.i(TAG, " registered = " + mRegistered);
+                    if (DEBUG_UNMOUNT) Log.i(TAG, " registered = " + mUpdatingStatus);
                     // Register only if needed.
-                    if (!mRegistered) {
-                        registerReceiver();
-                        if (DEBUG_UNMOUNT) Log.i(TAG, "Updating external media status");
-                        boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
-                        if (!hasExtPkgs) {
-                            // Unregister right away
-                            mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
-                        }
+                    if (!mUpdatingStatus) {
+                        if (DEBUG_UNMOUNT) Log.i(TAG, "Updating external media status on PackageManager");
+                        mUpdatingStatus = true;
+                        mPms.updateExternalMediaStatus(false, true);
                     }
                     break;
                 }
                 case H_UNMOUNT_PM_DONE: {
                     if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_DONE");
-                    // Unregister now.
-                    if (mRegistered) {
-                        unregisterReceiver();
+                    if (!mUpdatingStatus) {
+                        // Does not correspond to unmount's status update.
+                        return;
                     }
+                    if (DEBUG_UNMOUNT) Log.i(TAG, "Updated status. Processing requests");
+                    mUpdatingStatus = false;
                     int size = mForceUnmounts.size();
                     int sizeArr[] = new int[size];
                     int sizeArrN = 0;
@@ -261,7 +236,7 @@
                                 ActivityManagerService ams = (ActivityManagerService)
                                 ServiceManager.getService("activity");
                                 // Eliminate system process here?
-                                boolean ret = ams.killPidsForMemory(pids);
+                                boolean ret = ams.killPids(pids, "Unmount media");
                                 if (ret) {
                                     // Confirm if file references have been freed.
                                     pids = getStorageUsers(path);
@@ -277,8 +252,8 @@
                                     ucb));
                         } else {
                             if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
-                                Log.i(TAG, "Cannot unmount inspite of " +
-                                        MAX_UNMOUNT_RETRIES + " to unmount media");
+                                Log.i(TAG, "Cannot unmount media inspite of " +
+                                        MAX_UNMOUNT_RETRIES + " retries");
                                 // Send final broadcast indicating failure to unmount.                 
                             } else {
                                 mHandler.sendMessageDelayed(
@@ -337,16 +312,23 @@
                     public void run() {
                         try {
                             String path = Environment.getExternalStorageDirectory().getPath();
-                            if (getVolumeState(
-                                    Environment.getExternalStorageDirectory().getPath()).equals(
-                                            Environment.MEDIA_UNMOUNTED)) {
+                            String state = getVolumeState(path);
+
+                            if (state.equals(Environment.MEDIA_UNMOUNTED)) {
                                 int rc = doMountVolume(path);
                                 if (rc != StorageResultCode.OperationSucceeded) {
                                     Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
                                 }
+                            } else if (state.equals(Environment.MEDIA_SHARED)) {
+                                /*
+                                 * Bootstrap UMS enabled state since vold indicates
+                                 * the volume is shared (runtime restart while ums enabled)
+                                 */
+                                notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
                             }
+
                             /*
-                             * If UMS is connected in boot, send the connected event
+                             * If UMS was connected on boot, send the connected event
                              * now that we're up.
                              */
                             if (mSendUmsConnectedOnBoot) {
@@ -405,9 +387,9 @@
         }
         // Update state on PackageManager
         if (Environment.MEDIA_UNMOUNTED.equals(state)) {
-            mPms.updateExternalMediaStatus(false);
+            mPms.updateExternalMediaStatus(false, false);
         } else if (Environment.MEDIA_MOUNTED.equals(state)) {
-            mPms.updateExternalMediaStatus(true);
+            mPms.updateExternalMediaStatus(true, false);
         }
         String oldState = mLegacyState;
         mLegacyState = state;
@@ -750,19 +732,15 @@
         if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
             return VoldResponseCode.OpFailedVolNotMounted;
         }
-
-        // We unmounted the volume. No of the asec containers are available now.
-        synchronized (mAsecMountSet) {
-            mAsecMountSet.clear();
-        }
-        // Notify PackageManager of potential media removal and deal with
-        // return code later on. The caller of this api should be aware or have been
-        // notified that the applications installed on the media will be killed.
         // Redundant probably. But no harm in updating state again.
-        mPms.updateExternalMediaStatus(false);
+        mPms.updateExternalMediaStatus(false, false);
         try {
             mConnector.doCommand(String.format(
                     "volume unmount %s%s", path, (force ? " force" : "")));
+            // We unmounted the volume. None of the asec containers are available now.
+            synchronized (mAsecMountSet) {
+                mAsecMountSet.clear();
+            }
             return StorageResultCode.OperationSucceeded;
         } catch (NativeDaemonConnectorException e) {
             // Don't worry about mismatch in PackageManager since the
@@ -1225,7 +1203,10 @@
         try {
             mConnector.doCommand(cmd);
         } catch (NativeDaemonConnectorException e) {
-            rc = StorageResultCode.OperationFailedInternalError;
+            int code = e.getCode();
+            if (code != VoldResponseCode.OpFailedStorageBusy) {
+                rc = StorageResultCode.OperationFailedInternalError;
+            }
         }
 
         if (rc == StorageResultCode.OperationSucceeded) {
@@ -1325,5 +1306,9 @@
         Log.e(TAG, "Got an empty response");
         return "";
     }
+
+    public void finishMediaUpdate() {
+        mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+    }
 }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b114ca2..a3f2e09 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -460,17 +460,17 @@
         throw new IllegalStateException("Got an empty response");
     }
 
-    public void startAccessPoint(WifiConfiguration wifiConfig, String intf)
+    public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
              throws IllegalStateException {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
         mContext.enforceCallingOrSelfPermission(
-            android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
-        mConnector.doCommand(String.format("softap stop " + intf));
-        mConnector.doCommand(String.format("softap fwreload " + intf + " AP"));
-        mConnector.doCommand(String.format("softap start " + intf));
+                android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+        mConnector.doCommand(String.format("softap stop " + wlanIface));
+        mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
+        mConnector.doCommand(String.format("softap start " + wlanIface));
         if (wifiConfig == null) {
-            mConnector.doCommand(String.format("softap set " + intf + " wl0.1"));
+            mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
         } else {
             /**
              * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
@@ -482,10 +482,9 @@
              * argv6 - Channel
              * argv7 - Preamble
              * argv8 - Max SCB
-             *
-             * TODO: get a configurable softap interface from driver
              */
-            String str = String.format("softap set " + intf + " wl0.1 %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);
@@ -498,8 +497,25 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
         mContext.enforceCallingOrSelfPermission(
-            android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+                android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
         mConnector.doCommand("softap stopap");
     }
 
+    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
+            throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mContext.enforceCallingOrSelfPermission(
+            android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+        if (wifiConfig == null) {
+            mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+        } else {
+            String str = String.format("softap set " + wlanIface + " " + softapIface +
+                                       " \"%s\" %s %s", wifiConfig.SSID,
+                                       wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+                                       "wpa2-psk" : "open",
+                                       wifiConfig.preSharedKey);
+            mConnector.doCommand(str);
+        }
+    }
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 87a744e..f9e1963 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;
@@ -343,6 +343,7 @@
     static final int POST_INSTALL = 9;
     static final int MCS_RECONNECT = 10;
     static final int MCS_GIVE_UP = 11;
+    static final int UPDATED_MEDIA_STATUS = 12;
 
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
@@ -596,6 +597,13 @@
                         Slog.e(TAG, "Bogus post-install token " + msg.arg1);
                     }
                 } break;
+                case UPDATED_MEDIA_STATUS: {
+                    try {
+                        PackageHelper.getMountService().finishMediaUpdate();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "MountService not running?");
+                    }
+                } break;
             }
         }
     }
@@ -851,7 +859,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 +868,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 +2721,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 +2911,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 +2930,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 +2966,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.
@@ -3983,7 +3977,7 @@
                             && ps.permissionsFixed) {
                         // If this is an existing, non-system package, then
                         // we can't add any new permissions to it.
-                        if (!allowedSig && !gp.loadedPermissions.contains(perm)) {
+                        if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
                             allowed = false;
                             // Except...  if this is a permission that was added
                             // to the platform (note: need to only do this when
@@ -3995,7 +3989,7 @@
                                 if (npi.name.equals(perm)
                                         && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
                                     allowed = true;
-                                    Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg "
+                                    Log.i(TAG, "Auto-granting " + perm + " to old pkg "
                                             + pkg.packageName);
                                     break;
                                 }
@@ -4043,7 +4037,6 @@
             // permissions we have now selected are fixed until explicitly
             // changed.
             ps.permissionsFixed = true;
-            gp.loadedPermissions = new HashSet<String>(gp.grantedPermissions);
         }
     }
     
@@ -4500,7 +4493,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 +4695,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 {
@@ -6954,12 +6947,6 @@
                             pw.print("      "); pw.println(s);
                         }
                     }
-                    if (ps.loadedPermissions.size() > 0) {
-                        pw.println("    loadedPermissions:");
-                        for (String s : ps.loadedPermissions) {
-                            pw.print("      "); pw.println(s);
-                        }
-                    }
                 }
             }
             printedSomething = false;
@@ -7028,10 +7015,6 @@
                     for (String s : su.grantedPermissions) {
                         pw.print("      "); pw.println(s);
                     }
-                    pw.println("    loadedPermissions:");
-                    for (String s : su.loadedPermissions) {
-                        pw.print("      "); pw.println(s);
-                    }
                 }
             }
             
@@ -7534,8 +7517,6 @@
         HashSet<String> grantedPermissions = new HashSet<String>();
         int[] gids;
 
-        HashSet<String> loadedPermissions = new HashSet<String>();
-
         GrantedPermissions(int pkgFlags) {
             setFlags(pkgFlags);
         }
@@ -7635,7 +7616,6 @@
         public void copyFrom(PackageSettingBase base) {
             grantedPermissions = base.grantedPermissions;
             gids = base.gids;
-            loadedPermissions = base.loadedPermissions;
 
             timeStamp = base.timeStamp;
             timeStampString = base.timeStampString;
@@ -8069,7 +8049,6 @@
                             p.userId = dis.userId;
                             // Clone permissions
                             p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
-                            p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
                             // Clone component info
                             p.disabledComponents = new HashSet<String>(dis.disabledComponents);
                             p.enabledComponents = new HashSet<String>(dis.enabledComponents);
@@ -8174,7 +8153,7 @@
                 }
                 for (PackageSetting pkg:sus.packages) {
                     if (pkg.pkg != null &&
-                            !pkg.pkg.packageName.equalsIgnoreCase(deletedPs.pkg.packageName) &&
+                            !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
                             pkg.pkg.requestedPermissions.contains(eachPerm)) {
                         used = true;
                         break;
@@ -8183,7 +8162,6 @@
                 if (!used) {
                     // can safely delete this permission from list
                     sus.grantedPermissions.remove(eachPerm);
-                    sus.loadedPermissions.remove(eachPerm);
                 }
             }
             // Update gids
@@ -8294,6 +8272,7 @@
                         return;
                     }
                 } else {
+                    mSettingsFilename.delete();
                     Slog.w(TAG, "Preserving older settings backup");
                 }
             }
@@ -9057,7 +9036,7 @@
                         packageSetting.signatures.readXml(parser, mPastSignatures);
                     } else if (tagName.equals("perms")) {
                         readGrantedPermissionsLP(parser,
-                                packageSetting.loadedPermissions);
+                                packageSetting.grantedPermissions);
                         packageSetting.permissionsFixed = true;
                     } else {
                         reportSettingsProblem(Log.WARN,
@@ -9186,7 +9165,7 @@
                     if (tagName.equals("sigs")) {
                         su.signatures.readXml(parser, mPastSignatures);
                     } else if (tagName.equals("perms")) {
-                        readGrantedPermissionsLP(parser, su.loadedPermissions);
+                        readGrantedPermissionsLP(parser, su.grantedPermissions);
                     } else {
                         reportSettingsProblem(Log.WARN,
                                 "Unknown element under <shared-user>: "
@@ -9386,10 +9365,12 @@
    }
 
    /*
-    * Return true if PackageManager does have packages to be updated.
+    * Update media status on PackageManager.
     */
-   public boolean updateExternalMediaStatus(final boolean mediaStatus) {
-       final boolean ret;
+   public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
+       if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+           throw new SecurityException("Media status can only be updated by the system");
+       }
        synchronized (mPackages) {
            Log.i(TAG, "Updating external media status from " +
                    (mMediaMounted ? "mounted" : "unmounted") + " to " +
@@ -9397,32 +9378,29 @@
            if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
                    mediaStatus+", mMediaMounted=" + mMediaMounted);
            if (mediaStatus == mMediaMounted) {
-               return false;
+               if (reportStatus) {
+                   mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+               }
+               return;
            }
            mMediaMounted = mediaStatus;
-           Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE);
-           ret = appList != null && appList.size() > 0;
-           if (DEBUG_SD_INSTALL) {
-               if (appList != null) {
-                   for (String app : appList) {
-                       Log.i(TAG, "Should enable " + app + " on sdcard");
-                   }
-               }
-           }
-           if (DEBUG_SD_INSTALL)  Log.i(TAG, "updateExternalMediaStatus returning " + ret);
        }
        // Queue up an async operation since the package installation may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
-               updateExternalMediaStatusInner(mediaStatus, ret);
+               try {
+                   updateExternalMediaStatusInner(mediaStatus);
+               } finally {
+                   if (reportStatus) {
+                       mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+                   }
+               }
            }
        });
-       return ret;
    }
 
-   private void updateExternalMediaStatusInner(boolean mediaStatus,
-           boolean sendUpdateBroadcast) {
+   private void updateExternalMediaStatusInner(boolean mediaStatus) {
        // If we are up here that means there are packages to be
        // enabled or disabled.
        final String list[] = PackageHelper.getSecureContainerList();
@@ -9487,11 +9465,11 @@
        // Process packages with valid entries.
        if (mediaStatus) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
-           loadMediaPackages(processCids, uidArr, sendUpdateBroadcast, removeCids);
+           loadMediaPackages(processCids, uidArr, removeCids);
            startCleaningPackages();
        } else {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
-           unloadMediaPackages(processCids, uidArr, sendUpdateBroadcast);
+           unloadMediaPackages(processCids, uidArr);
        }
    }
 
@@ -9522,8 +9500,7 @@
     * to avoid unnecessary crashes.
     */
    private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
-           int uidArr[], boolean sendUpdateBroadcast,
-           HashSet<String> removeCids) {
+           int uidArr[], HashSet<String> removeCids) {
        ArrayList<String> pkgList = new ArrayList<String>();
        Set<SdInstallArgs> keys = processCids.keySet();
        boolean doGc = false;
@@ -9589,7 +9566,7 @@
            mSettings.writeLP();
        }
        // Send a broadcast to let everyone know we are done processing
-       if (sendUpdateBroadcast) {
+       if (pkgList.size() > 0) {
            sendResourcesChangedBroadcast(true, pkgList, uidArr);
        }
        if (doGc) {
@@ -9605,7 +9582,7 @@
    }
 
    private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
-           int uidArr[], boolean sendUpdateBroadcast) {
+           int uidArr[]) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
        ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
@@ -9628,7 +9605,7 @@
            }
        }
        // Send broadcasts
-       if (sendUpdateBroadcast) {
+       if (pkgList.size() > 0) {
            sendResourcesChangedBroadcast(false, pkgList, uidArr);
        }
        // Force gc
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index d72416d..848997b 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -571,6 +571,7 @@
             binder = b;
             tag = t;
             uid = u == MY_UID ? Process.SYSTEM_UID : u;
+            pid = Binder.getCallingPid();
             if (u != MY_UID || (
                     !"KEEP_SCREEN_ON_FLAG".equals(tag)
                     && !"KeyInputQueue".equals(tag))) {
@@ -595,6 +596,7 @@
         final IBinder binder;
         final String tag;
         final int uid;
+        final int pid;
         final int monitorType;
         boolean activated = true;
         int minState;
@@ -998,7 +1000,8 @@
                    activated = " activated";
                 }
                 pw.println("  " + type + " '" + wl.tag + "'" + acquireCausesWakeup
-                        + activated + " (minState=" + wl.minState + ")");
+                        + activated + " (minState=" + wl.minState + ", uid=" + wl.uid
+                        + ", pid=" + wl.pid + ")");
             }
 
             pw.println();
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 4eb529c..35b250e 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -96,6 +96,8 @@
     private static final boolean DBG = false;
     private static final Pattern scanResultPattern = Pattern.compile("\t+");
     private final WifiStateTracker mWifiStateTracker;
+    /* TODO: fetch a configurable interface */
+    private static final String SOFTAP_IFACE = "wl0.1";
 
     private Context mContext;
     private int mWifiApState;
@@ -308,13 +310,17 @@
                         }
                     } catch (Exception e) {
                         Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+                        try {
+                            nwService.stopAccessPoint();
+                        } catch (Exception ee) {
+                            Slog.e(TAG, "Could not stop AP, :" + ee);
+                        }
                         setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
                         return;
                     }
 
                     if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
                         Slog.e(TAG, "Error tethering "+intf);
-                        setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
                     }
                     break;
                 }
@@ -578,9 +584,10 @@
     }
 
     /**
-     * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)}
+     * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
      * @param wifiConfig SSID, security and channel details as
      *        part of WifiConfiguration
+     * @param enabled, true to enable and false to disable
      * @return {@code true} if the start operation was
      *         started or is already in the queue.
      */
@@ -652,11 +659,16 @@
             if(enable && (wifiConfig != null)) {
                 try {
                     persistApConfiguration(wifiConfig);
-                    nwService.stopAccessPoint();
-                    nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
+                    nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
+                                             SOFTAP_IFACE);
                     return true;
                 } catch(Exception e) {
                     Slog.e(TAG, "Exception in nwService during AP restart");
+                    try {
+                        nwService.stopAccessPoint();
+                    } catch (Exception ee) {
+                        Slog.e(TAG, "Could not stop AP, :" + ee);
+                    }
                     setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
                     return false;
                 }
@@ -692,7 +704,8 @@
             }
 
             try {
-                nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
+                nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
+                                           SOFTAP_IFACE);
             } catch(Exception e) {
                 Slog.e(TAG, "Exception in startAccessPoint()");
                 setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
@@ -1884,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/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 93e45fc..fdb67f8 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -10755,7 +10755,7 @@
                         pids[i] = pidCandidates.keyAt(i);
                     }
                     try {
-                        if (mActivityManager.killPidsForMemory(pids)) {
+                        if (mActivityManager.killPids(pids, "Free memory")) {
                             killedApps = true;
                         }
                     } catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2ecebed..7034c88 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8359,11 +8359,11 @@
         }
     }
 
-    public boolean killPidsForMemory(int[] pids) {
+    public boolean killPids(int[] pids, String pReason) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("killPidsForMemory only available to the system");
+            throw new SecurityException("killPids only available to the system");
         }
-        
+        String reason = (pReason == null) ? "Unknown" : pReason;
         // XXX Note: don't acquire main activity lock here, because the window
         // manager calls in with its locks held.
         
@@ -8387,7 +8387,7 @@
             if (worstType < EMPTY_APP_ADJ && worstType > HIDDEN_APP_MIN_ADJ) {
                 worstType = HIDDEN_APP_MIN_ADJ;
             }
-            Slog.w(TAG, "Killing processes for memory at adjustment " + worstType);
+            Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
             for (int i=0; i<pids.length; i++) {
                 ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
                 if (proc == null) {
@@ -8395,10 +8395,10 @@
                 }
                 int adj = proc.setAdj;
                 if (adj >= worstType) {
-                    Slog.w(TAG, "Killing for memory: " + proc + " (adj "
+                    Slog.w(TAG, "Killing " + reason + " : " + proc + " (adj "
                             + adj + ")");
-                    EventLog.writeEvent(EventLogTags.AM_KILL_FOR_MEMORY, proc.pid,
-                            proc.processName, adj);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, proc.pid,
+                            proc.processName, adj, reason);
                     killed = true;
                     Process.killProcess(pids[i]);
                 }
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/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 0ddcc247..aadd37d 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -58,7 +58,7 @@
 # The activity's onResume has been called.
 30022 am_on_resume_called (Component Name|3)
 # Kill a process to reclaim memory.
-30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5)
+30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
 # Discard an undelivered serialized broadcast (timeout/ANR/crash)
 30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
 30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
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/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 44e0dad..55840e2 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -991,10 +991,10 @@
             // asu = 0 (-113dB or less) is very weak
             // signal, its better to show 0 bars to the user in such cases.
             // asu = 99 is a special case, where the signal strength is unknown.
-            if (asu <= 0 || asu == 99) iconLevel = 0;
-            else if (asu >= 16) iconLevel = 4;
+            if (asu <= 2 || asu == 99) iconLevel = 0;
+            else if (asu >= 12) iconLevel = 4;
             else if (asu >= 8)  iconLevel = 3;
-            else if (asu >= 4)  iconLevel = 2;
+            else if (asu >= 5)  iconLevel = 2;
             else iconLevel = 1;
 
             // Though mPhone is a Manager, this call is not an IPC
diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
new file mode 100644
index 0000000..636ba21
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+/**
+ * Tests for {@link com.android.server.EntropyService}
+ */
+public class EntropyServiceTest extends AndroidTestCase {
+
+    public void testInitialWrite() throws Exception {
+        File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
+        File file = File.createTempFile("testInitialWrite", "dat", dir);
+        file.deleteOnExit();
+        assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
+
+        // The constructor has the side effect of writing to file
+        new EntropyService("/dev/null", file.getCanonicalPath());
+
+        assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
+    }
+}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index b8ea4c0..a284ea5 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -237,8 +237,8 @@
 
     /*
      * TODO(cleanup): It would make some sense if the result of
-     * preprocessing a message to determine the proper encoding (ie
-     * the resulting datastructure from calculateLength) could be
+     * preprocessing a message to determine the proper encoding (i.e.
+     * the resulting data structure from calculateLength) could be
      * passed as an argument to the actual final encoding function.
      * This would better ensure that the logic behind size calculation
      * actually matched the encoding.
@@ -427,7 +427,7 @@
      * @param destinationAddress the address of the destination for the message
      * @param destinationPort the port to deliver the message to at the
      *        destination
-     * @param data the dat for the message
+     * @param data the data for the message
      * @return a <code>SubmitPdu</code> containing the encoded SC
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ae5b1de..f018d107 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -46,7 +46,7 @@
  * {@link android.content.Context#getSystemService
  * Context.getSystemService(Context.TELEPHONY_SERVICE)}.
  * <p>
- * Note that acess to some telephony information is
+ * Note that access to some telephony information is
  * permission-protected. Your application cannot access the protected
  * information unless it has the appropriate permissions declared in
  * its manifest file. Where permissions apply, they are noted in the
@@ -356,7 +356,7 @@
     }
 
     /**
-     * Returns the ISO country code equivilent of the current registered
+     * Returns the ISO country code equivalent of the current registered
      * operator's MCC (Mobile Country Code).
      * <p>
      * Availability: Only when user is registered to a network. Result may be
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 6eea46e..d3a34ec 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -202,7 +202,7 @@
     /**
      * Supply the ICC PIN to the ICC
      *
-     * When the operation is complete, onComplete will be sent to it's
+     * When the operation is complete, onComplete will be sent to its
      * Handler.
      *
      * onComplete.obj will be an AsyncResult
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 358af95..a8f4143 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -585,7 +585,7 @@
 
     /**
      * Utility code to set the system locale if it's not set already
-     * @param langauge Two character language code desired
+     * @param language Two character language code desired
      * @param country Two character country code desired
      *
      *  {@hide}
@@ -694,22 +694,22 @@
 
     public void setTTYMode(int ttyMode, Message onComplete) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("setTTYMode");
     }
 
     public void queryTTYMode(Message onComplete) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("queryTTYMode");
     }
 
     public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("enableEnhancedVoicePrivacy");
     }
 
     public void getEnhancedVoicePrivacy(Message onComplete) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("getEnhancedVoicePrivacy");
     }
 
     public void setBandMode(int bandMode, Message response) {
@@ -754,7 +754,7 @@
      * Returns the CDMA ERI icon index to display
      */
     public int getCdmaEriIconIndex() {
-        Log.e(LOG_TAG, "Error! getCdmaEriIconIndex should never be executed in GSM mode");
+        logUnexpectedCdmaMethodCall("getCdmaEriIconIndex");
         return -1;
     }
 
@@ -764,7 +764,7 @@
      * 1 - FLASHING
      */
     public int getCdmaEriIconMode() {
-        Log.e(LOG_TAG, "Error! getCdmaEriIconMode should never be executed in GSM mode");
+        logUnexpectedCdmaMethodCall("getCdmaEriIconMode");
         return -1;
     }
 
@@ -772,82 +772,82 @@
      * Returns the CDMA ERI text,
      */
     public String getCdmaEriText() {
-        Log.e(LOG_TAG, "Error! getCdmaEriText should never be executed in GSM mode");
+        logUnexpectedCdmaMethodCall("getCdmaEriText");
         return "GSM nw, no ERI";
     }
 
     public String getCdmaMin() {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("getCdmaMin");
         return null;
     }
 
     public boolean isMinInfoReady() {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("isMinInfoReady");
         return false;
     }
 
     public String getCdmaPrlVersion(){
         //  This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("getCdmaPrlVersion");
         return null;
     }
 
     public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("sendBurstDtmf");
     }
 
     public void exitEmergencyCallbackMode() {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("exitEmergencyCallbackMode");
     }
 
     public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("registerForCdmaOtaStatusChange");
     }
 
     public void unregisterForCdmaOtaStatusChange(Handler h) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("unregisterForCdmaOtaStatusChange");
     }
 
     public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("registerForSubscriptionInfoReady");
     }
 
     public void unregisterForSubscriptionInfoReady(Handler h) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("unregisterForSubscriptionInfoReady");
     }
 
     public  boolean isOtaSpNumber(String dialStr) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("isOtaSpNumber");
         return false;
     }
 
     public void registerForCallWaiting(Handler h, int what, Object obj){
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("registerForCallWaiting");
     }
 
     public void unregisterForCallWaiting(Handler h){
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("unregisterForCallWaiting");
     }
 
     public void registerForEcmTimerReset(Handler h, int what, Object obj) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("registerForEcmTimerReset");
     }
 
     public void unregisterForEcmTimerReset(Handler h) {
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+        logUnexpectedCdmaMethodCall("unregisterForEcmTimerReset");
     }
 
     public void registerForSignalInfo(Handler h, int what, Object obj) {
@@ -908,12 +908,12 @@
 
      public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
          // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-         Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+         logUnexpectedCdmaMethodCall("setOnEcbModeExitResponse");
      }
 
      public void unsetOnEcbModeExitResponse(Handler h){
         // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-         Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+         logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
      }
 
     public String getInterfaceName(String apnType) {
@@ -984,7 +984,7 @@
     }
 
     /**
-     * Notifiy registrants of a new ringing Connection.
+     * Notify registrants of a new ringing Connection.
      * Subclasses of Phone probably want to replace this with a
      * version scoped to their packages
      */
@@ -1017,4 +1017,13 @@
                     + " mCallRingContinueToken=" + mCallRingContinueToken);
         }
     }
+
+    /**
+     * Common error logger method for unexpected calls to CDMA-only methods.
+     */
+    private void logUnexpectedCdmaMethodCall(String name)
+    {
+        Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
+                "called, CDMAPhone inactive.");
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 4a4282fc..764d12e 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -109,6 +109,12 @@
     /** Stop the sending */
     static final protected int EVENT_STOP_SENDING = 10;
 
+    /** Memory status reporting is acknowledged by RIL */
+    static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11;
+
+    /** Radio is ON */
+    static final protected int EVENT_RADIO_ON = 12;
+
     protected Phone mPhone;
     protected Context mContext;
     protected ContentResolver mResolver;
@@ -152,6 +158,7 @@
     private SmsMessageBase.SubmitPduBase mSubmitPduBase;
 
     protected boolean mStorageAvailable = true;
+    protected boolean mReportMemoryStatusPending = false;
 
     protected static int getNextConcatenatedRef() {
         sConcatenatedRef += 1;
@@ -235,6 +242,7 @@
         mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);
         mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
         mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
+        mCm.registerForOn(this, EVENT_RADIO_ON, null);
 
         // Don't always start message ref at 0.
         sConcatenatedRef = new Random().nextInt(256);
@@ -253,6 +261,7 @@
         mCm.unSetOnNewSMS(this);
         mCm.unSetOnSmsStatus(this);
         mCm.unSetOnIccSmsFull(this);
+        mCm.unregisterForOn(this);
     }
 
     protected void finalize() {
@@ -370,6 +379,26 @@
                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
             }
             break;
+
+        case EVENT_REPORT_MEMORY_STATUS_DONE:
+            ar = (AsyncResult)msg.obj;
+            if (ar.exception != null) {
+                mReportMemoryStatusPending = true;
+                Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
+                        + mStorageAvailable);
+            } else {
+                mReportMemoryStatusPending = false;
+            }
+            break;
+
+        case EVENT_RADIO_ON:
+            if (mReportMemoryStatusPending) {
+                Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
+                        + mStorageAvailable);
+                mCm.reportSmsMemoryStatus(mStorageAvailable,
+                        obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            }
+            break;
         }
     }
 
@@ -940,10 +969,10 @@
             public void onReceive(Context context, Intent intent) {
                 if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
                     mStorageAvailable = false;
-                    mCm.reportSmsMemoryStatus(false, null);
+                    mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
                 } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
                     mStorageAvailable = true;
-                    mCm.reportSmsMemoryStatus(true, null);
+                    mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
                 } else {
                     // Assume the intent is one of the SMS receive intents that
                     // was sent as an ordered broadcast.  Check result and ACK.
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..2f801cc 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 009c0f2..6975c70 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -86,14 +86,16 @@
         fail(errMsg);
     }
     void failStr(Exception e) {
-        Log.w(TAG, "e.getMessage="+e.getMessage());
-        Log.w(TAG, "e="+e);
+        failStr(e.getMessage());
     }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mOrigState = getMediaState();
+        if (!mountMedia()) {
+            Log.i(TAG, "sdcard not mounted? Some of these tests might fail");
+        }
     }
 
     @Override
@@ -297,6 +299,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 +337,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 +364,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 +429,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 +1109,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 +1117,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 +1173,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 +1850,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 +1889,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 +1948,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 +2159,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/ConnectivityManagerTest/Android.mk b/tests/ConnectivityManagerTest/Android.mk
new file mode 100644
index 0000000..bd773d0
--- /dev/null
+++ b/tests/ConnectivityManagerTest/Android.mk
@@ -0,0 +1,30 @@
+# Copyright 2010, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ConnectivityManagerTest
+
+#LOCAL_INSTRUMENTATION_FOR := connectivitymanagertest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ConnectivityManagerTest/AndroidManifest.xml b/tests/ConnectivityManagerTest/AndroidManifest.xml
new file mode 100644
index 0000000..76b58e1
--- /dev/null
+++ b/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.connectivitymanagertest">
+
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="ConnectivityManagerTestActivity"
+          android:label="CMTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.TEST" />
+            </intent-filter>
+        </activity>
+    </application>
+    <!--
+    This declares that this app uses the instrumentation test runner targeting
+    the package of browserpowertest. To run the tests use the command:
+    "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner"
+    -->
+    <instrumentation android:name=".ConnectivityManagerTestRunner"
+        android:targetPackage="com.android.connectivitymanagertest"
+        android:label="Test runner for Connectivity Manager Tests"
+    />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+</manifest>
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
new file mode 100644
index 0000000..1dffa02
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -0,0 +1,329 @@
+package com.android.connectivitymanagertest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import java.util.List;
+import android.widget.LinearLayout;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+
+/**
+ * An activity registered with connectivity manager broadcast
+ * provides network connectivity information and
+ * can be used to set device states: Cellular, Wifi, Airplane mode.
+ */
+public class ConnectivityManagerTestActivity extends Activity {
+
+    public static final String LOG_TAG = "ConnectivityManagerTestActivity";
+    public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds
+    public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+    public ConnectivityReceiver mConnectivityReceiver = null;
+    public WifiReceiver mWifiReceiver = null;
+    /*
+     * Track network connectivity information
+     */
+    public State mState;
+    public NetworkInfo mNetworkInfo;
+    public NetworkInfo mOtherNetworkInfo;
+    public boolean mIsFailOver;
+    public String mReason;
+    public boolean mScanResultIsAvailable = false;
+    public ConnectivityManager mCM;
+
+    /*
+     * Control Wifi States
+     */
+    public WifiManager mWifiManager;
+
+    /*
+     * Verify connectivity state
+     */
+    public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE;
+    NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
+
+    /**
+     * A wrapper of a broadcast receiver which provides network connectivity information
+     * for all kinds of network: wifi, mobile, etc.
+     */
+    private class ConnectivityReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
+                return;
+            }
+
+            boolean noConnectivity =
+                intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+
+            if (noConnectivity) {
+                mState = State.DISCONNECTED;
+            } else {
+                mState = State.CONNECTED;
+            }
+
+            mNetworkInfo = (NetworkInfo)
+                intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+
+            mOtherNetworkInfo = (NetworkInfo)
+                intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+
+            mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
+            mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
+            recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
+            if (mOtherNetworkInfo != null) {
+                recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
+            }
+        }
+    }
+
+    private class WifiReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (!action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                Log.v(LOG_TAG, "onReceive() is calleld with " + intent);
+                return;
+            }
+            notifyScanResult();
+        }
+    }
+
+    public ConnectivityManagerTestActivity() {
+        mState = State.UNKNOWN;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+
+        // Create a simple layout
+        LinearLayout contentView = new LinearLayout(this);
+        contentView.setOrientation(LinearLayout.VERTICAL);
+        setContentView(contentView);
+        setTitle("ConnectivityManagerTestActivity");
+
+        mConnectivityReceiver = new ConnectivityReceiver();
+        // register a connectivity receiver for CONNECTIVITY_ACTION;
+        registerReceiver(mConnectivityReceiver,
+                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+        mWifiReceiver = new WifiReceiver();
+        registerReceiver(mWifiReceiver,
+                new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+        // Get an instance of ConnectivityManager
+        mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+        // Get an instance of WifiManager
+        mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
+        initializeNetworkStates();
+
+        if (mWifiManager.isWifiEnabled()) {
+            Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+            clearWifi();
+        }
+     }
+
+    // for each network type, initialize network states to UNKNOWN, and no verification flag is set
+    public void initializeNetworkStates() {
+        for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
+            connectivityState[networkType] =  new NetworkState();
+            Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+                    connectivityState[networkType].toString());
+        }
+    }
+
+    // deposit a network state
+    public void recordNetworkState(int networkType, State networkState) {
+        Log.v(LOG_TAG, "record network state for network " +  networkType +
+                " state is " + networkState);
+        connectivityState[networkType].recordState(networkState);
+    }
+
+    // set the state transition criteria
+    public void setStateTransitionCriteria(int networkType, State initState,
+            int transitionDir, State targetState) {
+        connectivityState[networkType].setStateTransitionCriteria(
+                initState, transitionDir, targetState);
+    }
+
+    // Validate the states recorded
+    public boolean validateNetworkStates(int networkType) {
+        Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+        return connectivityState[networkType].validateStateTransition();
+    }
+
+    // return result from network state validation
+    public String getTransitionFailureReason(int networkType) {
+        Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+                connectivityState[networkType].toString());
+        return connectivityState[networkType].getReason();
+    }
+
+    private void notifyScanResult() {
+        synchronized (this) {
+            Log.v(LOG_TAG, "notify that scan results are available");
+            this.notify();
+        }
+    }
+
+    // Return true if device is currently connected to mobile network
+    public boolean isConnectedToMobile() {
+        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+    }
+
+    // Return true if device is currently connected to Wifi
+    public boolean isConnectedToWifi() {
+        return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+    }
+
+    public boolean enableWifi() {
+        return mWifiManager.setWifiEnabled(true);
+    }
+
+    /**
+     * Associate the device to given SSID
+     * If the device is already associated with a WiFi, disconnect and forget it,
+     * We don't verify whether the connection is successful or not, leave this to the test
+     */
+    public boolean connectToWifi(String knownSSID) {
+        //If Wifi is not enabled, enable it
+        if (!mWifiManager.isWifiEnabled()) {
+            Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+            mWifiManager.setWifiEnabled(true);
+        }
+
+        List<ScanResult> netList = mWifiManager.getScanResults();
+        if (netList == null) {
+            // if no scan results are available, start active scan
+            mWifiManager.startScanActive();
+            mScanResultIsAvailable = false;
+            long startTime = System.currentTimeMillis();
+            while (!mScanResultIsAvailable) {
+                if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+                    return false;
+                }
+                // wait for the scan results to be available
+                synchronized (this) {
+                    // wait for the scan result to be available
+                    try {
+                        this.wait(WAIT_FOR_SCAN_RESULT);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                    if ((mWifiManager.getScanResults() == null) ||
+                            (mWifiManager.getScanResults().size() <= 0)) {
+                        continue;
+                    }
+                    mScanResultIsAvailable = true;
+                }
+            }
+        }
+
+        netList = mWifiManager.getScanResults();
+        for (int i = 0; i < netList.size(); i++) {
+            ScanResult sr= netList.get(i);
+            if (sr.SSID.equals(knownSSID)) {
+                Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
+                WifiConfiguration config = new WifiConfiguration();
+                config.SSID = sr.SSID;
+                config.allowedKeyManagement.set(KeyMgmt.NONE);
+                int networkId = mWifiManager.addNetwork(config);
+                mWifiManager.saveConfiguration();
+                // Connect to network by disabling others.
+                mWifiManager.enableNetwork(networkId, true);
+                mWifiManager.reconnect();
+                break;
+           }
+        }
+
+        List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+        if (netConfList.size() <= 0) {
+            Log.v(LOG_TAG, knownSSID + " is not available");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Disable Wifi
+     * @return true if Wifi is disabled successfully
+     */
+    public boolean disableWiFi() {
+        return mWifiManager.setWifiEnabled(false);
+    }
+
+    /**
+     * Disconnect from the current Wifi and clear the configuration list
+     */
+    public boolean clearWifi() {
+       if (mWifiManager.isWifiEnabled()) {
+            //remove the current network Id
+            int curNetworkId = mWifiManager.getConnectionInfo().getNetworkId();
+            mWifiManager.removeNetwork(curNetworkId);
+            mWifiManager.saveConfiguration();
+
+            // remove other saved networks
+            List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+            if (netConfList != null) {
+                Log.v(LOG_TAG, "remove configured network ids");
+                for (int i = 0; i < netConfList.size(); i++) {
+                    WifiConfiguration conf = new WifiConfiguration();
+                    conf = netConfList.get(i);
+                    mWifiManager.removeNetwork(conf.networkId);
+                }
+            }
+            mWifiManager.saveConfiguration();
+            // disable Wifi
+            if (!mWifiManager.setWifiEnabled(false)) {
+                return false;
+            }
+            // wait for the actions to be completed
+            try {
+                Thread.sleep(5*1000);
+            } catch (InterruptedException e) {}
+        }
+        return true;
+    }
+
+    /**
+     * Set airplane mode
+     */
+    public void setAirplaneMode(Context context, boolean enableAM) {
+        //set the airplane mode
+        Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+                enableAM ? 1 : 0);
+        // Post the intent
+        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", enableAM);
+        context.sendBroadcast(intent);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        //Unregister receiver
+        if (mConnectivityReceiver != null) {
+            unregisterReceiver(mConnectivityReceiver);
+        }
+        if (mWifiReceiver != null) {
+            unregisterReceiver(mWifiReceiver);
+        }
+        Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+    }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
new file mode 100644
index 0000000..3affa65
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -0,0 +1,43 @@
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all connectivity manager tests.
+ *
+ * To run the connectivity manager tests:
+ *
+ * adb shell am instrument \
+ *     -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+
+public class ConnectivityManagerTestRunner extends InstrumentationTestRunner {
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(ConnectivityManagerMobileTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return ConnectivityManagerTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        String testSSID = (String) icicle.get("ssid");
+        if (testSSID != null) {
+            TEST_SSID = testSSID;
+        }
+    }
+
+    public String TEST_SSID = "GoogleGuest";
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
new file mode 100644
index 0000000..925120e
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
@@ -0,0 +1,177 @@
+package com.android.connectivitymanagertest;
+
+import android.net.NetworkInfo.State;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class NetworkState {
+    public static final int TO_DISCONNECTION = 0; // transition to disconnection
+    public static final int TO_CONNECTION = 1; // transition to connection
+    public static final int DO_NOTHING = -1;   // no state change
+    private final String LOG_TAG = "NetworkState";
+    private List<State> mStateDepository;
+    private State mTransitionTarget;
+    private int mTransitionDirection;
+    private String mReason = null;         // record mReason of state transition failure
+
+    public NetworkState() {
+        mStateDepository = new ArrayList<State>();
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    public NetworkState(State currentState) {
+        mStateDepository = new ArrayList<State>();
+        mStateDepository.add(currentState);
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    // Reinitialize the network state
+    public void resetNetworkState() {
+        mStateDepository.clear();
+        mTransitionDirection = DO_NOTHING;
+        mTransitionTarget = State.UNKNOWN;
+    }
+
+    // set the transition criteria, transitionDir could be:
+    // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
+    public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
+        if (!mStateDepository.isEmpty()) {
+            mStateDepository.clear();
+        }
+        mStateDepository.add(initState);
+        mTransitionDirection = transitionDir;
+        mTransitionTarget = targetState;
+        Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
+    }
+
+    public void recordState(State currentState) {
+        mStateDepository.add(currentState);
+    }
+
+    // Verify state transition
+    public boolean validateStateTransition() {
+        Log.v(LOG_TAG, "print state depository: " + printStates());
+        if (mTransitionDirection == DO_NOTHING) {
+            if (mStateDepository.isEmpty()) {
+                Log.v(LOG_TAG, "no state is recorded");
+                mReason = "no state is recorded.";
+                return false;
+            } else if (mStateDepository.size() > 1) {
+                Log.v(LOG_TAG, "no broadcast is expected, " +
+                        "instead broadcast is probably received");
+                mReason = "no broadcast is expected, instead broadcast is probably received";
+                return false;
+            } else if (mStateDepository.get(0) != mTransitionTarget) {
+                Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
+                        mStateDepository.get(0));
+                mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
+                return false;
+            }
+            return true;
+        } else if (mTransitionDirection == TO_CONNECTION) {
+            Log.v(LOG_TAG, "transition to CONNECTED");
+            return transitToConnection();
+        } else {
+            Log.v(LOG_TAG, "transition to DISCONNECTED");
+            return transitToDisconnection();
+        }
+    }
+
+    /*
+     * Transition from CONNECTED -> DISCONNECTED:
+     *    CONNECTED->DISCONNECTING->DISCONNECTED
+     * return false if any state transition is not valid and save a message in mReason
+     */
+    public boolean transitToDisconnection () {
+        mReason = "states: " + printStates();
+        if (mStateDepository.get(0) != State.CONNECTED) {
+            mReason += " initial state should be CONNECTED, but it is " +
+                    mStateDepository.get(0) + ".";
+            return false;
+        }
+        State lastState = mStateDepository.get(mStateDepository.size() - 1);
+        if ( lastState != mTransitionTarget) {
+            mReason += " the last state should be DISCONNECTED, but it is " + lastState;
+            return false;
+        }
+        for (int i = 1; i < mStateDepository.size() - 1; i++) {
+            State preState = mStateDepository.get(i-1);
+            State curState = mStateDepository.get(i);
+            if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+                    (curState == State.DISCONNECTED))) {
+                continue;
+            } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
+                continue;
+            } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
+                continue;
+            } else {
+                mReason += " Transition state from " + preState.toString() + " to " +
+                        curState.toString() + " is not valid.";
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // DISCONNECTED->CONNECTING->CONNECTED
+    public boolean transitToConnection() {
+        mReason = "states: " + printStates();
+        if (mStateDepository.get(0) != State.DISCONNECTED) {
+            mReason += " initial state should be DISCONNECTED, but it is " +
+                    mStateDepository.get(0) + ".";
+            return false;
+        }
+        State lastState = mStateDepository.get(mStateDepository.size() - 1);
+        if ( lastState != mTransitionTarget) {
+            mReason += " the last state should be CONNECTED, but it is " + lastState;
+            return false;
+        }
+        for (int i = 1; i < mStateDepository.size(); i++) {
+            State preState = mStateDepository.get(i-1);
+            State curState = mStateDepository.get(i);
+            if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
+                    (curState == State.CONNECTED))) {
+                continue;
+            } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+                continue;
+            } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
+                continue;
+            } else {
+                mReason += " Transition state from " + preState.toString() + " to " +
+                        curState.toString() + " is not valid.";
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public List<State> getTransitionStates() {
+        return mStateDepository;
+    }
+
+    // return state failure mReason
+    public String getReason() {
+        return mReason;
+    }
+
+    public String printStates() {
+        StringBuilder stateBuilder = new StringBuilder("");
+        for (int i = 0; i < mStateDepository.size(); i++) {
+            stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
+        }
+        return stateBuilder.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(" ");
+        builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
+                append("; ").append("states:").
+                append(printStates()).append("; ");
+        return builder.toString();
+    }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
new file mode 100644
index 0000000..ab81bb8
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -0,0 +1,135 @@
+package com.android.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+import com.android.connectivitymanagertest.NetworkState;
+import android.util.Log;
+import junit.framework.*;
+
+public class ConnectivityManagerMobileTest
+    extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+
+    private static final String LOG_TAG = "ConnectivityManagerMobileTest";
+    private static final String PKG_NAME = "com.android.connectivitymanagertest";
+    private static final long WIFI_CONNECTION_TIMEOUT = 30 * 1000;
+    private static final long WIFI_NOTIFICATION_TIMEOUT = 10 * 1000;
+    private String TEST_ACCESS_POINT;
+    private ConnectivityManagerTestActivity cmActivity;
+
+    public ConnectivityManagerMobileTest() {
+        super(PKG_NAME, ConnectivityManagerTestActivity.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        cmActivity = getActivity();
+        ConnectivityManagerTestRunner mRunner =
+                (ConnectivityManagerTestRunner)getInstrumentation();
+        TEST_ACCESS_POINT = mRunner.TEST_SSID;
+        // Each test case will start with cellular connection
+        verifyCellularConnection();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // clear Wifi after each test case
+        cmActivity.clearWifi();
+        cmActivity.finish();
+        Log.v(LOG_TAG, "tear down ConnectivityManager test activity");
+        super.tearDown();
+    }
+
+    // help function to verify 3G connection
+    public void verifyCellularConnection() {
+        NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+        assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
+            extraNetInfo.getType());
+        assertTrue("not connected to cellular network", extraNetInfo.isConnected());
+        assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
+    }
+
+    // Test case 1: Test enabling Wifi without associating with any AP
+    @LargeTest
+    public void test3GToWifiNotification() {
+        // As Wifi stays in DISCONNECTED, the connectivity manager will not broadcast
+        // any network connectivity event for Wifi
+        NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+                NetworkState.DO_NOTHING, State.CONNECTED);
+        networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+                NetworkState.DO_NOTHING, State.DISCONNECTED);
+        // Eanble Wifi
+        cmActivity.enableWifi();
+        try {
+            Thread.sleep(WIFI_NOTIFICATION_TIMEOUT);
+        } catch (Exception e) {
+            Log.v(LOG_TAG, "exception: " + e.toString());
+        }
+
+        // validate state and broadcast
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+            Log.v(LOG_TAG, "the state for WIFI is changed");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+            assertTrue(false);
+        }
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+            Log.v(LOG_TAG, "the state for MOBILE is changed");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+            assertTrue(false);
+        }
+        // Verify that the device is still connected to MOBILE
+        verifyCellularConnection();
+    }
+
+    // Test case 2: test connection to a given AP
+    @LargeTest
+    public void testConnectToWifi() {
+        //Prepare for connectivity verification
+        NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+                NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
+        networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+                NetworkState.TO_CONNECTION, State.CONNECTED);
+
+        // Enable Wifi and connect to a test access point
+        assertTrue("failed to connect to " + TEST_ACCESS_POINT,
+                cmActivity.connectToWifi(TEST_ACCESS_POINT));
+        try {
+            Thread.sleep(WIFI_CONNECTION_TIMEOUT);
+        } catch (Exception e) {
+            Log.v(LOG_TAG, "exception: " + e.toString());
+        }
+
+        // validate states
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+            Log.v(LOG_TAG, "Wifi state transition validation failed.");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+            assertTrue(false);
+        }
+        if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+            Log.v(LOG_TAG, "Mobile state transition validation failed.");
+            Log.v(LOG_TAG, "reason: " +
+                    cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+            assertTrue(false);
+        }
+    }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index ce1bf8d..5780c43 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -73,6 +73,7 @@
     private static final int LAYOUT_DUMP_DATABASE_CALLBACKS = 41;
     private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
     private static final int SET_GEOLOCATION_PERMISSION = 43;
+    private static final int OVERRIDE_PREFERENCE = 44;
     
     CallbackProxy(EventSender eventSender, 
             LayoutTestController layoutTestController) {
@@ -266,6 +267,12 @@
             mLayoutTestController.setGeolocationPermission(
                     msg.arg1 == 1 ? true : false);
             break;
+
+        case OVERRIDE_PREFERENCE:
+            String key = msg.getData().getString("key");
+            boolean value = msg.getData().getBoolean("value");
+            mLayoutTestController.overridePreference(key, value);
+            break;
         }
     }
 
@@ -461,6 +468,10 @@
         WebStorage.getInstance().setQuotaForOrigin("file://", quota);
     }
 
+    public void setAppCacheMaximumSize(long size) {
+        WebStorage.getInstance().setAppCacheMaximumSize(size);
+    }
+
     public void setCanOpenWindows() {
         obtainMessage(LAYOUT_SET_CAN_OPEN_WINDOWS).sendToTarget();
     }
@@ -480,4 +491,11 @@
     public void setGeolocationPermission(boolean allow) {
         obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
     }
+
+    public void overridePreference(String key, boolean value) {
+        Message message = obtainMessage(OVERRIDE_PREFERENCE);
+        message.getData().putString("key", key);
+        message.getData().putBoolean("value", value);
+        message.sendToTarget();
+    }
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index b668bd0..d10e382 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -82,19 +82,10 @@
         // This first block of tests are for HTML5 features, for which Android
         // should pass all tests. They are skipped only temporarily.
         // TODO: Fix these failing tests and remove them from this list.
-        ignoreResultList.add("http/tests/appcache/auth.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/destroyed-frame.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/detached-iframe.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/different-scheme.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/disabled.html"); // not found
+        ignoreResultList.add("http/tests/appcache/auth.html"); // DumpRenderTree throws exception when authentication fails
         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
         ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
         ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index f535ed7..9236345 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -65,4 +65,6 @@
 
     // For Geolocation tests
     public void setGeolocationPermission(boolean allow);
+
+    public void overridePreference(String key, boolean value);
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 02a7046..24f58b2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -61,6 +61,9 @@
 
     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
 
+    // String constants for use with layoutTestController.overridePreferences
+    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled";
+
     public class AsyncHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -459,6 +462,16 @@
         mGeolocationPermission = allow;
     }
 
+    public void overridePreference(String key, boolean value) {
+        // TODO: We should look up the correct WebView for the frame which
+        // called the layoutTestController method. Currently, we just use the
+        // WebView for the main frame. EventSender suffers from the same
+        // problem.
+        if (key.equals(WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
+            mWebView.getSettings().setAppCacheEnabled(value);
+        }
+    }
+
     private final WebViewClient mViewClient = new WebViewClient(){
         @Override
         public void onPageFinished(WebView view, String url) {
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 08530a0..c8b6837 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -37,12 +37,12 @@
           mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
           mUpdate(false), mExtending(false),
           mRequireLocalization(false), mPseudolocalize(false),
-          mUTF8(false), mEncodingSpecified(false), mValues(false),
+          mWantUTF16(false), mValues(false),
           mCompressionMethod(0), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
           mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
-          mRClassDir(NULL), mResourceIntermediatesDir(NULL),
+          mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
           mArgc(0), mArgv(NULL)
@@ -77,10 +77,8 @@
     void setRequireLocalization(bool val) { mRequireLocalization = val; }
     bool getPseudolocalize(void) const { return mPseudolocalize; }
     void setPseudolocalize(bool val) { mPseudolocalize = val; }
-    bool getUTF8(void) const { return mUTF8; }
-    void setUTF8(bool val) { mUTF8 = val; }
-    bool getEncodingSpecified(void) const { return mEncodingSpecified; }
-    void setEncodingSpecified(bool val) { mEncodingSpecified = val; }
+    bool getWantUTF16(void) const { return mWantUTF16; }
+    void setWantUTF16(bool val) { mWantUTF16 = val; }
     bool getValues(void) const { return mValues; }
     void setValues(bool val) { mValues = val; }
     int getCompressionMethod(void) const { return mCompressionMethod; }
@@ -122,13 +120,10 @@
     const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; }
     void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
 
+    const char*  getManifestMinSdkVersion() const { return mManifestMinSdkVersion; }
+    void setManifestMinSdkVersion(const char*  val) { mManifestMinSdkVersion = val; }
     const char*  getMinSdkVersion() const { return mMinSdkVersion; }
-    void setMinSdkVersion(const char*  val) {
-        mMinSdkVersion = val;
-        if (!mEncodingSpecified) {
-            setUTF8(isUTF8Available());
-        }
-    }
+    void setMinSdkVersion(const char*  val) { mMinSdkVersion = val; }
     const char*  getTargetSdkVersion() const { return mTargetSdkVersion; }
     void setTargetSdkVersion(const char*  val) { mTargetSdkVersion = val; }
     const char*  getMaxSdkVersion() const { return mMaxSdkVersion; }
@@ -167,6 +162,34 @@
     void setPackageCount(int val) { mPackageCount = val; }
 #endif
 
+    /* UTF-8 is only available on APIs 7 or above or
+     * SDK levels that have code names.
+     */
+    bool isUTF8Available() {
+        /* If the application specifies a minSdkVersion in the manifest
+         * then use that. Otherwise, check what the user specified on
+         * the command line. If neither, it's not available since
+         * the minimum SDK version is assumed to be 1.
+         */
+        const char *minVer;
+        if (mManifestMinSdkVersion != NULL) {
+            minVer = mManifestMinSdkVersion;
+        } else if (mMinSdkVersion != NULL) {
+            minVer = mMinSdkVersion;
+        } else {
+            return false;
+        }
+
+        char *end;
+        int minSdkNum = (int)strtol(minVer, &end, 0);
+        if (*end == '\0') {
+            if (minSdkNum < 7) {
+                return false;
+            }
+        }
+        return true;
+    }
+
 private:
     /* commands & modifiers */
     Command     mCmd;
@@ -179,8 +202,7 @@
     bool        mExtending;
     bool        mRequireLocalization;
     bool        mPseudolocalize;
-    bool        mUTF8;
-    bool        mEncodingSpecified;
+    bool        mWantUTF16;
     bool        mValues;
     int         mCompressionMethod;
     bool        mJunkPath;
@@ -200,6 +222,7 @@
     android::Vector<const char*> mNoCompressExtensions;
     android::Vector<const char*> mResourceSourceDirs;
 
+    const char* mManifestMinSdkVersion;
     const char* mMinSdkVersion;
     const char* mTargetSdkVersion;
     const char* mMaxSdkVersion;
@@ -216,19 +239,6 @@
     int         mPackageCount;
 #endif
 
-    /* UTF-8 is only available on APIs 7 or above or
-     * SDK levels that have code names.
-     */
-    bool isUTF8Available() {
-        char *end;
-        int minSdkNum = (int)strtol(mMinSdkVersion, &end, 0);
-        if (*end == '\0') {
-            if (minSdkNum < 7) {
-                return false;
-            }
-        }
-        return true;
-    }
 };
 
 #endif // __BUNDLE_H
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index dd98c85..b0c6e39 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -446,8 +446,7 @@
                     }
                     bundle.setCustomPackage(argv[0]);
                 } else if (strcmp(cp, "-utf16") == 0) {
-                    bundle.setEncodingSpecified(true);
-                    bundle.setUTF8(false);
+                    bundle.setWantUTF16(true);
                 } else if (strcmp(cp, "-rename-manifest-package") == 0) {
                     argc--;
                     argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index c0ebb59..a8ac2ec 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -226,7 +226,7 @@
                 if (minSdkIndex >= 0) {
                     const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
                     const char* minSdk8 = strdup(String8(minSdk16).string());
-                    bundle->setMinSdkVersion(minSdk8);
+                    bundle->setManifestMinSdkVersion(minSdk8);
                 }
             }
         }
@@ -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;
     }
@@ -768,7 +769,13 @@
 
     // Standard flags for compiled XML and optional UTF-8 encoding
     int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
-    if (bundle->getUTF8()) {
+
+    /* Only enable UTF-8 if the caller of aapt didn't specifically
+     * request UTF-16 encoding and the parameters of this package
+     * allow UTF-8 to be used.
+     */
+    if (!bundle->getWantUTF16()
+            && bundle->isUTF8Available()) {
         xmlFlags |= XML_COMPILE_UTF8;
     }
 
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index ab5e937..66db450 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2527,9 +2527,11 @@
     const size_t N = mOrderedPackages.size();
     size_t pi;
 
+    bool useUTF8 = !bundle->getWantUTF16() && bundle->isUTF8Available();
+
     // Iterate through all data, collecting all values (strings,
     // references, etc).
-    StringPool valueStrings = StringPool(false, bundle->getUTF8());
+    StringPool valueStrings = StringPool(false, useUTF8);
     for (pi=0; pi<N; pi++) {
         sp<Package> p = mOrderedPackages.itemAt(pi);
         if (p->getTypes().size() == 0) {
@@ -2537,8 +2539,8 @@
             continue;
         }
 
-        StringPool typeStrings = StringPool(false, bundle->getUTF8());
-        StringPool keyStrings = StringPool(false, bundle->getUTF8());
+        StringPool typeStrings = StringPool(false, useUTF8);
+        StringPool keyStrings = StringPool(false, useUTF8);
 
         const size_t N = p->getOrderedTypes().size();
         for (size_t ti=0; ti<N; ti++) {
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 9339428..80b8aedfa 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -54,10 +54,11 @@
 import android.database.ContentObserver;
 import com.android.internal.app.IBatteryStats;
 
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Set;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Track the state of Wifi connectivity. All event handling is done here,
@@ -282,8 +283,13 @@
      *         {@link WifiManager#WIFI_STATE_ENABLED},
      *         {@link WifiManager#WIFI_STATE_ENABLING},
      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
+     *
+     * getWifiState() is not synchronized to make sure it's always fast,
+     * even when the instance lock is held on other slow operations.
+     * Use a atomic variable for state.
      */
-    private int mWifiState;
+    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
+
     // Wi-Fi run states:
     private static final int RUN_STATE_STARTING = 1;
     private static final int RUN_STATE_RUNNING  = 2;
@@ -785,8 +791,8 @@
             case EVENT_SUPPLICANT_DISCONNECT:
                 mRunState = RUN_STATE_STOPPED;
                 noteRunState();
-                boolean died = mWifiState != WIFI_STATE_DISABLED &&
-                               mWifiState != WIFI_STATE_DISABLING;
+                boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
+                               mWifiState.get() != WIFI_STATE_DISABLING;
                 if (died) {
                     if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
                 } else {
@@ -1074,7 +1080,11 @@
                 break;
 
             case EVENT_DEFERRED_RECONNECT:
-                String BSSID = msg.obj.toString();
+                /**
+                 * mLastBssid can be null when there is a reconnect
+                 * request on the first BSSID we connect to
+                 */
+                String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
                 /**
                  * If we've exceeded the maximum number of retries for reconnecting
                  * to a given network, blacklist the BSSID to allow a connection attempt on
@@ -1500,12 +1510,12 @@
         return true;
     }
 
-    public synchronized int getWifiState() {
-        return mWifiState;
+    public int getWifiState() {
+        return mWifiState.get();
     }
 
-    public synchronized void setWifiState(int wifiState) {
-        mWifiState = wifiState;
+    public void setWifiState(int wifiState) {
+        mWifiState.set(wifiState);
     }
 
    /**
@@ -1584,7 +1594,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean ping() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.pingCommand();
@@ -1597,7 +1607,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean scan(boolean forceActive) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.scanCommand(forceActive);
@@ -1613,7 +1623,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setScanResultHandling(int mode) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.setScanResultHandlingCommand(mode);
@@ -1627,7 +1637,7 @@
      * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
      */
     public synchronized String scanResults() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return null;
         }
         return WifiNative.scanResultsCommand();
@@ -1639,7 +1649,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setScanMode(boolean isScanModeActive) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         if (mIsScanModeActive != isScanModeActive) {
@@ -1654,7 +1664,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean disconnect() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.disconnectCommand();
@@ -1666,7 +1676,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean reconnectCommand() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.reconnectCommand();
@@ -1678,7 +1688,7 @@
      * @return network id of the new network
      */
     public synchronized int addNetwork() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return -1;
         }
         return WifiNative.addNetworkCommand();
@@ -1691,7 +1701,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean removeNetwork(int networkId) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
@@ -1705,7 +1715,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.enableNetworkCommand(netId, disableOthers);
@@ -1718,7 +1728,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean disableNetwork(int netId) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.disableNetworkCommand(netId);
@@ -1730,7 +1740,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean reassociate() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.reassociateCommand();
@@ -1744,7 +1754,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean addToBlacklist(String bssid) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.addToBlacklistCommand(bssid);
@@ -1756,7 +1766,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean clearBlacklist() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.clearBlacklistCommand();
@@ -1768,7 +1778,7 @@
      * @return list of networks or null on failure
      */
     public synchronized String listNetworks() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return null;
         }
         return WifiNative.listNetworksCommand();
@@ -1782,7 +1792,7 @@
      * @return value corresponding to key
      */
     public synchronized String getNetworkVariable(int netId, String name) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return null;
         }
         return WifiNative.getNetworkVariableCommand(netId, name);
@@ -1797,7 +1807,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setNetworkVariable(int netId, String name, String value) {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.setNetworkVariableCommand(netId, name, value);
@@ -1817,7 +1827,7 @@
      *  ip_address=X.X.X.X
      */
     public synchronized String status() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return null;
         }
         return WifiNative.statusCommand();
@@ -1829,7 +1839,7 @@
      * @return RSSI value, -1 on failure
      */
     public synchronized int getRssi() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return -1;
         }
         return WifiNative.getRssiApproxCommand();
@@ -1841,7 +1851,7 @@
      * @return RSSI value, -1 on failure
      */
     public synchronized int getRssiApprox() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return -1;
         }
         return WifiNative.getRssiApproxCommand();
@@ -1853,7 +1863,7 @@
      * @return link speed, -1 on failure
      */
     public synchronized int getLinkSpeed() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return -1;
         }
         return WifiNative.getLinkSpeedCommand();
@@ -1865,7 +1875,7 @@
      * @return MAC address, null on failure
      */
     public synchronized String getMacAddress() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return null;
         }
         return WifiNative.getMacAddressCommand();
@@ -1877,7 +1887,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean startDriver() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.startDriverCommand();
@@ -1889,7 +1899,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean stopDriver() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.stopDriverCommand();
@@ -1901,7 +1911,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean startPacketFiltering() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.startPacketFiltering();
@@ -1913,7 +1923,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean stopPacketFiltering() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.stopPacketFiltering();
@@ -1927,7 +1937,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setPowerMode(int mode) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.setPowerModeCommand(mode);
@@ -1940,7 +1950,7 @@
      * the number of channels is invalid.
      */
     public synchronized boolean setNumAllowedChannels() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         try {
@@ -1965,7 +1975,7 @@
      * {@code numChannels} is outside the valid range.
      */
     public synchronized boolean setNumAllowedChannels(int numChannels) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         mNumAllowedChannels = numChannels;
@@ -1978,7 +1988,7 @@
      * @return channel count, -1 on failure
      */
     public synchronized int getNumAllowedChannels() {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return -1;
         }
         return WifiNative.getNumAllowedChannelsCommand();
@@ -1994,7 +2004,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setBluetoothCoexistenceMode(int mode) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return false;
         }
         return WifiNative.setBluetoothCoexistenceModeCommand(mode);
@@ -2008,7 +2018,7 @@
      * @param isBluetoothPlaying whether to enable or disable this mode
      */
     public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
-        if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
             return;
         }
         WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
@@ -2020,7 +2030,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean saveConfig() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.saveConfigCommand();
@@ -2032,7 +2042,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean reloadConfig() {
-        if (mWifiState != WIFI_STATE_ENABLED) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.reloadConfigCommand();