Merge "Do not send the not-modified response when validating the cache."
diff --git a/common/java/com/android/common/speech/Recognition.java b/common/java/com/android/common/speech/Recognition.java
index a79a19b..1970179 100644
--- a/common/java/com/android/common/speech/Recognition.java
+++ b/common/java/com/android/common/speech/Recognition.java
@@ -55,6 +55,7 @@
     public static final int HINT_CONTEXT_UNKNOWN = 0;
     public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1;
     public static final int HINT_CONTEXT_CAR_HOME = 2;
+    public static final int HINT_CONTEXT_LAUNCHER = 3;
 
     private Recognition() { }   // don't instantiate
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7c49bb7..b0a59c7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -39,6 +39,7 @@
 import android.content.res.Resources;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDebug;
+import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.os.Bundle;
@@ -1441,6 +1442,7 @@
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
         private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
+        private static final String DB_INFO_FORMAT = "  %8d %8d %10d  %s";
 
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
@@ -1760,8 +1762,7 @@
             int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
             int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount();
             long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
-            SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats();
-            SQLiteDebug.getPagerStats(stats);
+            SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
 
             // Check to see if we were called by checkin server. If so, print terse format.
             boolean doCheckinFormat = false;
@@ -1835,10 +1836,15 @@
 
                 // SQL
                 pw.print(sqliteAllocated); pw.print(',');
-                pw.print(stats.databaseBytes / 1024); pw.print(',');
-                pw.print(stats.numPagers); pw.print(',');
-                pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(',');
-                pw.print(stats.referencedBytes / 1024); pw.print('\n');
+                pw.print(stats.memoryUsed / 1024); pw.print(',');
+                pw.print(stats.pageCacheOverflo / 1024); pw.print(',');
+                pw.print(stats.largestMemAlloc / 1024); pw.print(',');
+                for (int i = 0; i < stats.dbStats.size(); i++) {
+                    DbStats dbStats = stats.dbStats.get(i);
+                    printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
+                            dbStats.lookaside, dbStats.dbName);
+                    pw.print(',');
+                }
 
                 return;
             }
@@ -1879,11 +1885,21 @@
             // SQLite mem info
             pw.println(" ");
             pw.println(" SQL");
-            printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:",
-                    stats.databaseBytes / 1024);
-            printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:",
-                    (stats.totalBytes - stats.referencedBytes) / 1024);
-            printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024);
+            printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "memoryUsed:",
+                    stats.memoryUsed / 1024);
+            printRow(pw, TWO_COUNT_COLUMNS, "pageCacheOverflo:", stats.pageCacheOverflo / 1024,
+                    "largestMemAlloc:", stats.largestMemAlloc / 1024);
+            pw.println(" ");
+            int N = stats.dbStats.size();
+            if (N > 0) {
+                pw.println(" DATABASES");
+                printRow(pw, "  %8s %8s %10s  %s", "Pagesize", "Dbsize", "Lookaside", "Dbname");
+                for (int i = 0; i < N; i++) {
+                    DbStats dbStats = stats.dbStats.get(i);
+                    printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
+                            dbStats.lookaside, dbStats.dbName);
+                }
+            }
 
             // Asset details.
             String assetAlloc = AssetManager.getAssetAllocations();
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 71fa2c2..1830f6c 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -32,7 +32,7 @@
         synchronized(mLock) {
             if (mReferenceCount <= 0) {
                 throw new IllegalStateException(
-                        "attempt to acquire a reference on an already-closed " + getObjInfo());
+                        "attempt to re-open an already-closed object: " + getObjInfo());
             }
             mReferenceCount++;
         }
