Merge "Unittests for EntropyService.  Make EntropyService more testable."
diff --git a/api/current.xml b/api/current.xml
index cd11b1d..9dc2218 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -80994,6 +80994,17 @@
  visibility="public"
 >
 </field>
+<field name="AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AUDIOFOCUS_LOSS"
  type="int"
  transient="false"
@@ -81016,6 +81027,17 @@
  visibility="public"
 >
 </field>
+<field name="AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AUDIOFOCUS_REQUEST_FAILED"
  type="int"
  transient="false"
@@ -193713,7 +193735,7 @@
  visibility="public"
 >
 </method>
-<method name="getUseSystemOverscrollBackground"
+<method name="getUseWebViewBackgroundForOverscrollBackground"
  return="boolean"
  abstract="false"
  native="false"
@@ -194303,7 +194325,7 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
-<method name="setUseSystemOverscrollBackground"
+<method name="setUseWebViewBackgroundForOverscrollBackground"
  return="void"
  abstract="false"
  native="false"
@@ -194313,7 +194335,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="system" type="boolean">
+<parameter name="view" type="boolean">
 </parameter>
 </method>
 <method name="setUseWideViewPort"
@@ -385783,7 +385805,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="text" type="java.lang.String">
+<parameter name="message" type="java.lang.String">
 </parameter>
 </method>
 </class>
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index e9b21f1..e8b5eaf 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -105,7 +105,7 @@
 
 #ifdef FWDUMP_bcm4329
     run_command("DUMP WIFI FIRMWARE LOG", 60,
-            "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
+            "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
 #endif
 
     print_properties();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index cfc2e75..659d70f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,11 +92,6 @@
             return;
         }
 
-        if ("mountsd".equals(op)) {
-            runMountSd();
-            return;
-        }
-
         if ("uninstall".equals(op)) {
             runUninstall();
             return;
@@ -646,37 +641,6 @@
         }
     }
 
-    private void runMountSd() {
-        String opt;
-        boolean mount = false;
-        while ((opt=nextOption()) != null) {
-            if (opt.equals("-m")) {
-                String mountStr = nextOptionData();
-                if (mountStr == null) {
-                    System.err.println("Error: no value specified for -m");
-                    showUsage();
-                    return;
-                }
-                if ("true".equalsIgnoreCase(mountStr)) {
-                    mount = true;
-                } else if ("false".equalsIgnoreCase(mountStr)) {
-                    mount = false;
-                } else {
-                    System.err.println("Error: no value specified for -m");
-                    showUsage();
-                    return;
-                }
-            }
-        }
-
-        try {
-            mPm.updateExternalMediaStatus(mount);
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-        }
-    }
-
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -866,7 +830,6 @@
         System.err.println("       pm path PACKAGE");
         System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
-        System.err.println("       pm mountsd [-m true/false]");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("");
diff --git a/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/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9201e3b..4b48409 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -175,6 +175,7 @@
     private boolean mShowTouchPoints = true;
     private int mPopupPreviewX;
     private int mPopupPreviewY;
+    private int mWindowY;
 
     private int mLastX;
     private int mLastY;
@@ -909,20 +910,36 @@
             getLocationInWindow(mOffsetInWindow);
             mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
             mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+            int[] mWindowLocation = new int[2];
+            getLocationOnScreen(mWindowLocation);
+            mWindowY = mWindowLocation[1];
         }
         // Set the preview background state
         mPreviewText.getBackground().setState(
                 key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+        mPopupPreviewX += mOffsetInWindow[0];
+        mPopupPreviewY += mOffsetInWindow[1];
+
+        // If the popup cannot be shown above the key, put it on the side
+        if (mPopupPreviewY + mWindowY < 0) {
+            // If the key you're pressing is on the left side of the keyboard, show the popup on
+            // the right, offset by enough to see at least one key to the left/right.
+            if (key.x + key.width <= getWidth() / 2) {
+                mPopupPreviewX += (int) (key.width * 2.5);
+            } else {
+                mPopupPreviewX -= (int) (key.width * 2.5);
+            }
+            mPopupPreviewY += popupHeight;
+        }
+
         if (previewPopup.isShowing()) {
-            previewPopup.update(mPopupPreviewX + mOffsetInWindow[0],
-                    mPopupPreviewY + mOffsetInWindow[1], 
+            previewPopup.update(mPopupPreviewX, mPopupPreviewY,
                     popupWidth, popupHeight);
         } else {
             previewPopup.setWidth(popupWidth);
             previewPopup.setHeight(popupHeight);
             previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, 
-                    mPopupPreviewX + mOffsetInWindow[0], 
-                    mPopupPreviewY + mOffsetInWindow[1]);
+                    mPopupPreviewX, mPopupPreviewY);
         }
         mPreviewText.setVisibility(VISIBLE);
     }
diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java
index b8e17da..43fb5f1 100644
--- a/core/java/android/net/http/Connection.java
+++ b/core/java/android/net/http/Connection.java
@@ -222,6 +222,12 @@
                         }
                     }
 
+                    /* we have a connection, let the event handler
+                     * know of any associated certificate,
+                     * potentially none.
+                     */
+                    req.mEventHandler.certificate(mCertificate);
+
                     try {
                         /* FIXME: don't increment failure count if old
                            connection?  There should not be a penalty for
diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java
index a035c19..2aa05eb 100644
--- a/core/java/android/net/http/EventHandler.java
+++ b/core/java/android/net/http/EventHandler.java
@@ -125,8 +125,8 @@
     public void endData();
 
     /**
-     * SSL certificate callback called every time a resource is
-     * loaded via a secure connection
+     * SSL certificate callback called before resource request is
+     * made, which will be null for insecure connection.
      */
     public void certificate(SslCertificate certificate);
 
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index a67fd9a..e512a1df 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -308,12 +308,6 @@
         SslError error = CertificateChainValidator.getInstance().
             doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName());
 
-        EventHandler eventHandler = req.getEventHandler();
-
-        // Update the certificate info (to be consistent, it is better to do it
-        // here, before we start handling SSL errors, if any)
-        eventHandler.certificate(mCertificate);
-
         // Inform the user if there is a problem
         if (error != null) {
             // handleSslErrorRequest may immediately unsuspend if it wants to
@@ -325,7 +319,7 @@
                 mSuspended = true;
             }
             // don't hold the lock while calling out to the event handler
-            boolean canHandle = eventHandler.handleSslErrorRequest(error);
+            boolean canHandle = req.getEventHandler().handleSslErrorRequest(error);
             if(!canHandle) {
                 throw new IOException("failed to handle "+ error);
             }
diff --git a/core/java/android/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/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index af42ce0..344b390 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -527,20 +527,12 @@
      */
     private int getFileSize(String uri) {
         int size = 0;
-        Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
-                new String[] { OpenableColumns.SIZE },
-                null,
-                null,
-                null);
-        if (cursor != null) {
-            try {
-                if (cursor.moveToNext()) {
-                    size = cursor.getInt(0);
-                }
-            } finally {
-                cursor.close();
-            }
-        }
+        try {
+            InputStream stream = mContext.getContentResolver()
+                            .openInputStream(Uri.parse(uri));
+            size = stream.available();
+            stream.close();
+        } catch (Exception e) {}
         return size;
     }
 
@@ -586,7 +578,10 @@
      * @param headers The http headers.
      * @param postData If the method is "POST" postData is sent as the request
      *                 body. Is null when empty.
-     * @param cacheMode The cache mode to use when loading this resource.
+     * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
+     * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
+     * @param mainResource True if the this resource is the main request, not a supporting resource
+     * @param userGesture
      * @param synchronous True if the load is synchronous.
      * @return A newly created LoadListener object.
      */
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 1c59c10..4f680e5 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -733,11 +733,15 @@
             ret.contentdisposition = contentDisposition;
         }
 
+        // lastModified and etag may be set back to http header. So they can't
+        // be empty string.
         String lastModified = headers.getLastModified();
-        if (lastModified != null) ret.lastModified = lastModified;
+        if (lastModified != null && lastModified.length() > 0) {
+            ret.lastModified = lastModified;
+        }
 
         String etag = headers.getEtag();
-        if (etag != null) ret.etag = etag;
+        if (etag != null && etag.length() > 0) ret.etag = etag;
 
         String cacheControl = headers.getCacheControl();
         if (cacheControl != null) {
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 4f2830b..1eb3fa4 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -397,6 +397,7 @@
                             notify();
                         }
                     }
+                    mWebView.dismissZoomControl();
                 }
                 break;
 
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 427ce76..74a648e 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -230,14 +230,8 @@
                 return 1;
             }
 
