Merge "Improve wakelock debugging:"
diff --git a/api/current.xml b/api/current.xml
index 9dc2218..10742db 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -80658,6 +80658,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"
@@ -80917,6 +80930,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"
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/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/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/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/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/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/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/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 53c238c..c44854a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -506,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/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index dae9187..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;
     }
 
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index dc7723f..2c24757 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -1198,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/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 86011d7..a30059c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1835,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;
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/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/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/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 68b351b..b56e638 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1472,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
@@ -1490,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) {
@@ -1517,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/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/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/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 48b3fbb..9eb63a6 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -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;
             }
         }
     }
@@ -9373,10 +9381,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 " +
@@ -9384,32 +9394,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();
@@ -9474,11 +9481,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);
        }
    }
 
@@ -9509,8 +9516,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;
@@ -9578,7 +9584,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) {
@@ -9594,7 +9600,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>();
@@ -9617,7 +9623,7 @@
            }
        }
        // Send broadcasts
-       if (sendUpdateBroadcast) {
+       if (pkgList.size() > 0) {
            sendResourcesChangedBroadcast(false, pkgList, uidArr);
        }
        // Force gc
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/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/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/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/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index dd7a169..6975c70 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -93,6 +93,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         mOrigState = getMediaState();
+        if (!mountMedia()) {
+            Log.i(TAG, "sdcard not mounted? Some of these tests might fail");
+        }
     }
 
     @Override
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/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index abae65d..73ed6ed 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,
@@ -284,6 +285,13 @@
      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
      */
     private int mWifiState;
+
+    /**
+     * For getWifiState(), to make sure it's always fast, even when the
+     * instance lock is held on other slow operations.
+     */
+    private final AtomicInteger mWifiStateAtomic = 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;
@@ -1504,11 +1512,12 @@
         return true;
     }
 
-    public synchronized int getWifiState() {
-        return mWifiState;
+    public int getWifiState() {
+        return mWifiStateAtomic.get();
     }
 
     public synchronized void setWifiState(int wifiState) {
+        mWifiStateAtomic.set(wifiState);
         mWifiState = wifiState;
     }