@@ -59,7 +59,6 @@
     private String getObjInfo() {
         StringBuilder buff = new StringBuilder();
         buff.append(this.getClass().getName());
-        buff.append(" Obj");
         buff.append(" (");
         if (this instanceof SQLiteDatabase) {
             buff.append("database = ");
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index dfd4024..b232ff9 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -23,6 +23,7 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.SQLException;
+import android.database.sqlite.SQLiteDebug.DbStats;
 import android.os.Debug;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -30,10 +31,14 @@
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Pair;
 
 import java.io.File;
+import java.lang.ref.WeakReference;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -787,25 +792,27 @@
      * @throws SQLiteException if the database cannot be opened
      */
     public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
-        SQLiteDatabase db = null;
+        SQLiteDatabase sqliteDatabase = null;
         try {
             // Open the database.
-            SQLiteDatabase sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+            sqliteDatabase = new SQLiteDatabase(path, factory, flags);
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 sqliteDatabase.enableSqlTracing(path);
             }
             if (SQLiteDebug.DEBUG_SQL_TIME) {
                 sqliteDatabase.enableSqlProfiling(path);
             }
-            return sqliteDatabase;
         } catch (SQLiteDatabaseCorruptException e) {
             // Try to recover from this, if we can.
             // TODO: should we do this for other open failures?
             Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
             EventLog.writeEvent(EVENT_DB_CORRUPT, path);
             new File(path).delete();
-            return new SQLiteDatabase(path, factory, flags);
+            sqliteDatabase = new SQLiteDatabase(path, factory, flags);
         }
+        ActiveDatabases.getInstance().mActiveDatabases.add(
+                new WeakReference<SQLiteDatabase>(sqliteDatabase));
+        return sqliteDatabase;
     }
 
     /**
@@ -2040,6 +2047,88 @@
         mMaxSqlCacheSize = cacheSize;
     }
 
+    static class ActiveDatabases {
+        private static final ActiveDatabases activeDatabases = new ActiveDatabases();
+        private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
+            new HashSet<WeakReference<SQLiteDatabase>>();
+        private ActiveDatabases() {} // disable instantiation of this class
+        static ActiveDatabases getInstance() {return activeDatabases;}
+    }
+
+    /* package */ static ArrayList<DbStats> getDbStats() {
+        ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
+        for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
+            SQLiteDatabase db = w.get();
+            if (db == null || !db.isOpen()) {
+                continue;
+            }
+            // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+            int lookasideUsed = db.native_getDbLookaside();
+
+            // get the lastnode of the dbname
+            String path = db.getPath();
+            int indx = path.lastIndexOf("/");
+            String lastnode = path.substring((indx != -1) ? ++indx : 0);
+
+            // get list of attached dbs and for each db, get its size and pagesize
+            ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
+            for (int i = 0; i < attachedDbs.size(); i++) {
+                Pair<String, String> p = attachedDbs.get(i);
+                long pageCount = getPragmaVal(db, p.first + ".page_count;");
+
+                // first entry in the attached db list is always the main database
+                // don't worry about prefixing the dbname with "main"
+                String dbName;
+                if (i == 0) {
+                    dbName = lastnode;
+                } else {
+                    // lookaside is only relevant for the main db
+                    lookasideUsed = 0;
+                    dbName = "  (attached) " + p.first;
+                    // if the attached db has a path, attach the lastnode from the path to above
+                    if (p.second.trim().length() > 0) {
+                        int idx = p.second.lastIndexOf("/");
+                        dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+                    }
+                }
+                dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(), lookasideUsed));
+            }
+        }
+        return dbStatsList;
+    }
+
+    /**
+     * get the specified pragma value from sqlite for the specified database.
+     * only handles pragma's that return int/long.
+     * NO JAVA locks are held in this method.
+     * TODO: use this to do all pragma's in this class
+     */
+    private static long getPragmaVal(SQLiteDatabase db, String pragma) {
+        SQLiteStatement prog = null;
+        try {
+            prog = new SQLiteStatement(db, "PRAGMA " + pragma);
+            long val = prog.simpleQueryForLong();
+            return val;
+        } finally {
+            if (prog != null) prog.close();
+        }
+    }
+
+    /**
+     * returns list of full pathnames of all attached databases
+     * including the main database
+     * TODO: move this to {@link DatabaseUtils}
+     */
+    private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
+        ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
+        Cursor c = dbObj.rawQuery("pragma database_list;", null);
+        while (c.moveToNext()) {
+             attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+        }
+        c.close();
+        return attachedDbs;
+    }
+
     /**
      * Native call to open the database.
      *
@@ -2093,4 +2182,11 @@
      * @return the number of changes made in the last statement executed.
      */
     /* package */ native int lastChangeCount();
+
+    /**
+     * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
+     * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
+     * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
+     */
+    private native int native_getDbLookaside();
 }
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 4ea680e..a4db6d9 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -16,6 +16,8 @@
 
 package android.database.sqlite;
 