-            diff = cookie2.name.hashCode() - cookie1.name.hashCode();
-            if (diff != 0) return diff;
-
-            // cookie1 and cookie2 both have non-null values so we emit a
-            // warning and treat them as the same.
-            Log.w(LOGTAG, "Found two cookies with the same value."
-                    + "cookie1=" + cookie1 + " , cookie2=" + cookie2);
-            return 0;
+            // Fallback to comparing the name to ensure consistent order.
+            return cookie1.name.compareTo(cookie2.name);
         }
     }
 
@@ -255,7 +249,7 @@
      * first.
      * 
      * @return CookieManager
-=     */
+     */
     public static synchronized CookieManager getInstance() {
         if (sRef == null) {
             sRef = new CookieManager();
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index a23a4ce..dc7723f 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -121,6 +121,7 @@
 
     // Does this loader correspond to the main-frame top-level page?
     private boolean mIsMainPageLoader;
+    // Does this loader correspond to the main content (as opposed to a supporting resource)
     private final boolean mIsMainResourceLoader;
     private final boolean mUserGesture;
 
@@ -520,23 +521,28 @@
     }
 
     /**
-     * Implementation of certificate handler for EventHandler.
-     * Called every time a resource is loaded via a secure
-     * connection. In this context, can be called multiple
-     * times if we have redirects
-     * @param certificate The SSL certifcate
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
+     * Implementation of certificate handler for EventHandler. Called
+     * before a resource is requested. In this context, can be called
+     * multiple times if we have redirects
+     *
+     * IMPORTANT: as this is called from network thread, can't call
+     * native directly
+     *
+     * @param certificate The SSL certifcate or null if the request
+     * was not secure
      */
     public void certificate(SslCertificate certificate) {
+        if (DebugFlags.LOAD_LISTENER) {
+            Log.v(LOGTAG, "LoadListener.certificate: " + certificate);
+        }
         sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate));
     }
 
     // Handle the certificate on the WebCore thread.
     private void handleCertificate(SslCertificate certificate) {
-        // if this is the top-most main-frame page loader
-        if (mIsMainPageLoader) {
-            // update the browser frame (ie, the main frame)
+        // if this is main resource of the top frame
+        if (mIsMainPageLoader && mIsMainResourceLoader) {
+            // update the browser frame with certificate
             mBrowserFrame.certificate(certificate);
         }
     }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index fb15f78..d1da5ea 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -192,7 +192,7 @@
     private boolean         mBuiltInZoomControls = false;
     private boolean         mAllowFileAccess = true;
     private boolean         mLoadWithOverviewMode = false;
-    private boolean         mUseSystemOverscrollBackground = false;
+    private boolean         mUseWebViewBackgroundOverscrollBackground = true;
 
     // private WebSettings, not accessible by the host activity
     static private int      mDoubleTapToastCount = 3;
@@ -471,20 +471,20 @@
     }
 
     /**
-     * Set whether the WebView uses system background for over scroll
-     * background. If false, it will use the WebView's background. Default is
-     * false.
+     * Set whether the WebView uses its background for over scroll background.
+     * If true, it will use the WebView's background. If false, it will use an
+     * internal pattern. Default is true.
      */
-    public void setUseSystemOverscrollBackground(boolean system) {
-        mUseSystemOverscrollBackground = system;
+    public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+        mUseWebViewBackgroundOverscrollBackground = view;
     }
 
     /**
-     * Returns true if this WebView uses system background instead of WebView
-     * background for over scroll background.
+     * Returns true if this WebView uses WebView's background instead of
+     * internal pattern for over scroll background.
      */
-    public boolean getUseSystemOverscrollBackground() {
-        return mUseSystemOverscrollBackground;
+    public boolean getUseWebViewBackgroundForOverscrollBackground() {
+        return mUseWebViewBackgroundOverscrollBackground;
     }
 
     /**
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 9314d7b..5345879 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -340,6 +340,15 @@
     }
 
     /**
+     * Sets the maximum size of the ApplicationCache.
+     * This should only ever be called on the WebKit thread.
+     * @hide Pending API council approval
+     */
+    public void setAppCacheMaximumSize(long size) {
+        nativeSetAppCacheMaximumSize(size);
+    }
+
+    /**
      * Utility function to send a message to our handler
      */
     private synchronized void postMessage(Message msg) {
@@ -402,4 +411,5 @@
     private static native void nativeSetQuotaForOrigin(String origin, long quota);
     private static native void nativeDeleteOrigin(String origin);
     private static native void nativeDeleteAllData();
+    private static native void nativeSetAppCacheMaximumSize(long size);
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1becf9e..cabda85 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1088,6 +1088,9 @@
      * Sets the SSL certificate for the main top-level page.
      */
     public void setCertificate(SslCertificate certificate) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "setCertificate=" + certificate);
+        }
         // here, the certificate can be null (if the site is not secure)
         mCertificate = certificate;
     }