+import java.util.ArrayList;
+
 import android.util.Log;
 
 /**
@@ -68,14 +70,87 @@
      * @see #getPagerStats(PagerStats)
      */
     public static class PagerStats {
-        /** The total number of bytes in all pagers in the current process */
+        /** The total number of bytes in all pagers in the current process
+         * @deprecated not used any longer
+         */
+        @Deprecated
         public long totalBytes;
-        /** The number of bytes in referenced pages in all pagers in the current process */
+        /** The number of bytes in referenced pages in all pagers in the current process
+         * @deprecated not used any longer
+         * */
+        @Deprecated
         public long referencedBytes;
-        /** The number of bytes in all database files opened in the current process */
+        /** The number of bytes in all database files opened in the current process
+         * @deprecated not used any longer
+         */
+        @Deprecated
         public long databaseBytes;
-        /** The number of pagers opened in the current process */
+        /** The number of pagers opened in the current process
+         * @deprecated not used any longer
+         */
+        @Deprecated
         public int numPagers;
+
+        /** the current amount of memory checked out by sqlite using sqlite3_malloc().
+         * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
+         */
+        public int memoryUsed;
+
+        /** the number of bytes of page cache allocation which could not be sattisfied by the
+         * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc().
+         * The returned value includes allocations that overflowed because they where too large
+         * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations
+         * that overflowed because no space was left in the page cache.
+         * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
+         */
+        public int pageCacheOverflo;
+
+        /** records the largest memory allocation request handed to sqlite3.
+         * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
+         */
+        public int largestMemAlloc;
+
+        /** a list of {@link DbStats} - one for each main database opened by the applications
+         * running on the android device
+         */
+        public ArrayList<DbStats> dbStats;
+    }
+
+    /**
+     * contains statistics about a database
+     * @author vnori@google.com (Your Name Here)
+     *
+     */
+    public static class DbStats {
+        /** name of the database */
+        public String dbName;
+
+        /** the page size for the database */
+        public long pageSize;
+
+        /** the database size */
+        public long dbSize;
+
+        /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
+        public int lookaside;
+
+        public DbStats(String dbName, long pageCount, long pageSize, int lookaside) {
+            this.dbName = dbName;
+            this.pageSize = pageSize;
+            dbSize = (pageCount * pageSize) / 1024;
+            this.lookaside = lookaside;
+        }
+    }
+
+    /**
+     * return all pager and database stats for the current process.
+     * @return {@link PagerStats}
+     */
+    public static PagerStats getDatabaseInfo() {
+        PagerStats stats = new PagerStats();
+        getPagerStats(stats);
+        stats.dbStats = SQLiteDatabase.getDbStats();
+        return stats;
     }
 
     /**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 3dd3918..b827af8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -117,7 +117,10 @@
      * exception.
      *
      * Verification of a package can take significant time, so this
-     * function should not be called from a UI thread.
+     * function should not be called from a UI thread.  Interrupting
+     * the thread while this function is in progress will result in a
+     * SecurityException being thrown (and the thread's interrupt flag
+     * will be cleared).
      *
      * @param packageFile  the package to be verified
      * @param listener     an object to receive periodic progress
@@ -259,7 +262,10 @@
             long soFar = 0;
             raf.seek(0);
             byte[] buffer = new byte[4096];
+            boolean interrupted = false;
             while (soFar < toRead) {
+                interrupted = Thread.interrupted();
+                if (interrupted) break;
                 int size = buffer.length;
                 if (soFar + size > toRead) {
                     size = (int)(toRead - soFar);
@@ -283,6 +289,10 @@
                 listener.onProgress(100);
             }
 
+            if (interrupted) {
+                throw new SignatureException("verification was interrupted");
+            }
+
             if (!sig.verify(sigInfo.getEncryptedDigest())) {
                 throw new SignatureException("signature digest verification failed");
             }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 2b78d2d..acdfc28 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -105,7 +105,7 @@
      * Distance a touch can wander before we think the user is attempting a paged scroll
      * (in dips)
      */