@@ -2294,20 +2297,13 @@
         scrollBar.draw(canvas);
     }
 
-    private boolean canOverscrollHorizontally() {
-        return (Math.abs(mMinZoomScale - mMaxZoomScale) >= MINIMUM_SCALE_INCREMENT)
-                && getSettings().supportZoom()
-                && getSettings().getUseWideViewPort();
-    }
-
     @Override
     protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
             boolean clampedY) {
         mInOverScrollMode = false;
         int maxX = computeMaxScrollX();
-        if (maxX == 0 && !canOverscrollHorizontally()) {
-            // do not over scroll x if the page just fits the screen and it
-            // can't zoom or the view doesn't use wide viewport
+        if (maxX == 0) {
+            // do not over scroll x if the page just fits the screen
             scrollX = pinLocX(scrollX);
         } else if (scrollX < 0 || scrollX > maxX) {
             mInOverScrollMode = true;
@@ -3120,8 +3116,8 @@
         }
 
         int saveCount = canvas.save();
-        if (mInOverScrollMode
-                && getSettings().getUseSystemOverscrollBackground()) {
+        if (mInOverScrollMode && !getSettings()
+                .getUseWebViewBackgroundForOverscrollBackground()) {
             if (mOverScrollBackground == null) {
                 mOverScrollBackground = new Paint();
                 Bitmap bm = BitmapFactory.decodeResource(
@@ -3983,8 +3979,7 @@
     protected void onDetachedFromWindow() {
         clearTextEntry(false);
         super.onDetachedFromWindow();
-        // Clean up the zoom controller
-        mZoomButtonsController.setVisible(false);
+        dismissZoomControl();
     }
 
     /**
@@ -4183,6 +4178,8 @@
             }
         }
 
+        dismissZoomControl();
+
         // onSizeChanged() is called during WebView layout. And any
         // requestLayout() is blocked during layout. As setNewZoomScale() will
         // call its child View to reposition itself through ViewManager's
@@ -4444,9 +4441,7 @@
         public boolean onScaleBegin(ScaleGestureDetector detector) {
             // cancel the single touch handling
             cancelTouch();
-            if (mZoomButtonsController.isVisible()) {
-                mZoomButtonsController.setVisible(false);
-            }
+            dismissZoomControl();
             // reset the zoom overview mode so that the page won't auto grow
             mInZoomOverview = false;
             // If it is in password mode, turn it off so it does not draw
@@ -5385,9 +5380,6 @@
                 vx = 0;
             }
         }
-        if (maxX == 0 && !canOverscrollHorizontally()) {
-            vx = 0;
-        }
         if (true /* EMG release: make our fling more like Maps' */) {
             // maps cuts their velocity in half
             vx = vx * 3 / 4;
@@ -5426,8 +5418,9 @@
         mLastVelY = vy;
         mLastVelocity = (float) Math.hypot(vx, vy);
 
+        // no horizontal overscroll if the content just fits
         mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
-                getViewWidth() / 3, getViewHeight() / 3);
+                maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3);
         // TODO: duration is calculated based on velocity, if the range is
         // small, the animation will stop before duration is up. We may
         // want to calculate how long the animation is going to run to precisely
@@ -5779,6 +5772,31 @@
         }
     }
 
+    void dismissZoomControl() {
+        if (mWebViewCore == null) {
+            // maybe called after WebView's destroy(). As we can't get settings,
+            // just hide zoom control for both styles.
+            mZoomButtonsController.setVisible(false);
+            if (mZoomControls != null) {
+                mZoomControls.hide();
+            }
+            return;
+        }
+        WebSettings settings = getSettings();
+        if (settings.getBuiltInZoomControls()) {
+            if (mZoomButtonsController.isVisible()) {
+                mZoomButtonsController.setVisible(false);
+            }
+        } else {
+            if (mZoomControlRunnable != null) {
+                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+            }
+            if (mZoomControls != null) {
+                mZoomControls.hide();
+            }
+        }
+    }
+
     // Rule for double tap:
     // 1. if the current scale is not same as the text wrap scale and layout
     //    algorithm is NARROW_COLUMNS, fit to column;
@@ -5793,20 +5811,9 @@
         mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
         mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
         WebSettings settings = getSettings();
-        // remove the zoom control after double tap
-        if (settings.getBuiltInZoomControls()) {
-            if (mZoomButtonsController.isVisible()) {
-                mZoomButtonsController.setVisible(false);
-            }
-        } else {
-            if (mZoomControlRunnable != null) {
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-            }
-            if (mZoomControls != null) {
-                mZoomControls.hide();
-            }
-        }
         settings.setDoubleTapToastCount(0);
+        // remove the zoom control after double tap
+        dismissZoomControl();
         ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
         if (plugin != null) {
             if (isPluginFitOnScreen(plugin)) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 46769ce..24275ec 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -46,6 +46,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -230,14 +231,15 @@
      * State for keeping track of counting information.
      */
     public static class Counter extends BatteryStats.Counter implements Unpluggable {
-        int mCount;
+        final AtomicInteger mCount = new AtomicInteger();
         int mLoadedCount;
         int mLastCount;
         int mUnpluggedCount;
         int mPluggedCount;
         
         Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
-            mPluggedCount = mCount = in.readInt();
+            mPluggedCount = in.readInt();
+            mCount.set(mPluggedCount);
             mLoadedCount = in.readInt();
             mLastCount = in.readInt();
             mUnpluggedCount = in.readInt();
@@ -249,18 +251,19 @@
         }
         
         public void writeToParcel(Parcel out) {
-            out.writeInt(mCount);
+            out.writeInt(mCount.get());
             out.writeInt(mLoadedCount);
             out.writeInt(mLastCount);
             out.writeInt(mUnpluggedCount);
         }
 
         public void unplug(long batteryUptime, long batteryRealtime) {
-            mUnpluggedCount = mCount = mPluggedCount;
+            mUnpluggedCount = mPluggedCount;
+            mCount.set(mPluggedCount);
         }
 
         public void plug(long batteryUptime, long batteryRealtime) {
-            mPluggedCount = mCount;
+            mPluggedCount = mCount.get();
         }
         
         /**
@@ -285,7 +288,7 @@
             if (which == STATS_LAST) {
                 val = mLastCount;
             } else {
-                val = mCount;
+                val = mCount.get();
                 if (which == STATS_UNPLUGGED) {
                     val -= mUnpluggedCount;
                 } else if (which != STATS_TOTAL) {
@@ -297,25 +300,27 @@
         }
 
         public void logState(Printer pw, String prefix) {
-            pw.println(prefix + "mCount=" + mCount
+            pw.println(prefix + "mCount=" + mCount.get()
                     + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
                     + " mUnpluggedCount=" + mUnpluggedCount
                     + " mPluggedCount=" + mPluggedCount);
         }
         
-        void stepLocked() {
-            mCount++;
+        void stepAtomic() {
+            mCount.incrementAndGet();
         }
 
         void writeSummaryFromParcelLocked(Parcel out) {
-            out.writeInt(mCount);
-            out.writeInt(mCount - mLoadedCount);
+            int count = mCount.get();
+            out.writeInt(count);
+            out.writeInt(count - mLoadedCount);
         }
 
         void readSummaryFromParcelLocked(Parcel in) {
-            mCount = mLoadedCount = in.readInt();
+            mLoadedCount = in.readInt();
+            mCount.set(mLoadedCount);
             mLastCount = in.readInt();
-            mUnpluggedCount = mPluggedCount = mCount;
+            mUnpluggedCount = mPluggedCount = mLoadedCount;
         }
     }
 
@@ -329,8 +334,8 @@
             super(unpluggables);
         }
 
-        public void addCountLocked(long count) {
-            mCount += count;
+        public void addCountAtomic(long count) {
+            mCount.addAndGet((int)count);
         }
     }
 
@@ -1124,8 +1129,8 @@
         }
     }
     
-    public void noteInputEventLocked() {
-        mInputEventCounter.stepLocked();
+    public void noteInputEventAtomic() {
+        mInputEventCounter.stepAtomic();
     }
     
     public void noteUserActivityLocked(int uid, int event) {
@@ -1680,7 +1685,7 @@
             }
             if (type < 0) type = 0;
             else if (type >= NUM_USER_ACTIVITY_TYPES) type = NUM_USER_ACTIVITY_TYPES-1;
-            mUserActivityCounters[type].stepLocked();
+            mUserActivityCounters[type].stepAtomic();
         }
         
         @Override
@@ -2172,7 +2177,7 @@
             /* Called by ActivityManagerService when CPU times are updated. */
             public void addSpeedStepTimes(long[] values) {
                 for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
-                    mSpeedBins[i].addCountLocked(values[i]);
+                    mSpeedBins[i].addCountAtomic(values[i]);
                 }
             }
 
diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp
index 8588bc2..94e4409 100644
--- a/core/jni/android_bluetooth_ScoSocket.cpp
+++ b/core/jni/android_bluetooth_ScoSocket.cpp
@@ -404,11 +404,15 @@
     env->ReleaseStringUTFChars(address, c_address);
     data->is_accept = false;
 
-    c_name = env->GetStringUTFChars(name, NULL);
-    /* See if this device is in the black list */
-    data->sco_pkt_type = getScoType(data->address, c_name);
-    env->ReleaseStringUTFChars(name, c_name);
-
+    if (name == NULL) {
+        LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
+        data->sco_pkt_type = 0;
+    } else {
+        c_name = env->GetStringUTFChars(name, NULL);
+        /* See if this device is in the black list */
+        data->sco_pkt_type = getScoType(data->address, c_name);
+        env->ReleaseStringUTFChars(name, c_name);
+    }
     if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
         LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
         return JNI_FALSE;
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/include/utils/threads.h b/include/utils/threads.h
index 130d83c..5ac0c5e 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -209,7 +209,7 @@
 class Mutex {
 public:
     enum {
-        NORMAL = 0,
+        PRIVATE = 0,
         SHARED = 1
     };
     
@@ -305,7 +305,13 @@
  */
 class Condition {
 public:
+    enum {
+        PRIVATE = 0,
+        SHARED = 1
+    };
+
     Condition();
+    Condition(int type);
     ~Condition();
     // Wait on the condition variable.  Lock the mutex before calling.
     status_t wait(Mutex& mutex);
@@ -329,6 +335,17 @@
 inline Condition::Condition() {
     pthread_cond_init(&mCond, NULL);
 }
+inline Condition::Condition(int type) {
+    if (type == SHARED) {
+        pthread_condattr_t attr;
+        pthread_condattr_init(&attr);
+        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+        pthread_cond_init(&mCond, &attr);
+        pthread_condattr_destroy(&attr);
+    } else {
+        pthread_cond_init(&mCond, NULL);
+    }
+}
 inline Condition::~Condition() {
     pthread_cond_destroy(&mCond);
 }
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 65ce1c1..a17e8ac 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -34,7 +34,7 @@
 // ----------------------------------------------------------------------------
 
 SharedClient::SharedClient()
-    : lock(Mutex::SHARED)
+    : lock(Mutex::SHARED), cv(Condition::SHARED)
 {
 }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 104ecb4..68b351b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1258,9 +1258,9 @@
      */
     public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
     /**
-     * @hide
      * Used to indicate a temporary request of audio focus, anticipated to last a short
-     * amount of time, and where it is acceptable for other audio applications to duck.
+     * amount of time, and where it is acceptable for other audio applications to keep playing
+     * after having lowered their output level (also referred to as "ducking").
      * Examples of temporary changes are the playback of driving directions where playback of music
      * in the background is acceptable.
      * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
@@ -1278,10 +1278,9 @@
      */
     public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
     /**
-     * @hide
      * Used to indicate a transient loss of audio focus where the loser of the audio focus can
-     * duck if it wants to continue playing, as the new focus owner doesn't require others
-     * to be silent.
+     * lower its output volume if it wants to continue playing (also referred to as "ducking"), as
+     * the new focus owner doesn't require others to be silent.
      * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
      */
     public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
@@ -1297,11 +1296,12 @@
          * The focusChange value indicates whether the focus was gained,
          * whether the focus was lost, and whether that loss is transient, or whether the new focus
          * holder will hold it for an unknown amount of time.
-         * When losing focus, listeners can use the duration hint to decide what
-         * behavior to adopt when losing focus. A music player could for instance elect to duck its
-         * music stream for transient focus losses, and pause otherwise.
-         * @param focusChange one of {@link AudioManager#AUDIOFOCUS_GAIN},
+         * When losing focus, listeners can use the focus change information to decide what
+         * behavior to adopt when losing focus. A music player could for instance elect to lower
+         * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
+         * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
          *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
+         *   and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
          */
         public void onAudioFocusChanged(int focusChange);
     }
@@ -1429,6 +1429,8 @@
      *  @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
      *      is temporary, and focus will be abandonned shortly. Examples of transient requests are
      *      for the playback of driving directions, or notifications sounds.
+     *      Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
+     *      the previous focus owner to keep playing if it ducks its audio output.
      *      Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
      *      as the playback of a song or a video.
      *  @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 1992c93..ae0eccb 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -687,6 +687,7 @@
             }
             if (mode != mMode) {
                 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
+                    checkForUndispatchedAudioFocusChange(mMode, mode);
                     mMode = mode;
 
                     synchronized(mSetModeDeathHandlers) {
@@ -1816,6 +1817,40 @@
     //==========================================================================================
     // AudioFocus
     //==========================================================================================
+    /**
+     * Flag to indicate that the top of the audio focus stack needs to recover focus
+     * but hasn't been signaled yet.
+     */
+    private boolean mHasUndispatchedAudioFocus = false;
+
+    private void checkForUndispatchedAudioFocusChange(int prevMode, int newMode) {
+        // when exiting a call
+        if ((prevMode == AudioSystem.MODE_IN_CALL) && (newMode != AudioSystem.MODE_IN_CALL)) {
+            // check for undispatched remote control focus gain
+            if (mHasUndispatchedAudioFocus) {
+                notifyTopOfAudioFocusStack();
+            }
+        }
+    }
+
+    private void notifyTopOfAudioFocusStack() {
+        // notify the top of the stack it gained focus
+        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+            if (canReassignAudioFocus()) {
+                try {
+                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
+                    mHasUndispatchedAudioFocus = false;
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
+                    e.printStackTrace();
+                }
+            } else {
+                mHasUndispatchedAudioFocus = true;
+            }
+        }
+    }
+
     private static class FocusStackEntry {
         public int mStreamType = -1;// no stream type
         public boolean mIsTransportControlReceiver = false;
@@ -1871,16 +1906,7 @@
             mFocusStack.pop();
             if (signal) {
                 // notify the new top of the stack it gained focus
-                if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)
-                        && canReassignAudioFocus()) {
-                    try {
-                        mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                                AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, " Failure to signal gain of focus due to "+ e);
-                        e.printStackTrace();
-                    }
-                }
+                notifyTopOfAudioFocusStack();
             }
         } else {
             // focus is abandoned by a client that's not at the top of the stack,
@@ -1902,8 +1928,9 @@
      * Remove focus listeners from the focus stack for a particular client.
      */
     private void removeFocusStackEntryForClient(IBinder cb) {
-        // focus is abandoned by a client that's not at the top of the stack,
-        // no need to update focus.
+        // is the owner of the audio focus part of the client to remove?
+        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
+                mFocusStack.peek().mSourceRef.equals(cb);
         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
         while(stackIterator.hasNext()) {
             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
@@ -1913,6 +1940,11 @@
                 mFocusStack.remove(fse);
             }
         }
+        if (isTopOfStackForClientToRemove) {
+            // we removed an entry at the top of the stack:
+            //  notify the new top of the stack it gained focus.
+            notifyTopOfAudioFocusStack();
+        }
     }
 
     /**
@@ -1970,9 +2002,14 @@
 
         synchronized(mFocusStack) {
             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
-                mFocusStack.peek().mFocusChangeType = focusChangeHint;
-                // if focus is already owned by this client, don't do anything
-                return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                // if focus is already owned by this client and the reason for acquiring the focus
+                // hasn't changed, don't do anything
+                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                }
+                // the reason for the audio focus request has changed: remove the current top of
+                // stack and respond as if we had a new focus owner
+                mFocusStack.pop();
             }
 
             // notify current top of stack it is losing focus
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2c5cbf6..5988d34 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -52,6 +52,7 @@
  *
  * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a>
  * documentation for additional help with using MediaRecorder.
+ * <p>Note: Currently, MediaRecorder does not work on the emulator.
  */
 public class MediaRecorder
 {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 74852dc..cd7bcd5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -983,8 +983,10 @@
 // =========================================================================
 
 audio_track_cblk_t::audio_track_cblk_t()
-    : lock(Mutex::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
-    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0)
+    : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
+    userBase(0), serverBase(0), buffers(0), frameCount(0),
+    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+    flowControlFlag(1), forceReady(0)
 {
 }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d50f5910..d3ac026 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
@@ -1325,5 +1303,9 @@
         Log.e(TAG, "Got an empty response");
         return "";
     }
+
+    public void finishMediaUpdate() {
+        mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+    }
 }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 5be919d..a3f2e09 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -483,8 +483,8 @@
              * argv7 - Preamble
              * argv8 - Max SCB
              */