-    private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 3;
+    private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;
     
     /**
      * Distance between the first touch and second touch to still be considered a double tap
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 75db0a0..47e7791 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -176,4 +176,19 @@
             }
         });
     }
+
+    ChildView hitTest(int contentX, int contentY) {
+        if (mHidden) {
+            return null;
+        }
+        for (ChildView v : mChildren) {
+            if (v.mView.getVisibility() == View.VISIBLE) {
+                if (contentX >= v.x && contentX < (v.x + v.width)
+                        && contentY >= v.y && contentY < (v.y + v.height)) {
+                    return v;
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5099cd4..825ea46 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -479,8 +479,6 @@
     private boolean mSupportMultiTouch;
     // use the framework's ScaleGestureDetector to handle multi-touch
     private ScaleGestureDetector mScaleDetector;
-    // minimum scale change during multi-touch zoom
-    private static float PREVIEW_SCALE_INCREMENT = 0.01f;
 
     // the anchor point in the document space where VIEW_SIZE_CHANGED should
     // apply to
@@ -619,6 +617,8 @@
     static int DEFAULT_SCALE_PERCENT;
     private float mDefaultScale;
 
+    private static float MINIMUM_SCALE_INCREMENT = 0.01f;
+
     // set to true temporarily during ScaleGesture triggered zoom
     private boolean mPreviewZoomOnly = false;
 
@@ -2306,7 +2306,8 @@
             boolean clampedY) {
         mInOverScrollMode = false;
         int maxX = computeMaxScrollX();
-        if (maxX == 0 && (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f)
+        if (maxX == 0 && (Math.abs(mMinZoomScale - mMaxZoomScale)
+                < MINIMUM_SCALE_INCREMENT)
                 || !getSettings().supportZoom()
                 || !getSettings().getUseWideViewPort()) {
             // do not over scroll x if the page just fits the screen and it
@@ -4466,7 +4467,8 @@
                 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
                 // don't reflow when zoom in; when zoom out, do reflow if the
                 // new scale is almost minimum scale;
-                boolean reflowNow = (mActualScale - mMinZoomScale <= 0.01f)
+                boolean reflowNow = (mActualScale - mMinZoomScale
+                        <= MINIMUM_SCALE_INCREMENT)
                         || ((mActualScale <= 0.8 * mTextWrapScale));
                 // force zoom after mPreviewZoomOnly is set to false so that the
                 // new view size will be passed to the WebKit
@@ -4493,7 +4495,7 @@
         public boolean onScale(ScaleGestureDetector detector) {
             float scale = (float) (Math.round(detector.getScaleFactor()
                     * mActualScale * 100) / 100.0);
-            if (Math.abs(scale - mActualScale) >= PREVIEW_SCALE_INCREMENT) {
+            if (Math.abs(scale - mActualScale) >= MINIMUM_SCALE_INCREMENT) {
                 mPreviewZoomOnly = true;
                 // limit the scale change per step
                 if (scale > mActualScale) {
@@ -5430,17 +5432,17 @@
         invalidate();
     }
 
-    private boolean zoomWithPreview(float scale) {
+    private boolean zoomWithPreview(float scale, boolean updateTextWrapScale) {
         float oldScale = mActualScale;
         mInitialScrollX = mScrollX;
         mInitialScrollY = mScrollY;
 
         // snap to DEFAULT_SCALE if it is close
-        if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
+        if (Math.abs(scale - mDefaultScale) < MINIMUM_SCALE_INCREMENT) {
             scale = mDefaultScale;
         }
 
-        setNewZoomScale(scale, true, false);
+        setNewZoomScale(scale, updateTextWrapScale, false);
 
         if (oldScale != mActualScale) {
             // use mZoomPickerScale to see zoom preview first
@@ -5552,7 +5554,7 @@
         mZoomCenterY = getViewHeight() * .5f;
         mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
         mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        return zoomWithPreview(mActualScale * 1.25f);
+        return zoomWithPreview(mActualScale * 1.25f, true);
     }
 
     /**
@@ -5567,7 +5569,7 @@
         mZoomCenterY = getViewHeight() * .5f;
         mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
         mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        return zoomWithPreview(mActualScale * 0.8f);
+        return zoomWithPreview(mActualScale * 0.8f, true);
     }
 
     private void updateSelection() {
@@ -5692,6 +5694,84 @@
         }
     }
 
+    /*
+     * Return true if the view (Plugin) is fully visible and maximized inside
+     * the WebView.
+     */
+    private boolean isPluginFitOnScreen(ViewManager.ChildView view) {
+        int viewWidth = getViewWidth();
+        int viewHeight = getViewHeightWithTitle();
+        float scale = Math.min((float) viewWidth / view.width,
+                (float) viewHeight / view.height);
+        if (scale < mMinZoomScale) {
+            scale = mMinZoomScale;
+        } else if (scale > mMaxZoomScale) {
+            scale = mMaxZoomScale;
+        }
+        if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
+            if (contentToViewX(view.x) >= mScrollX
+                    && contentToViewX(view.x + view.width) <= mScrollX
+                            + viewWidth
+                    && contentToViewY(view.y) >= mScrollY
+                    && contentToViewY(view.y + view.height) <= mScrollY
+                            + viewHeight) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * Maximize and center the view inside the WebView. If the zoom doesn't need
+     * to be changed, do an animated scroll to center it. If the zoom needs to
+     * be changed, find the zoom center and do a smooth zoom transition.
+     */
+    private void centerPluginOnScreen(ViewManager.ChildView child) {
+        int viewWidth = getViewWidth();
+        int viewHeight = getViewHeightWithTitle();
+        float scale = Math.min((float) viewWidth / child.width,
+                (float) viewHeight / child.height);
+        if (scale < mMinZoomScale) {
+            scale = mMinZoomScale;
+        } else if (scale > mMaxZoomScale) {
+            scale = mMaxZoomScale;
+        }
+        if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
+            pinScrollTo(
+                    contentToViewX(child.x + child.width / 2) - viewWidth / 2,
+                    contentToViewY(child.y + child.height / 2) - viewHeight / 2,
+                    true, 0);
+        } else {
+            int oldScreenX = contentToViewX(child.x) - mScrollX;
+            int newPluginX = (int) (child.x * scale);
+            int newPluginWidth = (int) (child.width * scale);
+            int newMaxWidth = (int) (mContentWidth * scale);
+            int newScreenX = (viewWidth - newPluginWidth) / 2;
+            // pin the newX to the WebView
+            if (newScreenX > newPluginX) {
+                newScreenX = newPluginX;
+            } else if (newScreenX > (newMaxWidth - newPluginX - newPluginWidth)) {
+                newScreenX = viewWidth - (newMaxWidth - newPluginX);
+            }
+            mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale)
+                    / (scale - mActualScale);
+            int oldScreenY = contentToViewY(child.y) - mScrollY;
+            int newPluginY = (int) (child.y * scale) + getTitleHeight();
+            int newPluginHeight = (int) (child.height * scale);
+            int newMaxHeight = (int) (mContentHeight * scale) + getTitleHeight();
+            int newScreenY = (viewHeight - newPluginHeight) / 2;
+            // pin the newY to the WebView
+            if (newScreenY > newPluginY) {
+                newScreenY = newPluginY;
+            } else if (newScreenY > (newMaxHeight - newPluginY - newPluginHeight)) {
+                newScreenY = viewHeight - (newMaxHeight - newPluginY);
+            }
+            mZoomCenterY = (oldScreenY * scale - newScreenY * mActualScale)
+                    / (scale - mActualScale);
+            zoomWithPreview(scale, false);
+        }
+    }
+
     // 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;
@@ -5720,22 +5800,36 @@
             }
         }
         settings.setDoubleTapToastCount(0);
+        ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
+        if (plugin != null) {
+            if (isPluginFitOnScreen(plugin)) {
+                mInZoomOverview = true;
+                // Force the titlebar fully reveal in overview mode
+                if (mScrollY < getTitleHeight()) mScrollY = 0;
+                zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth,
+                        true);
+            } else {
+                mInZoomOverview = false;
+                centerPluginOnScreen(plugin);
+            }
+            return;
+        }
         boolean zoomToDefault = false;
         if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
-                && (Math.abs(mActualScale - mTextWrapScale) >= 0.01f)) {
+                && (Math.abs(mActualScale - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT)) {
             setNewZoomScale(mActualScale, true, true);
             float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
-            if (Math.abs(mActualScale - overviewScale) < 0.01f) {
+            if (Math.abs(mActualScale - overviewScale) < MINIMUM_SCALE_INCREMENT) {
                 mInZoomOverview = true;
             }
         } else if (!mInZoomOverview) {
             float newScale = (float) getViewWidth() / mZoomOverviewWidth;
-            if (Math.abs(mActualScale - newScale) >= 0.01f) {
+            if (Math.abs(mActualScale - newScale) >= MINIMUM_SCALE_INCREMENT) {
                 mInZoomOverview = true;
                 // Force the titlebar fully reveal in overview mode
                 if (mScrollY < getTitleHeight()) mScrollY = 0;
-                zoomWithPreview(newScale);
-            } else if (Math.abs(mActualScale - mDefaultScale) >= 0.01f) {
+                zoomWithPreview(newScale, true);
+            } else if (Math.abs(mActualScale - mDefaultScale) >= MINIMUM_SCALE_INCREMENT) {
                 zoomToDefault = true;
             }
         } else {
@@ -5758,7 +5852,7 @@
                     mZoomCenterX = 0;
                 }
             }
-            zoomWithPreview(mDefaultScale);
+            zoomWithPreview(mDefaultScale, true);
         }
     }
 