-            String str = String.format("softap set " + wlanIface + " " + softapIface + " %s %s %s",
-                                       wifiConfig.SSID,
+            String str = String.format("softap set " + wlanIface + " " + softapIface +
+                                       " \"%s\" %s %s", wifiConfig.SSID,
                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
                                        "wpa2-psk" : "open",
                                        wifiConfig.preSharedKey);
@@ -511,7 +511,7 @@
             mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
         } else {
             String str = String.format("softap set " + wlanIface + " " + softapIface +
-                                       " %s %s %s", wifiConfig.SSID,
+                                       " \"%s\" %s %s", wifiConfig.SSID,
                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
                                        "wpa2-psk" : "open",
                                        wifiConfig.preSharedKey);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 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/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index cdf4e95..33bbc13 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -158,9 +158,7 @@
 
     public void noteInputEvent() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteInputEventLocked();
-        }
+        mStats.noteInputEventAtomic();
     }
     
     public void noteUserActivity(int uid, int event) {
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 0ddcc247..aadd37d 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -58,7 +58,7 @@
 # The activity's onResume has been called.
 30022 am_on_resume_called (Component Name|3)
 # Kill a process to reclaim memory.
-30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5)
+30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
 # Discard an undelivered serialized broadcast (timeout/ANR/crash)
 30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
 30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