@@ -6103,7 +6197,8 @@
                                     scale = restoreState.mTextWrapScale;
                                 }
                                 setNewZoomScale(scale, Math.abs(scale
-                                    - mTextWrapScale) >= 0.01f, false);
+                                    - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT,
+                                    false);
                             }
                             setContentScrollTo(restoreState.mScrollX,
                                 restoreState.mScrollY);
@@ -6156,7 +6251,8 @@
                                 - mZoomOverviewWidth) > 1) {
                             setNewZoomScale((float) viewWidth
                                     / mZoomOverviewWidth, Math.abs(mActualScale
-                                            - mTextWrapScale) < 0.01f, false);
+                                    - mTextWrapScale) < MINIMUM_SCALE_INCREMENT,
+                                    false);
                         }
                     }
                     if (draw.mFocusSizeChanged && inEditingMode()) {
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 44e091d..fb93014 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -306,6 +306,16 @@
     return sqlite3_changes(handle);
 }
 
+/* native int native_getDbLookaside(); */
+static jint native_getDbLookaside(JNIEnv* env, jobject object)
+{
+    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
+    int pCur = -1;
+    int unused;
+    sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
+    return pCur;
+}
+
 /* set locale in the android_metadata table, install localized collators, and rebuild indexes */
 static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
 {
@@ -442,6 +452,7 @@
     {"lastInsertRow", "()J", (void *)lastInsertRow},
     {"lastChangeCount", "()I", (void *)lastChangeCount},
     {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
+    {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
     {"releaseMemory", "()I", (void *)native_releaseMemory},
 };
 
diff --git a/core/jni/android_database_SQLiteDebug.cpp b/core/jni/android_database_SQLiteDebug.cpp
index 916df35..873b2a1 100644
--- a/core/jni/android_database_SQLiteDebug.cpp
+++ b/core/jni/android_database_SQLiteDebug.cpp
@@ -29,36 +29,28 @@
 // From mem_mspace.c in libsqlite
 extern "C" mspace sqlite3_get_mspace();
 
-// From sqlite.c, hacked in for Android
-extern "C" void sqlite3_get_pager_stats(sqlite3_int64 * totalBytesOut,
-                                       sqlite3_int64 * referencedBytesOut,
-                                       sqlite3_int64 * dbBytesOut,
-                                       int * numPagersOut);
-
 namespace android {
 
-static jfieldID gTotalBytesField;
-static jfieldID gReferencedBytesField;
-static jfieldID gDbBytesField;
-static jfieldID gNumPagersField;
+static jfieldID gMemoryUsedField;
+static jfieldID gPageCacheOverfloField;
+static jfieldID gLargestMemAllocField;
 
 
 #define USE_MSPACE 0
 
 static void getPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
 {
-    sqlite3_int64 totalBytes;
-    sqlite3_int64 referencedBytes;
-    sqlite3_int64 dbBytes;
-    int numPagers;
+    int memoryUsed;
+    int pageCacheOverflo;
+    int largestMemAlloc;
+    int unused;
 
-    sqlite3_get_pager_stats(&totalBytes, &referencedBytes, &dbBytes,
-            &numPagers);
-
-    env->SetLongField(statsObj, gTotalBytesField, totalBytes);
-    env->SetLongField(statsObj, gReferencedBytesField, referencedBytes);
-    env->SetLongField(statsObj, gDbBytesField, dbBytes);
-    env->SetIntField(statsObj, gNumPagersField, numPagers);
+    sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0);
+    sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0);
+    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflo, &unused, 0);
+    env->SetIntField(statsObj, gMemoryUsedField, memoryUsed);
+    env->SetIntField(statsObj, gPageCacheOverfloField, pageCacheOverflo);
+    env->SetIntField(statsObj, gLargestMemAllocField, largestMemAlloc);
 }
 
 static jlong getHeapSize(JNIEnv *env, jobject clazz)
@@ -213,27 +205,21 @@
         return -1;
     }
 
-    gTotalBytesField = env->GetFieldID(clazz, "totalBytes", "J");
-    if (gTotalBytesField == NULL) {
-        LOGE("Can't find totalBytes");
+    gMemoryUsedField = env->GetFieldID(clazz, "memoryUsed", "I");
+    if (gMemoryUsedField == NULL) {
+        LOGE("Can't find memoryUsed");
         return -1;
     }
 
-    gReferencedBytesField = env->GetFieldID(clazz, "referencedBytes", "J");
-    if (gReferencedBytesField == NULL) {
-        LOGE("Can't find referencedBytes");
+    gLargestMemAllocField = env->GetFieldID(clazz, "largestMemAlloc", "I");
+    if (gLargestMemAllocField == NULL) {
+        LOGE("Can't find largestMemAlloc");
         return -1;
     }
 
-    gDbBytesField = env->GetFieldID(clazz, "databaseBytes", "J");
-    if (gDbBytesField == NULL) {
-        LOGE("Can't find databaseBytes");
-        return -1;
-    }
-
-    gNumPagersField = env->GetFieldID(clazz, "numPagers", "I");
-    if (gNumPagersField == NULL) {
-        LOGE("Can't find numPagers");
+    gPageCacheOverfloField = env->GetFieldID(clazz, "pageCacheOverflo", "I");
+    if (gPageCacheOverfloField == NULL) {
+        LOGE("Can't find pageCacheOverflo");
         return -1;
     }
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f5e2f1d..06e9bbf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1748,9 +1748,9 @@
              The default value is true. -->
         <attr name="footerDividersEnabled" format="boolean" />
         <!-- Drawable to draw above list content. -->
-        <attr name="overscrollHeader" format="reference" />
+        <attr name="overscrollHeader" format="reference|color" />
         <!-- Drawable to draw below list content. -->
-        <attr name="overscrollFooter" format="reference" />
+        <attr name="overscrollFooter" format="reference|color" />
     </declare-styleable>
     <declare-styleable name="MenuView">
         <!-- Default appearance of menu item text. -->
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index f623295b..c6bbbcc 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -159,8 +159,10 @@
 status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
 {
     Mutex::Autolock _l(mLock);
-    if ((mPlayer != NULL) && ( mCurrentState & MEDIA_PLAYER_INITIALIZED ))
-    {
+    const bool hasBeenInitialized =
+            (mCurrentState != MEDIA_PLAYER_STATE_ERROR) &&
+            ((mCurrentState & MEDIA_PLAYER_IDLE) != MEDIA_PLAYER_IDLE);
+    if ((mPlayer != NULL) && hasBeenInitialized) {
          LOGV("invoke %d", request.dataSize());
          return  mPlayer->invoke(request, reply);
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 78d8c49..da9232b 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -53,6 +53,7 @@
 import android.os.SystemProperties;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
@@ -106,6 +107,7 @@
 
     // for enabling and disabling notification pulse behavior
     private boolean mScreenOn = true;
+    private boolean mInCall = false;
     private boolean mNotificationPulseEnabled;
 
     // for adb connected notifications
@@ -362,6 +364,9 @@
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 mScreenOn = false;
                 updateNotificationPulse();
+            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
+                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
+                updateNotificationPulse();
             }
         }
     };
@@ -444,6 +449,7 @@
         filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
         mContext.registerReceiver(mIntentReceiver, filter);
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -1064,7 +1070,8 @@
         }
 
         // we only flash if screen is off and persistent pulsing is enabled
-        if (mLedNotification == null || mScreenOn) {
+        // and we are not currently in a call
+        if (mLedNotification == null || mScreenOn || mInCall) {
             mNotificationLight.turnOff();
         } else {
             int ledARGB = mLedNotification.notification.ledARGB;