diff --git a/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/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index ce1bf8d..5780c43 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -73,6 +73,7 @@
     private static final int LAYOUT_DUMP_DATABASE_CALLBACKS = 41;
     private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
     private static final int SET_GEOLOCATION_PERMISSION = 43;
+    private static final int OVERRIDE_PREFERENCE = 44;
     
     CallbackProxy(EventSender eventSender, 
             LayoutTestController layoutTestController) {
@@ -266,6 +267,12 @@
             mLayoutTestController.setGeolocationPermission(
                     msg.arg1 == 1 ? true : false);
             break;
+
+        case OVERRIDE_PREFERENCE:
+            String key = msg.getData().getString("key");
+            boolean value = msg.getData().getBoolean("value");
+            mLayoutTestController.overridePreference(key, value);
+            break;
         }
     }
 
@@ -461,6 +468,10 @@
         WebStorage.getInstance().setQuotaForOrigin("file://", quota);
     }
 
+    public void setAppCacheMaximumSize(long size) {
+        WebStorage.getInstance().setAppCacheMaximumSize(size);
+    }
+
     public void setCanOpenWindows() {
         obtainMessage(LAYOUT_SET_CAN_OPEN_WINDOWS).sendToTarget();
     }
@@ -480,4 +491,11 @@
     public void setGeolocationPermission(boolean allow) {
         obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
     }
+
+    public void overridePreference(String key, boolean value) {
+        Message message = obtainMessage(OVERRIDE_PREFERENCE);
+        message.getData().putString("key", key);
+        message.getData().putBoolean("value", value);
+        message.sendToTarget();
+    }
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index d51d17a..d10e382 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -82,18 +82,10 @@
         // This first block of tests are for HTML5 features, for which Android
         // should pass all tests. They are skipped only temporarily.
         // TODO: Fix these failing tests and remove them from this list.
-        ignoreResultList.add("http/tests/appcache/auth.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/destroyed-frame.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/detached-iframe.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/different-scheme.html"); // file not found
-        ignoreResultList.add("http/tests/appcache/disabled.html"); // not found
+        ignoreResultList.add("http/tests/appcache/auth.html"); // DumpRenderTree throws exception when authentication fails
         ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
         ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
-        ignoreResultList.add("http/tests/appcache/max-size.html"); // no layoutTestController.setAppCacheMaximumSize
         ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
-        ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found
         ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
         ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index f535ed7..9236345 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -65,4 +65,6 @@
 
     // For Geolocation tests
     public void setGeolocationPermission(boolean allow);
+
+    public void overridePreference(String key, boolean value);
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 02a7046..24f58b2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -61,6 +61,9 @@
 
     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
 
+    // String constants for use with layoutTestController.overridePreferences
+    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled";
+
     public class AsyncHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -459,6 +462,16 @@
         mGeolocationPermission = allow;
     }
 
+    public void overridePreference(String key, boolean value) {
+        // TODO: We should look up the correct WebView for the frame which
+        // called the layoutTestController method. Currently, we just use the
+        // WebView for the main frame. EventSender suffers from the same
+        // problem.
+        if (key.equals(WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
+            mWebView.getSettings().setAppCacheEnabled(value);
+        }
+    }
+
     private final WebViewClient mViewClient = new WebViewClient(){
         @Override
         public void onPageFinished(WebView view, String url) {