Merge "Add multisampling support to renderscript."
diff --git a/Android.mk b/Android.mk
index 79f6220..5572c50 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,6 +127,7 @@
 	core/java/android/os/IPermissionController.aidl \
 	core/java/android/os/IPowerManager.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
+	core/java/android/os/IUpdateLock.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
diff --git a/api/16.txt b/api/16.txt
index be544de..357d618 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -27303,7 +27303,6 @@
     method public boolean onTextContextMenuItem(int);
     method public void removeTextChangedListener(android.text.TextWatcher);
     method protected void resetResolvedDrawables();
-    method protected void resetResolvedLayoutDirection();
     method protected void resolveDrawables();
     method protected void resolveTextDirection();
     method public void setAllCaps(boolean);
diff --git a/api/current.txt b/api/current.txt
index fffa1fe..3fa7ad9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -414,7 +414,7 @@
     field public static final int ems = 16843096; // 0x1010158
     field public static final deprecated int enabled = 16842766; // 0x101000e
     field public static final int endColor = 16843166; // 0x101019e
-    field public static final int endYear = 16843133; // 0x101017d
+    field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
     field public static final int entries = 16842930; // 0x10100b2
     field public static final int entryValues = 16843256; // 0x10101f8
@@ -593,6 +593,7 @@
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
+    field public static final int layoutDirection = 16843689; // 0x10103a9
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
     field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -614,10 +615,10 @@
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
     field public static final int layout_marginBottom = 16843002; // 0x10100fa
-    field public static final int layout_marginEnd = 16843692; // 0x10103ac
+    field public static final int layout_marginEnd = 16843693; // 0x10103ad
     field public static final int layout_marginLeft = 16842999; // 0x10100f7
     field public static final int layout_marginRight = 16843001; // 0x10100f9
-    field public static final int layout_marginStart = 16843691; // 0x10103ab
+    field public static final int layout_marginStart = 16843692; // 0x10103ac
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -713,10 +714,10 @@
     field public static final int packageNames = 16843649; // 0x1010381
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
-    field public static final int paddingEnd = 16843690; // 0x10103aa
+    field public static final int paddingEnd = 16843691; // 0x10103ab
     field public static final int paddingLeft = 16842966; // 0x10100d6
     field public static final int paddingRight = 16842968; // 0x10100d8
-    field public static final int paddingStart = 16843689; // 0x10103a9
+    field public static final int paddingStart = 16843690; // 0x10103aa
     field public static final int paddingTop = 16842967; // 0x10100d7
     field public static final int panelBackground = 16842846; // 0x101005e
     field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -887,7 +888,7 @@
     field public static final int starStyle = 16842882; // 0x1010082
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startOffset = 16843198; // 0x10101be
-    field public static final int startYear = 16843132; // 0x101017c
+    field public static final deprecated int startYear = 16843132; // 0x101017c
     field public static final int stateNotNeeded = 16842774; // 0x1010016
     field public static final int state_above_anchor = 16842922; // 0x10100aa
     field public static final int state_accelerated = 16843547; // 0x101031b
@@ -7275,6 +7276,7 @@
     method public android.database.sqlite.SQLiteStatement compileStatement(java.lang.String) throws android.database.SQLException;
     method public static android.database.sqlite.SQLiteDatabase create(android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public int delete(java.lang.String, java.lang.String, java.lang.String[]);
+    method public static boolean deleteDatabase(java.io.File);
     method public boolean enableWriteAheadLogging();
     method public void endTransaction();
     method public void execSQL(java.lang.String) throws android.database.SQLException;
@@ -7392,8 +7394,8 @@
     ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public synchronized void close();
     method public java.lang.String getDatabaseName();
-    method public synchronized android.database.sqlite.SQLiteDatabase getReadableDatabase();
-    method public synchronized android.database.sqlite.SQLiteDatabase getWritableDatabase();
+    method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
+    method public android.database.sqlite.SQLiteDatabase getWritableDatabase();
     method public abstract void onCreate(android.database.sqlite.SQLiteDatabase);
     method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
     method public void onOpen(android.database.sqlite.SQLiteDatabase);
@@ -7580,6 +7582,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
+    method public void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
@@ -7616,11 +7619,11 @@
   }
 
   public class DrmStore {
-    ctor public DrmStore();
+    ctor public deprecated DrmStore();
   }
 
   public static class DrmStore.Action {
-    ctor public DrmStore.Action();
+    ctor public deprecated DrmStore.Action();
     field public static final int DEFAULT = 0; // 0x0
     field public static final int DISPLAY = 7; // 0x7
     field public static final int EXECUTE = 6; // 0x6
@@ -7641,7 +7644,7 @@
   }
 
   public static class DrmStore.DrmObjectType {
-    ctor public DrmStore.DrmObjectType();
+    ctor public deprecated DrmStore.DrmObjectType();
     field public static final int CONTENT = 1; // 0x1
     field public static final int RIGHTS_OBJECT = 2; // 0x2
     field public static final int TRIGGER_OBJECT = 3; // 0x3
@@ -7649,7 +7652,7 @@
   }
 
   public static class DrmStore.Playback {
-    ctor public DrmStore.Playback();
+    ctor public deprecated DrmStore.Playback();
     field public static final int PAUSE = 2; // 0x2
     field public static final int RESUME = 3; // 0x3
     field public static final int START = 0; // 0x0
@@ -7657,7 +7660,7 @@
   }
 
   public static class DrmStore.RightsStatus {
-    ctor public DrmStore.RightsStatus();
+    ctor public deprecated DrmStore.RightsStatus();
     field public static final int RIGHTS_EXPIRED = 2; // 0x2
     field public static final int RIGHTS_INVALID = 1; // 0x1
     field public static final int RIGHTS_NOT_ACQUIRED = 3; // 0x3
@@ -7668,7 +7671,8 @@
     ctor public DrmSupportInfo();
     method public void addFileSuffix(java.lang.String);
     method public void addMimeType(java.lang.String);
-    method public java.lang.String getDescriprition();
+    method public deprecated java.lang.String getDescriprition();
+    method public java.lang.String getDescription();
     method public java.util.Iterator<java.lang.String> getFileSuffixIterator();
     method public java.util.Iterator<java.lang.String> getMimeTypeIterator();
     method public void setDescription(java.lang.String);
@@ -11814,6 +11818,7 @@
     enum_constant public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
     enum_constant public static final android.net.NetworkInfo.DetailedState SCANNING;
     enum_constant public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+    enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
   }
 
   public static final class NetworkInfo.State extends java.lang.Enum {
@@ -15087,7 +15092,7 @@
     field public static final android.os.Parcelable.Creator STRING_CREATOR;
   }
 
-  public class ParcelFileDescriptor implements android.os.Parcelable {
+  public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
     ctor public ParcelFileDescriptor(android.os.ParcelFileDescriptor);
     method public static android.os.ParcelFileDescriptor adoptFd(int);
     method public void close() throws java.io.IOException;
@@ -15342,6 +15347,7 @@
     method public abstract void acquired();
     method public void cleanup(android.os.IBinder, boolean);
     method public void dump();
+    method public void dump(java.io.PrintWriter);
     method public boolean isAcquired();
     method public void release(android.os.IBinder);
     method public abstract void released();
@@ -15997,6 +16003,7 @@
     field public static final java.lang.String CALENDAR_ID = "calendar_id";
     field public static final java.lang.String CAN_INVITE_OTHERS = "canInviteOthers";
     field public static final java.lang.String DESCRIPTION = "description";
+    field public static final java.lang.String DISPLAY_COLOR = "displayColor";
     field public static final java.lang.String DTEND = "dtend";
     field public static final java.lang.String DTSTART = "dtstart";
     field public static final java.lang.String DURATION = "duration";
@@ -16080,6 +16087,7 @@
   protected static abstract interface CalendarContract.RemindersColumns {
     field public static final java.lang.String EVENT_ID = "event_id";
     field public static final java.lang.String METHOD = "method";
+    field public static final int METHOD_ALARM = 4; // 0x4
     field public static final int METHOD_ALERT = 1; // 0x1
     field public static final int METHOD_DEFAULT = 0; // 0x0
     field public static final int METHOD_EMAIL = 2; // 0x2
@@ -23100,6 +23108,7 @@
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
+    method public boolean canResolveLayoutDirection();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -23185,6 +23194,7 @@
     method public boolean getKeepScreenOn();
     method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
     method public int getLayerType();
+    method public int getLayoutDirection();
     method public android.view.ViewGroup.LayoutParams getLayoutParams();
     method public final int getLeft();
     method protected float getLeftFadingEdgeStrength();
@@ -23214,6 +23224,8 @@
     method public final android.view.ViewParent getParent();
     method public float getPivotX();
     method public float getPivotY();
+    method public int getResolvedLayoutDirection();
+    method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
     method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
@@ -23257,6 +23269,7 @@
     method public boolean hasFocus();
     method public boolean hasFocusable();
     method public boolean hasOnClickListeners();
+    method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
     method protected void initializeFadingEdge(android.content.res.TypedArray);
@@ -23281,7 +23294,9 @@
     method public boolean isHovered();
     method public boolean isInEditMode();
     method public boolean isInTouchMode();
+    method protected static boolean isLayoutDirectionRtl(java.util.Locale);
     method public boolean isLayoutRequested();
+    method public boolean isLayoutRtl();
     method public boolean isLongClickable();
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
@@ -23332,10 +23347,12 @@
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
+    method public void onPaddingChanged(int);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onResetResolvedTextDirection();
-    method public void onResolvePadding(int);
-    method public void onResolveTextDirection();
+    method public void onResolvedLayoutDirectionChanged();
+    method public void onResolvedLayoutDirectionReset();
+    method public void onResolvedTextDirectionChanged();
+    method public void onResolvedTextDirectionReset();
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method protected android.os.Parcelable onSaveInstanceState();
     method protected void onScrollChanged(int, int, int, int);
@@ -23370,6 +23387,7 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method public void resetResolvedLayoutDirection();
     method public void resetResolvedTextDirection();
     method public void resolvePadding();
     method public static int resolveSize(int, int);
@@ -23404,12 +23422,14 @@
     method public void setFocusable(boolean);
     method public void setFocusableInTouchMode(boolean);
     method public void setHapticFeedbackEnabled(boolean);
+    method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
     method public void setHorizontalScrollBarEnabled(boolean);
     method public void setHovered(boolean);
     method public void setId(int);
     method public void setKeepScreenOn(boolean);
     method public void setLayerType(int, android.graphics.Paint);
+    method public void setLayoutDirection(int);
     method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
     method public final void setLeft(int);
     method public void setLongClickable(boolean);
@@ -23512,6 +23532,10 @@
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
     field public static final int LAYER_TYPE_NONE = 0; // 0x0
     field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = -2147483648; // 0x80000000
+    field public static final int LAYOUT_DIRECTION_LOCALE = -1073741824; // 0xc0000000
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1073741824; // 0x40000000
     field public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
     field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
     field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
@@ -23826,7 +23850,6 @@
     method public void requestDisallowInterceptTouchEvent(boolean);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
-    method protected void resetResolvedLayoutDirection();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -23864,6 +23887,7 @@
     ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet);
     ctor public ViewGroup.LayoutParams(int, int);
     ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void onResolveLayoutDirection(int);
     method protected void setBaseAttributes(android.content.res.TypedArray, int, int);
     field public static final deprecated int FILL_PARENT = -1; // 0xffffffff
     field public static final int MATCH_PARENT = -1; // 0xffffffff
@@ -26089,17 +26113,36 @@
     ctor public CalendarView(android.content.Context, android.util.AttributeSet);
     ctor public CalendarView(android.content.Context, android.util.AttributeSet, int);
     method public long getDate();
+    method public int getDateTextAppearance();
     method public int getFirstDayOfWeek();
+    method public int getFocusedMonthDateColor();
     method public long getMaxDate();
     method public long getMinDate();
+    method public android.graphics.drawable.Drawable getSelectedDateVerticalBar();
+    method public int getSelectedWeekBackgroundColor();
     method public boolean getShowWeekNumber();
+    method public int getShownWeekCount();
+    method public int getUnfocusedMonthDateColor();
+    method public int getWeekDayTextAppearance();
+    method public int getWeekNumberColor();
+    method public int getWeekSeparatorLineColor();
     method public void setDate(long);
     method public void setDate(long, boolean, boolean);
+    method public void setDateTextAppearance(int);
     method public void setFirstDayOfWeek(int);
+    method public void setFocusedMonthDateColor(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setOnDateChangeListener(android.widget.CalendarView.OnDateChangeListener);
+    method public void setSelectedDateVerticalBar(int);
+    method public void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
+    method public void setSelectedWeekBackgroundColor(int);
     method public void setShowWeekNumber(boolean);
+    method public void setShownWeekCount(int);
+    method public void setUnfocusedMonthDateColor(int);
+    method public void setWeekDayTextAppearance(int);
+    method public void setWeekNumberColor(int);
+    method public void setWeekSeparatorLineColor(int);
   }
 
   public static abstract interface CalendarView.OnDateChangeListener {
@@ -27658,7 +27701,6 @@
     method public boolean onTextContextMenuItem(int);
     method public void removeTextChangedListener(android.text.TextWatcher);
     method protected void resetResolvedDrawables();
-    method protected void resetResolvedLayoutDirection();
     method protected void resolveDrawables();
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3545ace..0ab6aa3 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -38,8 +38,8 @@
 #include <ui/DisplayInfo.h>
 #include <ui/FramebufferNativeWindow.h>
 
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/ISurfaceComposerClient.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
 
 #include <core/SkBitmap.h>
 #include <core/SkStream.h>
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index c85d72c..62da82f 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -23,9 +23,6 @@
 #include <androidfw/AssetManager.h>
 #include <utils/threads.h>
 
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-
 #include <EGL/egl.h>
 #include <GLES/gl.h>
 
@@ -33,7 +30,9 @@
 
 namespace android {
 
-class AssetManager;
+class Surface;
+class SurfaceComposerClient;
+class SurfaceControl;
 
 // ---------------------------------------------------------------------------
 
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index ff809d3..417e138 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -25,8 +25,6 @@
 #include <utils/Log.h>
 #include <utils/threads.h>
 
-#include <surfaceflinger/ISurfaceComposer.h>
-
 #if defined(HAVE_PTHREADS)
 # include <pthread.h>
 # include <sys/resource.h>
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 1dcba70..bd9eb9a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -69,6 +69,7 @@
         + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
         + "  <TYPE> specifies data type such as:\n"
         + "  b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+        + "  Note: Omit the value for passing an empty string, e.g column:s:\n"
         + "  Example:\n"
         + "  # Add \"new_setting\" secure setting with value \"new_value\".\n"
         + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
@@ -245,13 +246,17 @@
             if (TextUtils.isEmpty(argument)) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            String[] binding = argument.split(COLON);
-            if (binding.length != 3) {
+            final int firstColonIndex = argument.indexOf(COLON);
+            if (firstColonIndex < 0) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            String column = binding[0];
-            String type = binding[1];
-            String value = binding[2];
+            final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1);
+            if (secondColonIndex < 0) {
+                throw new IllegalArgumentException("Binding not well formed: " + argument);
+            }
+            String column = argument.substring(0, firstColonIndex);
+            String type = argument.substring(firstColonIndex + 1, secondColonIndex);
+            String value = argument.substring(secondColonIndex + 1);
             if (TYPE_STRING.equals(type)) {
                 values.put(column, value);
             } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 16dc517..13b63dc 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -86,16 +86,18 @@
     dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
     dump_file("BUDDYINFO", "/proc/buddyinfo");
 
-    if (screenshot_path[0]) {
-        ALOGI("taking screenshot\n");
-        run_command(NULL, 5, "su", "root", "screenshot", screenshot_path, NULL);
-        ALOGI("wrote screenshot: %s\n", screenshot_path);
-    }
+    print_properties();
 
-    run_command("SYSTEM SETTINGS", 20, "su", "root", "sqlite3",
-            "/data/data/com.android.providers.settings/databases/settings.db",
-            "pragma user_version; select * from system; select * from secure;", NULL);
-    run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
+    /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
+    dump_file("LAST KMSG", "/proc/last_kmsg");
+    do_dmesg();
+
+    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
+    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+
+    run_command("PROCESSES", 10, "ps", "-P", NULL);
+    run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
+    run_command("LIBRANK", 10, "librank", NULL);
 
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
@@ -114,11 +116,6 @@
         dump_file("VM TRACES AT LAST ANR", anr_traces_path);
     }
 
-    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
-    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
-    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
-
-    run_command("NETWORK INTERFACES", 10, "su", "root", "netcfg", NULL);
     dump_file("NETWORK DEV INFO", "/proc/net/dev");
     dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
     dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
@@ -126,6 +123,23 @@
 
     dump_file("NETWORK ROUTES", "/proc/net/route");
     dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
+
+    if (screenshot_path[0]) {
+        ALOGI("taking screenshot\n");
+        run_command(NULL, 5, SU_PATH, "root", "screenshot", screenshot_path, NULL);
+        ALOGI("wrote screenshot: %s\n", screenshot_path);
+    }
+
+    run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3",
+            "/data/data/com.android.providers.settings/databases/settings.db",
+            "pragma user_version; select * from system; select * from secure;", NULL);
+
+    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+    run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
+    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
+    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
+
+    run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL);
     run_command("IP RULES", 10, "ip", "rule", "show", NULL);
     run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
     run_command("ROUTE TABLE 60", 10, "ip", "route", "show", "table", "60", NULL);
@@ -133,28 +147,28 @@
     run_command("ROUTE TABLE 61", 10, "ip", "route", "show", "table", "61", NULL);
     run_command("ROUTE TABLE 61 v6", 10, "ip", "-6", "route", "show", "table", "61", NULL);
     dump_file("ARP CACHE", "/proc/net/arp");
-    run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-nvx", NULL);
-    run_command("IP6TABLES", 10, "su", "root", "ip6tables", "-L", "-nvx", NULL);
-    run_command("IPTABLE NAT", 10, "su", "root", "iptables", "-t", "nat", "-L", "-n", NULL);
-    run_command("IPT6ABLE NAT", 10, "su", "root", "ip6tables", "-t", "nat", "-L", "-n", NULL);
+    run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL);
+    run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL);
+    run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-n", NULL);
+    run_command("IPT6ABLE NAT", 10, SU_PATH, "root", "ip6tables", "-t", "nat", "-L", "-n", NULL);
 
     run_command("WIFI NETWORKS", 20,
-            "su", "root", "wpa_cli", "list_networks", NULL);
+            SU_PATH, "root", "wpa_cli", "list_networks", NULL);
 
     property_get("dhcp.wlan0.gateway", network, "");
     if (network[0])
-        run_command("PING GATEWAY", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+        run_command("PING GATEWAY", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
     property_get("dhcp.wlan0.dns1", network, "");
     if (network[0])
-        run_command("PING DNS1", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+        run_command("PING DNS1", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
     property_get("dhcp.wlan0.dns2", network, "");
     if (network[0])
-        run_command("PING DNS2", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+        run_command("PING DNS2", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
 #ifdef FWDUMP_bcm4329
     run_command("DUMP WIFI STATUS", 20,
-            "su", "root", "dhdutil", "-i", "wlan0", "dump", NULL);
+            SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
     run_command("DUMP WIFI INTERNAL COUNTERS", 20,
-            "su", "root", "wlutil", "counters", NULL);
+            SU_PATH, "root", "wlutil", "counters", NULL);
 #endif
 
     char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
@@ -168,37 +182,24 @@
                     "vril-dump", NULL);
         } else {
             run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
-                    "su", "root", "vril-dump", NULL);
+                    SU_PATH, "root", "vril-dump", NULL);
         }
     }
 
-    print_properties();
-
-    do_dmesg();
-
-    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
-    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-
     run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
     run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
 
-    run_command("PROCESSES", 10, "ps", "-P", NULL);
-    run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
-    run_command("LIBRANK", 10, "librank", NULL);
-
     dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
     dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
     dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
     dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
     dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
 
-    run_command("FILESYSTEMS & FREE SPACE", 10, "su", "root", "df", NULL);
+    run_command("FILESYSTEMS & FREE SPACE", 10, SU_PATH, "root", "df", NULL);
 
     dump_file("PACKAGE SETTINGS", "/data/system/packages.xml");
     dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
 
-    /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
-    dump_file("LAST KMSG", "/proc/last_kmsg");
     run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
     dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
     dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
@@ -218,7 +219,7 @@
     dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
     printf("\n");
 
-    run_command("LIST OF OPEN FILES", 10, "su", "root", "lsof", NULL);
+    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
 
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
 
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index b02db0b..c1c2ad8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -21,6 +21,8 @@
 #include <unistd.h>
 #include <stdio.h>
 
+#define SU_PATH "/system/xbin/su"
+
 /* prints the contents of a file */
 int dump_file(const char *title, const char* path);
 
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 21526f9..0d5ab90 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -127,7 +127,7 @@
 
     sprintf(title, "SHOW MAP %d (%s)", pid, name);
     sprintf(arg, "%d", pid);
-    run_command(title, 10, "su", "root", "showmap", arg, NULL);
+    run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
 }
 
 /* prints the contents of a file */
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 90dfe76..46e41e3 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -24,7 +24,7 @@
 #include <sys/mman.h>
 
 #include <binder/IMemory.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 
 #include <SkImageEncoder.h>
 #include <SkBitmap.h>
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index f269e80..fac3a8c 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -396,7 +396,7 @@
     for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
         CodecState *state = &mStateByTrackIndex.editValueAt(i);
 
-        CHECK_EQ(state->mCodec->stop(), (status_t)OK);
+        CHECK_EQ(state->mCodec->release(), (status_t)OK);
     }
 
     mStartTimeRealUs = -1ll;
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 1b01bd6..fbf800c 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -30,7 +30,7 @@
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NuMediaExtractor.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 
 static void usage(const char *me) {
     fprintf(stderr, "usage: %s [-a] use audio\n"
@@ -295,7 +295,7 @@
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
         CodecState *state = &stateByTrack.editValueAt(i);
 
-        CHECK_EQ((status_t)OK, state->mCodec->stop());
+        CHECK_EQ((status_t)OK, state->mCodec->release());
     }
 
     return 0;
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 6f0fb54..1d28793 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -32,8 +32,7 @@
 #include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/Utils.h>
 
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 
 #include "include/ESDS.h"
 
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 7cb8f62..dab2e0f 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -55,9 +55,7 @@
 #include <fcntl.h>
 
 #include <gui/SurfaceTextureClient.h>
-
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 
 using namespace android;
 
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 0d6c738..efa1445 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -32,8 +32,7 @@
 
 #include <binder/IServiceManager.h>
 #include <media/IMediaPlayerService.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 
 #include <fcntl.h>
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5d1d461..aa15f39 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -102,6 +102,8 @@
 import java.util.TimeZone;
 import java.util.regex.Pattern;
 
+import libcore.io.IoUtils;
+
 import dalvik.system.CloseGuard;
 
 final class SuperNotCalledException extends AndroidRuntimeException {
@@ -2357,41 +2359,47 @@
     }
 
     private void handleDumpService(DumpComponentInfo info) {
-        Service s = mServices.get(info.token);
-        if (s != null) {
-            PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
-            s.dump(info.fd.getFileDescriptor(), pw, info.args);
-            pw.flush();
-            try {
-                info.fd.close();
-            } catch (IOException e) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            Service s = mServices.get(info.token);
+            if (s != null) {
+                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+                s.dump(info.fd.getFileDescriptor(), pw, info.args);
+                pw.flush();
             }
+        } finally {
+            IoUtils.closeQuietly(info.fd);
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
     private void handleDumpActivity(DumpComponentInfo info) {
-        ActivityClientRecord r = mActivities.get(info.token);
-        if (r != null && r.activity != null) {
-            PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
-            r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
-            pw.flush();
-            try {
-                info.fd.close();
-            } catch (IOException e) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            ActivityClientRecord r = mActivities.get(info.token);
+            if (r != null && r.activity != null) {
+                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+                r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
+                pw.flush();
             }
+        } finally {
+            IoUtils.closeQuietly(info.fd);
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
     private void handleDumpProvider(DumpComponentInfo info) {
-        ProviderClientRecord r = mLocalProviders.get(info.token);
-        if (r != null && r.mLocalProvider != null) {
-            PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
-            r.mLocalProvider.dump(info.fd.getFileDescriptor(), pw, info.args);
-            pw.flush();
-            try {
-                info.fd.close();
-            } catch (IOException e) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            ProviderClientRecord r = mLocalProviders.get(info.token);
+            if (r != null && r.mLocalProvider != null) {
+                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+                r.mLocalProvider.dump(info.fd.getFileDescriptor(), pw, info.args);
+                pw.flush();
             }
+        } finally {
+            IoUtils.closeQuietly(info.fd);
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
@@ -3751,6 +3759,7 @@
     }
 
     final void handleTrimMemory(int level) {
+        WindowManagerImpl.getDefault().trimMemory(level);
         ArrayList<ComponentCallbacks2> callbacks;
 
         synchronized (mPackages) {
@@ -3761,7 +3770,7 @@
         for (int i=0; i<N; i++) {
             callbacks.get(i).onTrimMemory(level);
         }
-        WindowManagerImpl.getDefault().trimMemory(level);
+        WindowManagerImpl.getDefault().terminateEgl();
     }
 
     private void setupGraphicsSupport(LoadedApk info) {
@@ -3986,31 +3995,39 @@
             dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
         }
 
-        // If the app is being launched for full backup or restore, bring it up in
-        // a restricted environment with the base application class.
-        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
-        mInitialApplication = app;
-
-        // don't bring up providers in restricted mode; they may depend on the
-        // app's custom Application class
-        if (!data.restrictedBackupMode){ 
-            List<ProviderInfo> providers = data.providers;
-            if (providers != null) {
-                installContentProviders(app, providers);
-                // For process that contains content providers, we want to
-                // ensure that the JIT is enabled "at some point".
-                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
-            }
-        }
-
+        // Allow disk access during application and provider setup. This could
+        // block processing ordered broadcasts, but later processing would
+        // probably end up doing the same disk access.
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
         try {
-            mInstrumentation.callApplicationOnCreate(app);
-        } catch (Exception e) {
-            if (!mInstrumentation.onException(app, e)) {
-                throw new RuntimeException(
-                    "Unable to create application " + app.getClass().getName()
-                    + ": " + e.toString(), e);
+            // If the app is being launched for full backup or restore, bring it up in
+            // a restricted environment with the base application class.
+            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
+            mInitialApplication = app;
+
+            // don't bring up providers in restricted mode; they may depend on the
+            // app's custom Application class
+            if (!data.restrictedBackupMode) {
+                List<ProviderInfo> providers = data.providers;
+                if (providers != null) {
+                    installContentProviders(app, providers);
+                    // For process that contains content providers, we want to
+                    // ensure that the JIT is enabled "at some point".
+                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
+                }
             }
+
+            try {
+                mInstrumentation.callApplicationOnCreate(app);
+            } catch (Exception e) {
+                if (!mInstrumentation.onException(app, e)) {
+                    throw new RuntimeException(
+                        "Unable to create application " + app.getClass().getName()
+                        + ": " + e.toString(), e);
+                }
+            }
+        } finally {
+            StrictMode.setThreadPolicy(savedPolicy);
         }
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6d5cce5..e348b87 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -785,7 +785,7 @@
     public boolean deleteDatabase(String name) {
         try {
             File f = validateFilePath(name, false);
-            return f.delete();
+            return SQLiteDatabase.deleteDatabase(f);
         } catch (Exception e) {
         }
         return false;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f427e78..492fcc7 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -149,7 +149,7 @@
  * {@link Fragment#getFragmentManager() Fragment.getFragmentManager()}.
  *
  * <p>The Fragment class can be used many ways to achieve a wide variety of
- * results.  It is core, it represents a particular operation or interface
+ * results. In its core, it represents a particular operation or interface
  * that is running within a larger {@link Activity}.  A Fragment is closely
  * tied to the Activity it is in, and can not be used apart from one.  Though
  * Fragment defines its own lifecycle, that lifecycle is dependent on its
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 96a65da..7a612bc 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1080,7 +1080,8 @@
     }
 
     /**
-     * Notify registered observers that a row was updated.
+     * Notify registered observers that a row was updated and attempt to sync changes
+     * to the network.
      * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
      * By default, CursorAdapter objects will get this notification.
      *
@@ -1098,6 +1099,9 @@
      * Notify registered observers that a row was updated.
      * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
      * By default, CursorAdapter objects will get this notification.
+     * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
+     * adapter that's registered for the authority of the provided uri. No account will be
+     * passed to the sync adapter, so all matching accounts will be synchronized.
      *
      * @param uri The uri of the content that was changed.
      * @param observer The observer that originated the change, may be <code>null</null>.
@@ -1105,6 +1109,7 @@
      * has requested to receive self-change notifications by implementing
      * {@link ContentObserver#deliverSelfNotifications()} to return true.
      * @param syncToNetwork If true, attempt to sync the change to the network.
+     * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
      */
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
         try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a1198de..111f45e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1646,6 +1646,17 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
+     * android.os.IUpdateLock} for managing runtime sequences that
+     * must not be interrupted by headless OTA application or similar.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.os.UpdateLock
+     */
+    public static final String UPDATE_LOCK_SERVICE = "updatelock";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.NetworkManagementService} for handling management of
      * system network services
      *
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index e04b2f7..079f739 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.LocaleUtil;
+import android.view.View;
 
 import java.util.Locale;
 
@@ -280,9 +281,9 @@
     public int compatSmallestScreenWidthDp;
 
     /**
-     * @hide The text layout direction associated to the current Locale
+     * @hide The layout direction associated to the current Locale
      */
-    public int textLayoutDirection;
+    public int layoutDirection;
 
     /**
      * @hide Internal book-keeping.
@@ -310,7 +311,7 @@
         mnc = o.mnc;
         if (o.locale != null) {
             locale = (Locale) o.locale.clone();
-            textLayoutDirection = o.textLayoutDirection;
+            layoutDirection = o.layoutDirection;
         }
         userSetLocale = o.userSetLocale;
         touchscreen = o.touchscreen;
@@ -346,10 +347,10 @@
         } else {
             sb.append(" (no locale)");
         }
-        switch (textLayoutDirection) {
-            case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break;
-            case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
-            default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
+        switch (layoutDirection) {
+            case View.LAYOUT_DIRECTION_LTR: /* ltr not interesting */ break;
+            case View.LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
+            default: sb.append(" layoutDir="); sb.append(layoutDirection); break;
         }
         if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
             sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
@@ -472,7 +473,7 @@
         screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
         screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
         smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-        textLayoutDirection = LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+        layoutDirection = View.LAYOUT_DIRECTION_LTR;
         seq = 0;
     }
 
@@ -508,7 +509,7 @@
             changed |= ActivityInfo.CONFIG_LOCALE;
             locale = delta.locale != null
                     ? (Locale) delta.locale.clone() : null;
-            textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+            layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
         }
         if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
         {
@@ -776,7 +777,7 @@
         dest.writeInt(compatScreenWidthDp);
         dest.writeInt(compatScreenHeightDp);
         dest.writeInt(compatSmallestScreenWidthDp);
-        dest.writeInt(textLayoutDirection);
+        dest.writeInt(layoutDirection);
         dest.writeInt(seq);
     }
 
@@ -804,7 +805,7 @@
         compatScreenWidthDp = source.readInt();
         compatScreenHeightDp = source.readInt();
         compatSmallestScreenWidthDp = source.readInt();
-        textLayoutDirection = source.readInt();
+        layoutDirection = source.readInt();
         seq = source.readInt();
     }
     
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index b5cef81..d16f29f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -208,6 +208,11 @@
                 mConfiguration.label,
                 SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
 
+        setPageSize();
+        setSyncModeFromConfiguration();
+        setJournalModeFromConfiguration();
+        setJournalSizeLimit();
+        setAutoCheckpointInterval();
         setLocaleFromConfiguration();
     }
 
@@ -231,6 +236,44 @@
         }
     }
 
+    private void setPageSize() {
+        if (!mConfiguration.isInMemoryDb()) {
+            execute("PRAGMA page_size=" + SQLiteGlobal.getDefaultPageSize(), null, null);
+        }
+    }
+
+    private void setAutoCheckpointInterval() {
+        if (!mConfiguration.isInMemoryDb()) {
+            executeForLong("PRAGMA wal_autocheckpoint=" + SQLiteGlobal.getWALAutoCheckpoint(),
+                    null, null);
+        }
+    }
+
+    private void setJournalSizeLimit() {
+        if (!mConfiguration.isInMemoryDb()) {
+            executeForLong("PRAGMA journal_size_limit=" + SQLiteGlobal.getJournalSizeLimit(),
+                    null, null);
+        }
+    }
+
+    private void setSyncModeFromConfiguration() {
+        if (!mConfiguration.isInMemoryDb()) {
+            execute("PRAGMA synchronous=" + mConfiguration.syncMode, null, null);
+        }
+    }
+
+    private void setJournalModeFromConfiguration() {
+        if (!mConfiguration.isInMemoryDb()) {
+            String result = executeForString("PRAGMA journal_mode=" + mConfiguration.journalMode,
+                    null, null);
+            if (!result.equalsIgnoreCase(mConfiguration.journalMode)) {
+                Log.e(TAG, "setting journal_mode to " + mConfiguration.journalMode
+                        + " failed for db: " + mConfiguration.label
+                        + " (on pragma set journal_mode, sqlite returned:" + result);
+            }
+        }
+    }
+
     private void setLocaleFromConfiguration() {
         nativeSetLocale(mConnectionPtr, mConfiguration.locale.toString());
     }
@@ -246,7 +289,11 @@
             }
         }
 
-        // Remember whether locale has changed.
+        // Remember what changed.
+        boolean syncModeChanged = !configuration.syncMode.equalsIgnoreCase(
+                mConfiguration.syncMode);
+        boolean journalModeChanged = !configuration.journalMode.equalsIgnoreCase(
+                mConfiguration.journalMode);
         boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
 
         // Update configuration parameters.
@@ -255,6 +302,16 @@
         // Update prepared statement cache size.
         mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
 
+        // Update sync mode.
+        if (syncModeChanged) {
+            setSyncModeFromConfiguration();
+        }
+
+        // Update journal mode.
+        if (journalModeChanged) {
+            setJournalModeFromConfiguration();
+        }
+
         // Update locale.
         if (localeChanged) {
             setLocaleFromConfiguration();
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 236948e..3562e89 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -92,13 +92,25 @@
             new ArrayList<SQLiteConnection>();
     private SQLiteConnection mAvailablePrimaryConnection;
 
+    // Describes what should happen to an acquired connection when it is returned to the pool.
+    enum AcquiredConnectionStatus {
+        // The connection should be returned to the pool as usual.
+        NORMAL,
+
+        // The connection must be reconfigured before being returned.
+        RECONFIGURE,
+
+        // The connection must be closed and discarded.
+        DISCARD,
+    }
+
     // Weak references to all acquired connections.  The associated value
-    // is a boolean that indicates whether the connection must be reconfigured
-    // before being returned to the available connection list.
+    // indicates whether the connection must be reconfigured before being
+    // returned to the available connection list or discarded.
     // For example, the prepared statement cache size may have changed and
-    // need to be updated.
-    private final WeakHashMap<SQLiteConnection, Boolean> mAcquiredConnections =
-            new WeakHashMap<SQLiteConnection, Boolean>();
+    // need to be updated in preparation for the next client.
+    private final WeakHashMap<SQLiteConnection, AcquiredConnectionStatus> mAcquiredConnections =
+            new WeakHashMap<SQLiteConnection, AcquiredConnectionStatus>();
 
     /**
      * Connection flag: Read-only.
@@ -168,7 +180,7 @@
     private void open() {
         // Open the primary connection.
         // This might throw if the database is corrupt.
-        mAvailablePrimaryConnection = openConnectionLocked(
+        mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
                 true /*primaryConnection*/); // might throw
 
         // Mark the pool as being open for business.
@@ -209,16 +221,7 @@
 
                 mIsOpen = false;
 
-                final int count = mAvailableNonPrimaryConnections.size();
-                for (int i = 0; i < count; i++) {
-                    closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
-                }
-                mAvailableNonPrimaryConnections.clear();
-
-                if (mAvailablePrimaryConnection != null) {
-                    closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
-                    mAvailablePrimaryConnection = null;
-                }
+                closeAvailableConnectionsAndLogExceptionsLocked();
 
                 final int pendingCount = mAcquiredConnections.size();
                 if (pendingCount != 0) {
@@ -254,21 +257,27 @@
         synchronized (mLock) {
             throwIfClosedLocked();
 
-            final boolean poolSizeChanged = mConfiguration.maxConnectionPoolSize
-                    != configuration.maxConnectionPoolSize;
-            mConfiguration.updateParametersFrom(configuration);
+            if (mConfiguration.openFlags != configuration.openFlags) {
+                // Try to reopen the primary connection using the new open flags then
+                // close and discard all existing connections.
+                // This might throw if the database is corrupt or cannot be opened in
+                // the new mode in which case existing connections will remain untouched.
+                SQLiteConnection newPrimaryConnection = openConnectionLocked(configuration,
+                        true /*primaryConnection*/); // might throw
 
-            if (poolSizeChanged) {
-                int availableCount = mAvailableNonPrimaryConnections.size();
-                while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
-                    SQLiteConnection connection =
-                            mAvailableNonPrimaryConnections.remove(availableCount);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                }
+                closeAvailableConnectionsAndLogExceptionsLocked();
+                discardAcquiredConnectionsLocked();
+
+                mAvailablePrimaryConnection = newPrimaryConnection;
+                mConfiguration.updateParametersFrom(configuration);
+            } else {
+                // Reconfigure the database connections in place.
+                mConfiguration.updateParametersFrom(configuration);
+
+                closeExcessConnectionsAndLogExceptionsLocked();
+                reconfigureAllConnectionsLocked();
             }
 
-            reconfigureAllConnectionsLocked();
-
             wakeConnectionWaitersLocked();
         }
     }
@@ -310,8 +319,8 @@
      */
     public void releaseConnection(SQLiteConnection connection) {
         synchronized (mLock) {
-            Boolean mustReconfigure = mAcquiredConnections.remove(connection);
-            if (mustReconfigure == null) {
+            AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
+            if (status == null) {
                 throw new IllegalStateException("Cannot perform this operation "
                         + "because the specified connection was not acquired "
                         + "from this pool or has already been released.");
@@ -320,18 +329,8 @@
             if (!mIsOpen) {
                 closeConnectionAndLogExceptionsLocked(connection);
             } else if (connection.isPrimaryConnection()) {
-                assert mAvailablePrimaryConnection == null;
-                try {
-                    if (mustReconfigure == Boolean.TRUE) {
-                        connection.reconfigure(mConfiguration); // might throw
-                    }
-                } catch (RuntimeException ex) {
-                    Log.e(TAG, "Failed to reconfigure released primary connection, closing it: "
-                            + connection, ex);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                    connection = null;
-                }
-                if (connection != null) {
+                if (recycleConnectionLocked(connection, status)) {
+                    assert mAvailablePrimaryConnection == null;
                     mAvailablePrimaryConnection = connection;
                 }
                 wakeConnectionWaitersLocked();
@@ -339,17 +338,7 @@
                     mConfiguration.maxConnectionPoolSize - 1) {
                 closeConnectionAndLogExceptionsLocked(connection);
             } else {
-                try {
-                    if (mustReconfigure == Boolean.TRUE) {
-                        connection.reconfigure(mConfiguration); // might throw
-                    }
-                } catch (RuntimeException ex) {
-                    Log.e(TAG, "Failed to reconfigure released non-primary connection, "
-                            + "closing it: " + connection, ex);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                    connection = null;
-                }
-                if (connection != null) {
+                if (recycleConnectionLocked(connection, status)) {
                     mAvailableNonPrimaryConnections.add(connection);
                 }
                 wakeConnectionWaitersLocked();
@@ -357,6 +346,25 @@
         }
     }
 
+    // Can't throw.
+    private boolean recycleConnectionLocked(SQLiteConnection connection,
+            AcquiredConnectionStatus status) {
+        if (status == AcquiredConnectionStatus.RECONFIGURE) {
+            try {
+                connection.reconfigure(mConfiguration); // might throw
+            } catch (RuntimeException ex) {
+                Log.e(TAG, "Failed to reconfigure released connection, closing it: "
+                        + connection, ex);
+                status = AcquiredConnectionStatus.DISCARD;
+            }
+        }
+        if (status == AcquiredConnectionStatus.DISCARD) {
+            closeConnectionAndLogExceptionsLocked(connection);
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Returns true if the session should yield the connection due to
      * contention over available database connections.
@@ -407,9 +415,10 @@
     }
 
     // Might throw.
-    private SQLiteConnection openConnectionLocked(boolean primaryConnection) {
+    private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
+            boolean primaryConnection) {
         final int connectionId = mNextConnectionId++;
-        return SQLiteConnection.open(this, mConfiguration,
+        return SQLiteConnection.open(this, configuration,
                 connectionId, primaryConnection); // might throw
     }
 
@@ -443,6 +452,30 @@
     }
 
     // Can't throw.
+    private void closeAvailableConnectionsAndLogExceptionsLocked() {
+        final int count = mAvailableNonPrimaryConnections.size();
+        for (int i = 0; i < count; i++) {
+            closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
+        }
+        mAvailableNonPrimaryConnections.clear();
+
+        if (mAvailablePrimaryConnection != null) {
+            closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
+            mAvailablePrimaryConnection = null;
+        }
+    }
+
+    // Can't throw.
+    private void closeExcessConnectionsAndLogExceptionsLocked() {
+        int availableCount = mAvailableNonPrimaryConnections.size();
+        while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
+            SQLiteConnection connection =
+                    mAvailableNonPrimaryConnections.remove(availableCount);
+            closeConnectionAndLogExceptionsLocked(connection);
+        }
+    }
+
+    // Can't throw.
     private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
         try {
             connection.close(); // might throw
@@ -453,8 +486,12 @@
     }
 
     // Can't throw.
+    private void discardAcquiredConnectionsLocked() {
+        markAcquiredConnectionsLocked(AcquiredConnectionStatus.DISCARD);
+    }
+
+    // Can't throw.
     private void reconfigureAllConnectionsLocked() {
-        boolean wake = false;
         if (mAvailablePrimaryConnection != null) {
             try {
                 mAvailablePrimaryConnection.reconfigure(mConfiguration); // might throw
@@ -463,7 +500,6 @@
                         + mAvailablePrimaryConnection, ex);
                 closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
                 mAvailablePrimaryConnection = null;
-                wake = true;
             }
         }
 
@@ -478,27 +514,30 @@
                 closeConnectionAndLogExceptionsLocked(connection);
                 mAvailableNonPrimaryConnections.remove(i--);
                 count -= 1;
-                wake = true;
             }
         }
 
+        markAcquiredConnectionsLocked(AcquiredConnectionStatus.RECONFIGURE);
+    }
+
+    // Can't throw.
+    private void markAcquiredConnectionsLocked(AcquiredConnectionStatus status) {
         if (!mAcquiredConnections.isEmpty()) {
             ArrayList<SQLiteConnection> keysToUpdate = new ArrayList<SQLiteConnection>(
                     mAcquiredConnections.size());
-            for (Map.Entry<SQLiteConnection, Boolean> entry : mAcquiredConnections.entrySet()) {
-                if (entry.getValue() != Boolean.TRUE) {
+            for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry
+                    : mAcquiredConnections.entrySet()) {
+                AcquiredConnectionStatus oldStatus = entry.getValue();
+                if (status != oldStatus
+                        && oldStatus != AcquiredConnectionStatus.DISCARD) {
                     keysToUpdate.add(entry.getKey());
                 }
             }
             final int updateCount = keysToUpdate.size();
             for (int i = 0; i < updateCount; i++) {
-                mAcquiredConnections.put(keysToUpdate.get(i), Boolean.TRUE);
+                mAcquiredConnections.put(keysToUpdate.get(i), status);
             }
         }
-
-        if (wake) {
-            wakeConnectionWaitersLocked();
-        }
     }
 
     // Might throw.
@@ -658,8 +697,7 @@
         int activeConnections = 0;
         int idleConnections = 0;
         if (!mAcquiredConnections.isEmpty()) {
-            for (Map.Entry<SQLiteConnection, Boolean> entry : mAcquiredConnections.entrySet()) {
-                final SQLiteConnection connection = entry.getKey();
+            for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
                 String description = connection.describeCurrentOperationUnsafe();
                 if (description != null) {
                     requests.add(description);
@@ -769,7 +807,8 @@
 
         // Uhoh.  No primary connection!  Either this is the first time we asked
         // for it, or maybe it leaked?
-        connection = openConnectionLocked(true /*primaryConnection*/); // might throw
+        connection = openConnectionLocked(mConfiguration,
+                true /*primaryConnection*/); // might throw
         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
         return connection;
     }
@@ -807,7 +846,8 @@
         if (openConnections >= mConfiguration.maxConnectionPoolSize) {
             return null;
         }
-        connection = openConnectionLocked(false /*primaryConnection*/); // might throw
+        connection = openConnectionLocked(mConfiguration,
+                false /*primaryConnection*/); // might throw
         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
         return connection;
     }
@@ -818,7 +858,7 @@
             final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
             connection.setOnlyAllowReadOnlyOperations(readOnly);
 
-            mAcquiredConnections.put(connection, Boolean.FALSE);
+            mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
         } catch (RuntimeException ex) {
             Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
                     + connection +", connectionFlags=" + connectionFlags);
@@ -858,7 +898,7 @@
     private void throwIfClosedLocked() {
         if (!mIsOpen) {
             throw new IllegalStateException("Cannot perform this operation "
-                    + "because the connection pool have been closed.");
+                    + "because the connection pool has been closed.");
         }
     }
 
@@ -922,11 +962,11 @@
 
             printer.println("  Acquired connections:");
             if (!mAcquiredConnections.isEmpty()) {
-                for (Map.Entry<SQLiteConnection, Boolean> entry :
+                for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry :
                         mAcquiredConnections.entrySet()) {
                     final SQLiteConnection connection = entry.getKey();
                     connection.dumpUnsafe(indentedPrinter, verbose);
-                    indentedPrinter.println("  Pending reconfiguration: " + entry.getValue());
+                    indentedPrinter.println("  Status: " + entry.getValue());
                 }
             } else {
                 indentedPrinter.println("<none>");
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 505f83e..04ee142 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -36,6 +36,7 @@
 import dalvik.system.CloseGuard;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -677,6 +678,72 @@
         return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
     }
 
+    /**
+     * Deletes a database including its journal file and other auxiliary files
+     * that may have been created by the database engine.
+     *
+     * @param file The database file path.
+     * @return True if the database was successfully deleted.
+     */
+    public static boolean deleteDatabase(File file) {
+        if (file == null) {
+            throw new IllegalArgumentException("file must not be null");
+        }
+
+        boolean deleted = false;
+        deleted |= file.delete();
+        deleted |= new File(file.getPath() + "-journal").delete();
+        deleted |= new File(file.getPath() + "-shm").delete();
+        deleted |= new File(file.getPath() + "-wal").delete();
+
+        File dir = file.getParentFile();
+        if (dir != null) {
+            final String prefix = file.getName() + "-mj";
+            final FileFilter filter = new FileFilter() {
+                @Override
+                public boolean accept(File candidate) {
+                    return candidate.getName().startsWith(prefix);
+                }
+            };
+            for (File masterJournal : dir.listFiles(filter)) {
+                deleted |= masterJournal.delete();
+            }
+        }
+        return deleted;
+    }
+
+    /**
+     * Reopens the database in read-write mode.
+     * If the database is already read-write, does nothing.
+     *
+     * @throws SQLiteException if the database could not be reopened as requested, in which
+     * case it remains open in read only mode.
+     * @throws IllegalStateException if the database is not open.
+     *
+     * @see #isReadOnly()
+     * @hide
+     */
+    public void reopenReadWrite() {
+        synchronized (mLock) {
+            throwIfNotOpenLocked();
+
+            if (!isReadOnlyLocked()) {
+                return; // nothing to do
+            }
+
+            // Reopen the database in read-write mode.
+            final int oldOpenFlags = mConfigurationLocked.openFlags;
+            mConfigurationLocked.openFlags = (mConfigurationLocked.openFlags & ~OPEN_READ_MASK)
+                    | OPEN_READWRITE;
+            try {
+                mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+            } catch (RuntimeException ex) {
+                mConfigurationLocked.openFlags = oldOpenFlags;
+                throw ex;
+            }
+        }
+    }
+
     private void open() {
         try {
             try {
@@ -685,9 +752,6 @@
                 onCorruption();
                 openInner();
             }
-
-            // Disable WAL if it was previously enabled.
-            setJournalMode("TRUNCATE");
         } catch (SQLiteException ex) {
             Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
             close();
@@ -707,19 +771,6 @@
         }
     }
 
-    private void setJournalMode(String mode) {
-        // Journal mode can be set only for non-memory databases
-        // AND can't be set for readonly databases
-        if (isInMemoryDatabase() || isReadOnly()) {
-            return;
-        }
-        String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
-        if (!s.equalsIgnoreCase(mode)) {
-            Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + getLabel()
-                    + " (on pragma set journal_mode, sqlite returned:" + s);
-        }
-    }
-
     /**
      * Create a memory backed SQLite database.  Its contents will be destroyed
      * when the database is closed.
@@ -1729,13 +1780,11 @@
             }
 
             mIsWALEnabledLocked = true;
-            mConfigurationLocked.maxConnectionPoolSize = Math.max(2,
-                    Resources.getSystem().getInteger(
-                            com.android.internal.R.integer.db_connection_pool_size));
+            mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
+            mConfigurationLocked.syncMode = SQLiteGlobal.getWALSyncMode();
+            mConfigurationLocked.journalMode = "WAL";
             mConnectionPoolLocked.reconfigure(mConfigurationLocked);
         }
-
-        setJournalMode("WAL");
         return true;
     }
 
@@ -1753,10 +1802,10 @@
 
             mIsWALEnabledLocked = false;
             mConfigurationLocked.maxConnectionPoolSize = 1;
+            mConfigurationLocked.syncMode = SQLiteGlobal.getDefaultSyncMode();
+            mConfigurationLocked.journalMode = SQLiteGlobal.getDefaultJournalMode();
             mConnectionPoolLocked.reconfigure(mConfigurationLocked);
         }
-
-        setJournalMode("TRUNCATE");
     }
 
     /**
@@ -1902,28 +1951,6 @@
         return true;
     }
 
-    /**
-     * Prevent other threads from using the database's primary connection.
-     *
-     * This method is only used by {@link SQLiteOpenHelper} when transitioning from
-     * a readable to a writable database.  It should not be used in any other way.
-     *
-     * @see #unlockPrimaryConnection()
-     */
-    void lockPrimaryConnection() {
-        getThreadSession().beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED,
-                null, SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY, null);
-    }
-
-    /**
-     * Allow other threads to use the database's primary connection.
-     *
-     * @see #lockPrimaryConnection()
-     */
-    void unlockPrimaryConnection() {
-        getThreadSession().endTransaction(null);
-    }
-
     @Override
     public String toString() {
         return "SQLiteDatabase: " + getPath();
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index bc79ad3..efbcaca 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -51,17 +51,17 @@
     public final String path;
 
     /**
-     * The flags used to open the database.
-     */
-    public final int openFlags;
-
-    /**
      * The label to use to describe the database when it appears in logs.
      * This is derived from the path but is stripped to remove PII.
      */
     public final String label;
 
     /**
+     * The flags used to open the database.
+     */
+    public int openFlags;
+
+    /**
      * The maximum number of connections to retain in the connection pool.
      * Must be at least 1.
      *
@@ -85,6 +85,20 @@
     public Locale locale;
 
     /**
+     * The database synchronization mode.
+     *
+     * Default is {@link SQLiteGlobal#getDefaultSyncMode()}.
+     */
+    public String syncMode;
+
+    /**
+     * The database journal mode.
+     *
+     * Default is {@link SQLiteGlobal#getDefaultJournalMode()}.
+     */
+    public String journalMode;
+
+    /**
      * The custom functions to register.
      */
     public final ArrayList<SQLiteCustomFunction> customFunctions =
@@ -103,13 +117,15 @@
         }
 
         this.path = path;
-        this.openFlags = openFlags;
         label = stripPathForLogs(path);
+        this.openFlags = openFlags;
 
         // Set default values for optional parameters.
         maxConnectionPoolSize = 1;
         maxSqlCacheSize = 25;
         locale = Locale.getDefault();
+        syncMode = SQLiteGlobal.getDefaultSyncMode();
+        journalMode = SQLiteGlobal.getDefaultJournalMode();
     }
 
     /**
@@ -123,7 +139,6 @@
         }
 
         this.path = other.path;
-        this.openFlags = other.openFlags;
         this.label = other.label;
         updateParametersFrom(other);
     }
@@ -138,14 +153,17 @@
         if (other == null) {
             throw new IllegalArgumentException("other must not be null.");
         }
-        if (!path.equals(other.path) || openFlags != other.openFlags) {
+        if (!path.equals(other.path)) {
             throw new IllegalArgumentException("other configuration must refer to "
                     + "the same database.");
         }
 
+        openFlags = other.openFlags;
         maxConnectionPoolSize = other.maxConnectionPoolSize;
         maxSqlCacheSize = other.maxSqlCacheSize;
         locale = other.locale;
+        syncMode = other.syncMode;
+        journalMode = other.journalMode;
         customFunctions.clear();
         customFunctions.addAll(other.customFunctions);
     }
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 204483d..10ce991 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -33,12 +33,16 @@
 
     /**
      * Controls the printing of informational SQL log messages.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
      */
     public static final boolean DEBUG_SQL_LOG =
             Log.isLoggable("SQLiteLog", Log.VERBOSE);
 
     /**
      * Controls the printing of SQL statements as they are executed.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
      */
     public static final boolean DEBUG_SQL_STATEMENTS =
             Log.isLoggable("SQLiteStatements", Log.VERBOSE);
@@ -46,6 +50,8 @@
     /**
      * Controls the printing of wall-clock time taken to execute SQL statements
      * as they are executed.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
      */
     public static final boolean DEBUG_SQL_TIME =
             Log.isLoggable("SQLiteTime", Log.VERBOSE);
@@ -61,15 +67,17 @@
      *
      * Reads the "db.log.slow_query_threshold" system property, which can be changed
      * by the user at any time.  If the value is zero, then all queries will
-     * be considered slow.  If the value does not exist, then no queries will
+     * be considered slow.  If the value does not exist or is negative, then no queries will
      * be considered slow.
      *
      * This value can be changed dynamically while the system is running.
+     * For example, "adb shell setprop db.log.slow_query_threshold 200" will
+     * log all queries that take 200ms or longer to run.
      * @hide
      */
     public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) {
         int slowQueryMillis = SystemProperties.getInt("db.log.slow_query_threshold", -1);
-        return slowQueryMillis >= 0 && elapsedTimeMillis > slowQueryMillis;
+        return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index dbefd63..5d8f80e 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.content.res.Resources;
 import android.os.StatFs;
 
 /**
@@ -64,4 +65,52 @@
             return sDefaultPageSize;
         }
     }
+
+    /**
+     * Gets the default journal mode when WAL is not in use.
+     */
+    public static String getDefaultJournalMode() {
+        return Resources.getSystem().getString(
+                com.android.internal.R.string.db_default_journal_mode);
+    }
+
+    /**
+     * Gets the journal size limit in bytes.
+     */
+    public static int getJournalSizeLimit() {
+        return Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_journal_size_limit);
+    }
+
+    /**
+     * Gets the default database synchronization mode when WAL is not in use.
+     */
+    public static String getDefaultSyncMode() {
+        return Resources.getSystem().getString(
+                com.android.internal.R.string.db_default_sync_mode);
+    }
+
+    /**
+     * Gets the database synchronization mode when in WAL mode.
+     */
+    public static String getWALSyncMode() {
+        return Resources.getSystem().getString(
+                com.android.internal.R.string.db_wal_sync_mode);
+    }
+
+    /**
+     * Gets the WAL auto-checkpoint integer in database pages.
+     */
+    public static int getWALAutoCheckpoint() {
+        return Math.max(1, Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_wal_autocheckpoint));
+    }
+
+    /**
+     * Gets the connection pool size when in WAL mode.
+     */
+    public static int getWALConnectionPoolSize() {
+        return Math.max(2, Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_connection_pool_size));
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 46d9369..ffa4663 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -43,13 +43,21 @@
 public abstract class SQLiteOpenHelper {
     private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
 
+    // When true, getReadableDatabase returns a read-only database if it is just being opened.
+    // The database handle is reopened in read/write mode when getWritableDatabase is called.
+    // We leave this behavior disabled in production because it is inefficient and breaks
+    // many applications.  For debugging purposes it can be useful to turn on strict
+    // read-only semantics to catch applications that call getReadableDatabase when they really
+    // wanted getWritableDatabase.
+    private static final boolean DEBUG_STRICT_READONLY = false;
+
     private final Context mContext;
     private final String mName;
     private final CursorFactory mFactory;
     private final int mNewVersion;
 
-    private SQLiteDatabase mDatabase = null;
-    private boolean mIsInitializing = false;
+    private SQLiteDatabase mDatabase;
+    private boolean mIsInitializing;
     private final DatabaseErrorHandler mErrorHandler;
 
     /**
@@ -127,76 +135,9 @@
      * @throws SQLiteException if the database cannot be opened for writing
      * @return a read/write database object valid until {@link #close} is called
      */
-    public synchronized SQLiteDatabase getWritableDatabase() {
-        if (mDatabase != null) {
-            if (!mDatabase.isOpen()) {
-                // darn! the user closed the database by calling mDatabase.close()
-                mDatabase = null;
-            } else if (!mDatabase.isReadOnly()) {
-                return mDatabase;  // The database is already open for business
-            }
-        }
-
-        if (mIsInitializing) {
-            throw new IllegalStateException("getWritableDatabase called recursively");
-        }
-
-        // If we have a read-only database open, someone could be using it
-        // (though they shouldn't), which would cause a lock to be held on
-        // the file, and our attempts to open the database read-write would
-        // fail waiting for the file lock.  To prevent that, we acquire a lock
-        // on the read-only database, which shuts out other users.
-
-        boolean success = false;
-        SQLiteDatabase db = null;
-        if (mDatabase != null) {
-            mDatabase.lockPrimaryConnection();
-        }
-        try {
-            mIsInitializing = true;
-            if (mName == null) {
-                db = SQLiteDatabase.create(null);
-            } else {
-                db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
-            }
-
-            int version = db.getVersion();
-            if (version != mNewVersion) {
-                db.beginTransaction();
-                try {
-                    if (version == 0) {
-                        onCreate(db);
-                    } else {
-                        if (version > mNewVersion) {
-                            onDowngrade(db, version, mNewVersion);
-                        } else {
-                            onUpgrade(db, version, mNewVersion);
-                        }
-                    }
-                    db.setVersion(mNewVersion);
-                    db.setTransactionSuccessful();
-                } finally {
-                    db.endTransaction();
-                }
-            }
-
-            onOpen(db);
-            success = true;
-            return db;
-        } finally {
-            mIsInitializing = false;
-            if (success) {
-                if (mDatabase != null) {
-                    try { mDatabase.close(); } catch (Exception e) { }
-                    mDatabase.unlockPrimaryConnection();
-                }
-                mDatabase = db;
-            } else {
-                if (mDatabase != null) {
-                    mDatabase.unlockPrimaryConnection();
-                }
-                if (db != null) db.close();
-            }
+    public SQLiteDatabase getWritableDatabase() {
+        synchronized (this) {
+            return getDatabaseLocked(true);
         }
     }
 
@@ -218,45 +159,95 @@
      * @return a database object valid until {@link #getWritableDatabase}
      *     or {@link #close} is called.
      */
-    public synchronized SQLiteDatabase getReadableDatabase() {
+    public SQLiteDatabase getReadableDatabase() {
+        synchronized (this) {
+            return getDatabaseLocked(false);
+        }
+    }
+
+    private SQLiteDatabase getDatabaseLocked(boolean writable) {
         if (mDatabase != null) {
             if (!mDatabase.isOpen()) {
-                // darn! the user closed the database by calling mDatabase.close()
+                // Darn!  The user closed the database by calling mDatabase.close().
                 mDatabase = null;
-            } else {
-                return mDatabase;  // The database is already open for business
+            } else if (!writable || !mDatabase.isReadOnly()) {
+                // The database is already open for business.
+                return mDatabase;
             }
         }
 
         if (mIsInitializing) {
-            throw new IllegalStateException("getReadableDatabase called recursively");
+            throw new IllegalStateException("getDatabase called recursively");
         }
 
-        try {
-            return getWritableDatabase();
-        } catch (SQLiteException e) {
-            if (mName == null) throw e;  // Can't open a temp database read-only!
-            Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
-        }
-
-        SQLiteDatabase db = null;
+        SQLiteDatabase db = mDatabase;
         try {
             mIsInitializing = true;
-            String path = mContext.getDatabasePath(mName).getPath();
-            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,
-                    mErrorHandler);
-            if (db.getVersion() != mNewVersion) {
-                throw new SQLiteException("Can't upgrade read-only database from version " +
-                        db.getVersion() + " to " + mNewVersion + ": " + path);
+
+            if (db != null) {
+                if (writable && db.isReadOnly()) {
+                    db.reopenReadWrite();
+                }
+            } else if (mName == null) {
+                db = SQLiteDatabase.create(null);
+            } else {
+                try {
+                    if (DEBUG_STRICT_READONLY && !writable) {
+                        final String path = mContext.getDatabasePath(mName).getPath();
+                        db = SQLiteDatabase.openDatabase(path, mFactory,
+                                SQLiteDatabase.OPEN_READONLY, mErrorHandler);
+                    } else {
+                        db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
+                    }
+                } catch (SQLiteException ex) {
+                    if (writable) {
+                        throw ex;
+                    }
+                    Log.e(TAG, "Couldn't open " + mName
+                            + " for writing (will try read-only):", ex);
+                    final String path = mContext.getDatabasePath(mName).getPath();
+                    db = SQLiteDatabase.openDatabase(path, mFactory,
+                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);
+                }
             }
 
+            final int version = db.getVersion();
+            if (version != mNewVersion) {
+                if (db.isReadOnly()) {
+                    throw new SQLiteException("Can't upgrade read-only database from version " +
+                            db.getVersion() + " to " + mNewVersion + ": " + mName);
+                }
+
+                db.beginTransaction();
+                try {
+                    if (version == 0) {
+                        onCreate(db);
+                    } else {
+                        if (version > mNewVersion) {
+                            onDowngrade(db, version, mNewVersion);
+                        } else {
+                            onUpgrade(db, version, mNewVersion);
+                        }
+                    }
+                    db.setVersion(mNewVersion);
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
             onOpen(db);
-            Log.w(TAG, "Opened " + mName + " in read-only mode");
+
+            if (db.isReadOnly()) {
+                Log.w(TAG, "Opened " + mName + " in read-only mode");
+            }
+
             mDatabase = db;
-            return mDatabase;
+            return db;
         } finally {
             mIsInitializing = false;
-            if (db != null && db != mDatabase) db.close();
+            if (db != null && db != mDatabase) {
+                db.close();
+            }
         }
     }
 
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 2f43cb8..0bc6b58 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -77,7 +77,9 @@
         /** Attempt to connect failed. */
         FAILED,
         /** Access to this network is blocked. */
-        BLOCKED
+        BLOCKED,
+        /** Link has poor connectivity. */
+        VERIFYING_POOR_LINK
     }
 
     /**
@@ -94,6 +96,7 @@
         stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
         stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
         stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
+        stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
         stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
         stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
         stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
new file mode 100644
index 0000000..8e666bc
--- /dev/null
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.arp;
+
+import android.os.SystemClock;
+import android.util.Log;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+import libcore.net.RawSocket;
+
+/**
+ * This class allows simple ARP exchanges over an uninitialized network
+ * interface.
+ *
+ * @hide
+ */
+public class ArpPeer {
+    private String mInterfaceName;
+    private final InetAddress mMyAddr;
+    private final byte[] mMyMac = new byte[6];
+    private final InetAddress mPeer;
+    private final RawSocket mSocket;
+    private final byte[] L2_BROADCAST;  // TODO: refactor from DhcpClient.java
+    private static final int MAX_LENGTH = 1500; // refactor from DhcpPacket.java
+    private static final int ETHERNET_TYPE = 1;
+    private static final int ARP_LENGTH = 28;
+    private static final int MAC_ADDR_LENGTH = 6;
+    private static final int IPV4_LENGTH = 4;
+    private static final String TAG = "ArpPeer";
+
+    public ArpPeer(String interfaceName, InetAddress myAddr, String mac,
+                   InetAddress peer) throws SocketException {
+        mInterfaceName = interfaceName;
+        mMyAddr = myAddr;
+
+        for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+            mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+                        i*3, (i*3) + 2), 16);
+        }
+
+        if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
+            throw new IllegalArgumentException("IPv6 unsupported");
+        }
+
+        mPeer = peer;
+        L2_BROADCAST = new byte[MAC_ADDR_LENGTH];
+        Arrays.fill(L2_BROADCAST, (byte) 0xFF);
+
+        mSocket = new RawSocket(mInterfaceName, RawSocket.ETH_P_ARP);
+    }
+
+    /**
+     * Returns the MAC address (or null if timeout) for the requested
+     * peer.
+     */
+    public byte[] doArp(int timeoutMillis) {
+        ByteBuffer buf = ByteBuffer.allocate(MAX_LENGTH);
+        byte[] desiredIp = mPeer.getAddress();
+        long timeout = SystemClock.elapsedRealtime() + timeoutMillis;
+
+        // construct ARP request packet, using a ByteBuffer as a
+        // convenient container
+        buf.clear();
+        buf.order(ByteOrder.BIG_ENDIAN);
+
+        buf.putShort((short) ETHERNET_TYPE); // Ethernet type, 16 bits
+        buf.putShort(RawSocket.ETH_P_IP); // Protocol type IP, 16 bits
+        buf.put((byte)MAC_ADDR_LENGTH);  // MAC address length, 6 bytes
+        buf.put((byte)IPV4_LENGTH);  // IPv4 protocol size
+        buf.putShort((short) 1); // ARP opcode 1: 'request'
+        buf.put(mMyMac);        // six bytes: sender MAC
+        buf.put(mMyAddr.getAddress());  // four bytes: sender IP address
+        buf.put(new byte[MAC_ADDR_LENGTH]); // target MAC address: unknown
+        buf.put(desiredIp); // target IP address, 4 bytes
+        buf.flip();
+        mSocket.write(L2_BROADCAST, buf.array(), 0, buf.limit());
+
+        byte[] recvBuf = new byte[MAX_LENGTH];
+
+        while (SystemClock.elapsedRealtime() < timeout) {
+            long duration = (long) timeout - SystemClock.elapsedRealtime();
+            int readLen = mSocket.read(recvBuf, 0, recvBuf.length, -1,
+                (int) duration);
+
+            // Verify packet details. see RFC 826
+            if ((readLen >= ARP_LENGTH) // trailing bytes at times
+                && (recvBuf[0] == 0) && (recvBuf[1] == ETHERNET_TYPE) // type Ethernet
+                && (recvBuf[2] == 8) && (recvBuf[3] == 0) // protocol IP
+                && (recvBuf[4] == MAC_ADDR_LENGTH) // mac length
+                && (recvBuf[5] == IPV4_LENGTH) // IPv4 protocol size
+                && (recvBuf[6] == 0) && (recvBuf[7] == 2) // ARP reply
+                // verify desired IP address
+                && (recvBuf[14] == desiredIp[0]) && (recvBuf[15] == desiredIp[1])
+                && (recvBuf[16] == desiredIp[2]) && (recvBuf[17] == desiredIp[3]))
+            {
+                // looks good.  copy out the MAC
+                byte[] result = new byte[MAC_ADDR_LENGTH];
+                System.arraycopy(recvBuf, 8, result, 0, MAC_ADDR_LENGTH);
+                return result;
+            }
+        }
+
+        return null;
+    }
+
+    public void close() {
+        try {
+            mSocket.close();
+        } catch (IOException ex) {
+        }
+    }
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 61bc324..10da9ef 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -45,4 +45,6 @@
     void setForegroundNdefPush(in NdefMessage msg, in INdefPushCallback callback);
 
     void dispatch(in Tag tag);
+
+    void setP2pModes(int initatorModes, int targetModes);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 5176857..23f96e3 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -903,6 +903,17 @@
     /**
      * @hide
      */
+    public void setP2pModes(int initiatorModes, int targetModes) {
+        try {
+            sService.setP2pModes(initiatorModes, targetModes);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * @hide
+     */
     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
         if (mContext == null) {
             throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index fd6bed7..97ed235 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -281,9 +281,6 @@
                             e.getCause());
                 } catch (CancellationException e) {
                     postResultIfNotInvoked(null);
-                } catch (Throwable t) {
-                    throw new RuntimeException("An error occured while executing "
-                            + "doInBackground()", t);
                 }
             }
         };
diff --git a/services/audioflinger/AudioBufferProvider.cpp b/core/java/android/os/IUpdateLock.aidl
similarity index 65%
rename from services/audioflinger/AudioBufferProvider.cpp
rename to core/java/android/os/IUpdateLock.aidl
index 678fd58..4492fb8 100644
--- a/services/audioflinger/AudioBufferProvider.cpp
+++ b/core/java/android/os/IUpdateLock.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#undef __STRICT_ANSI__
-#define __STDINT_LIMITS
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
+package android.os;
 
-#include "AudioBufferProvider.h"
-
-namespace android {
-
-const int64_t AudioBufferProvider::kInvalidPTS = INT64_MAX;
-
-}; // namespace android
+/**
+ * Direct interface to the UpdateLockService's functionality
+ *
+ * {@hide}
+ */
+interface IUpdateLock {
+    void acquireUpdateLock(IBinder token, String tag);
+    void releaseUpdateLock(IBinder token);
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index ac15d9c..3e90dfc 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -15,6 +15,7 @@
  */
 
 package android.os;
+import java.io.Closeable;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -28,7 +29,7 @@
  * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
  * you to close it when done with it.
  */
-public class ParcelFileDescriptor implements Parcelable {
+public class ParcelFileDescriptor implements Parcelable, Closeable {
     private final FileDescriptor mFileDescriptor;
     private boolean mClosed;
     //this field is to create wrapper for ParcelFileDescriptor using another
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index ac3cc92..9b3a2d6 100755
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.WeakHashMap;
 import java.util.Set;
 import android.util.Log;
@@ -115,15 +117,31 @@
 
     public void dump()
     {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            Log.i(mTag, s);
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            pw.println(s);
+        }
+    }
+
+    private ArrayList<String> dumpInternal() {
+        ArrayList<String> a = new ArrayList<String>();
         synchronized (mTokens) {
             Set<IBinder> keys = mTokens.keySet();
-            Log.i(mTag, "Token count: " + mTokens.size());
+            a.add("Token count: " + mTokens.size());
             int i = 0;
             for (IBinder b: keys) {
-                Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b);
+                a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b);
                 i++;
             }
         }
+        return a;
     }
 
     private Runnable mNotificationTask = new Runnable() {
diff --git a/core/java/android/os/UpdateLock.java b/core/java/android/os/UpdateLock.java
new file mode 100644
index 0000000..4060326
--- /dev/null
+++ b/core/java/android/os/UpdateLock.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Advisory wakelock-like mechanism by which processes that should not be interrupted for
+ * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
+ * or kiosk-like operation.
+ *
+ * @hide
+ */
+public class UpdateLock {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UpdateLock";
+
+    private static IUpdateLock sService;
+    private static void checkService() {
+        if (sService == null) {
+            sService = IUpdateLock.Stub.asInterface(
+                    ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
+        }
+    }
+
+    IBinder mToken;
+    int mCount = 0;
+    boolean mRefCounted = true;
+    boolean mHeld = false;
+    final String mTag;
+
+    /**
+     * Broadcast Intent action sent when the global update lock state changes,
+     * i.e. when the first locker acquires an update lock, or when the last
+     * locker releases theirs.  The broadcast is sticky but is sent only to
+     * registered receivers.
+     */
+    public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
+
+    /**
+     * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
+     * whether now is an appropriate time to interrupt device activity with an
+     * update operation.  True means that updates are okay right now; false indicates
+     * that perhaps later would be a better time.
+     */
+    public static final String NOW_IS_CONVENIENT = "nowisconvenient";
+
+    /**
+     * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
+     * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
+     * in the System.currentTimeMillis() time base, which may be non-monotonic especially
+     * around reboots.
+     */
+    public static final String TIMESTAMP = "timestamp";
+
+    /**
+     * Construct an UpdateLock instance.
+     * @param tag An arbitrary string used to identify this lock instance in dump output.
+     */
+    public UpdateLock(String tag) {
+        mTag = tag;
+        mToken = new Binder();
+    }
+
+    /**
+     * Change the refcount behavior of this update lock.
+     */
+    public void setReferenceCounted(boolean isRefCounted) {
+        if (DEBUG) {
+            Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
+        }
+        mRefCounted = isRefCounted;
+    }
+
+    /**
+     * Is this lock currently held?
+     */
+    public boolean isHeld() {
+        synchronized (mToken) {
+            return mHeld;
+        }
+    }
+
+    /**
+     * Acquire an update lock.
+     */
+    public void acquire() {
+        if (DEBUG) {
+            Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
+        }
+        checkService();
+        synchronized (mToken) {
+            acquireLocked();
+        }
+    }
+
+    private void acquireLocked() {
+        if (!mRefCounted || mCount++ == 0) {
+            if (sService != null) {
+                try {
+                    sService.acquireUpdateLock(mToken, mTag);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to acquire");
+                }
+            }
+            mHeld = true;
+        }
+    }
+
+    /**
+     * Release this update lock.
+     */
+    public void release() {
+        if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
+        checkService();
+        synchronized (mToken) {
+            releaseLocked();
+        }
+    }
+
+    private void releaseLocked() {
+        if (!mRefCounted || --mCount == 0) {
+            if (sService != null) {
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+            mHeld = false;
+        }
+        if (mCount < 0) {
+            throw new RuntimeException("UpdateLock under-locked");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        synchronized (mToken) {
+            // if mHeld is true, sService must be non-null
+            if (mHeld) {
+                Log.wtf(TAG, "UpdateLock finalized while still held");
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 413150b..83799c4 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -402,8 +402,8 @@
          * A comma separated list of reminder methods supported for this
          * calendar in the format "#,#,#". Valid types are
          * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
-         * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column
-         * name.
+         * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS},
+         * {@link Reminders#METHOD_ALARM}. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ALLOWED_REMINDERS = "allowedReminders";
@@ -856,6 +856,17 @@
         public static final String EVENT_COLOR_KEY = "eventColor_index";
 
         /**
+         * This will be {@link #EVENT_COLOR} if it is not null; otherwise, this will be
+         * {@link Calendars#CALENDAR_COLOR}.
+         * Read-only value. To modify, write to {@link #EVENT_COLOR} or
+         * {@link Calendars#CALENDAR_COLOR} directly.
+         *<P>
+         *     Type: INTEGER
+         *</P>
+         */
+        public static final String DISPLAY_COLOR = "displayColor";
+
+        /**
          * The event status. Column name.
          * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
          */
@@ -1930,11 +1941,11 @@
 
         /**
          * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
-         * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS}
-         * are possible values; the device will only process
-         * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the
-         * other types are simply stored so we can send the same reminder info
-         * back to the server when we make changes).
+         * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, {@link #METHOD_SMS} and
+         * {@link #METHOD_ALARM} are possible values; the device will only
+         * process {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders
+         * (the other types are simply stored so we can send the same reminder
+         * info back to the server when we make changes).
          */
         public static final String METHOD = "method";
 
@@ -1942,6 +1953,7 @@
         public static final int METHOD_ALERT = 1;
         public static final int METHOD_EMAIL = 2;
         public static final int METHOD_SMS = 3;
+        public static final int METHOD_ALARM = 4;
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index d3ad63d..6c6b1184 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1833,19 +1833,19 @@
             public static final String LANGUAGE = "language";
 
             /**
-             * The latitude where the image was captured.
+             * The latitude where the video was captured.
              * <P>Type: DOUBLE</P>
              */
             public static final String LATITUDE = "latitude";
 
             /**
-             * The longitude where the image was captured.
+             * The longitude where the video was captured.
              * <P>Type: DOUBLE</P>
              */
             public static final String LONGITUDE = "longitude";
 
             /**
-             * The date & time that the image was taken in units
+             * The date & time that the video was taken in units
              * of milliseconds since jan 1, 1970.
              * <P>Type: INTEGER</P>
              */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0aad64a..b42417a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3125,15 +3125,8 @@
          * ms delay before rechecking an 'online' wifi connection when it is thought to be unstable.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS =
-                "wifi_watchdog_dns_check_short_interval_ms";
-
-        /**
-         * ms delay before rechecking an 'online' wifi connection when it is thought to be stable.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS =
-                "wifi_watchdog_dns_check_long_interval_ms";
+        public static final String WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS =
+                "wifi_watchdog_arp_interval_ms";
 
         /**
          * ms delay before rechecking a connect SSID for walled garden with a http download.
@@ -3143,44 +3136,28 @@
                 "wifi_watchdog_walled_garden_interval_ms";
 
         /**
-         * max blacklist calls on an SSID before full dns check failures disable the network.
+         * Number of ARP pings per check.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_MAX_SSID_BLACKLISTS =
-                "wifi_watchdog_max_ssid_blacklists";
+        public static final String WIFI_WATCHDOG_NUM_ARP_PINGS = "wifi_watchdog_num_arp_pings";
 
         /**
-         * Number of dns pings per check.
+         * Minimum number of responses to the arp pings to consider the test 'successful'.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_NUM_DNS_PINGS = "wifi_watchdog_num_dns_pings";
+        public static final String WIFI_WATCHDOG_MIN_ARP_RESPONSES =
+                "wifi_watchdog_min_arp_responses";
 
         /**
-         * Minimum number of responses to the dns pings to consider the test 'successful'.
+         * Timeout on ARP pings
          * @hide
          */
-        public static final String WIFI_WATCHDOG_MIN_DNS_RESPONSES =
-                "wifi_watchdog_min_dns_responses";
+        public static final String WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS =
+                "wifi_watchdog_arp_ping_timeout_ms";
 
         /**
-         * Timeout on dns pings
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS =
-                "wifi_watchdog_dns_ping_timeout_ms";
-
-        /**
-         * We consider action from a 'blacklist' call to have finished by the end of
-         * this interval.  If we are connected to the same AP with no network connection,
-         * we are likely stuck on an SSID with no external connectivity.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS =
-                "wifi_watchdog_blacklist_followup_interval_ms";
-
-        /**
-         * Setting to turn off poor network avoidance on Wi-Fi. Feature is disabled by default and
-         * the setting needs to be set to 1 to enable it.
+         * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
+         * the setting needs to be set to 0 to disable it.
          * @hide
          */
         public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
@@ -3204,14 +3181,6 @@
                 "wifi_watchdog_walled_garden_url";
 
         /**
-         * Boolean to determine whether to notify on disabling a network.  Secure setting used
-         * to notify user only once.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP =
-                "wifi_watchdog_show_disabled_network_popup";
-
-        /**
          * The maximum number of times we will retry a connection to an access
          * point for which we have failed in acquiring an IP address from DHCP.
          * A value of N means that we will make N+1 connection attempts in all.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index ca7263c..8c97293 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import com.android.internal.util.ArrayUtils;
 import org.ccil.cowan.tagsoup.HTMLSchema;
 import org.ccil.cowan.tagsoup.Parser;
 import org.xml.sax.Attributes;
@@ -45,13 +46,11 @@
 import android.text.style.TypefaceSpan;
 import android.text.style.URLSpan;
 import android.text.style.UnderlineSpan;
-import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
 import java.io.IOException;
 import java.io.StringReader;
-import java.nio.CharBuffer;
 import java.util.HashMap;
 
 /**
@@ -203,9 +202,26 @@
         }
     }
 
+    private static String getOpenParaTagWithDirection(Spanned text, int start, int end) {
+        final int len = end - start;
+        final byte[] levels = new byte[ArrayUtils.idealByteArraySize(len)];
+        final char[] buffer = TextUtils.obtain(len);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        int paraDir = AndroidBidi.bidi(Layout.DIR_REQUEST_DEFAULT_LTR, buffer, levels, len,
+                false /* no info */);
+        switch(paraDir) {
+            case Layout.DIR_RIGHT_TO_LEFT:
+                return "<p dir=rtl>";
+            case Layout.DIR_LEFT_TO_RIGHT:
+            default:
+                return "<p dir=ltr>";
+        }
+    }
+
     private static void withinBlockquote(StringBuilder out, Spanned text,
                                          int start, int end) {
-        out.append("<p>");
+        out.append(getOpenParaTagWithDirection(text, start, end));
 
         int next;
         for (int i = start; i < end; i = next) {
@@ -340,7 +356,7 @@
             }
         }
 
-        String p = last ? "" : "</p>\n<p>";
+        String p = last ? "" : "</p>\n" + getOpenParaTagWithDirection(text, start, end);
 
         if (nl == 1) {
             out.append("<br>\n");
@@ -350,7 +366,6 @@
             for (int i = 2; i < nl; i++) {
                 out.append("<br>");
             }
-
             out.append(p);
         }
     }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 1dd4c8a..299e115 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -716,7 +716,8 @@
             boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
             boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
 
-            boolean doEllipsis = (firstLine && !moreChars &&
+            boolean doEllipsis =
+                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
                                 ellipsize != TextUtils.TruncateAt.MARQUEE) ||
                         (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
                                 ellipsize == TextUtils.TruncateAt.END);
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index ae41eab..6ca6161 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -18,6 +18,7 @@
 
 
 import android.util.LocaleUtil;
+import android.view.View;
 
 /**
  * Some objects that implement TextDirectionHeuristic.
@@ -240,7 +241,7 @@
         @Override
         protected boolean defaultIsRtl() {
             final int dir = LocaleUtil.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
-            return (dir == LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE);
+            return (dir == View.LAYOUT_DIRECTION_RTL);
         }
 
         public static final TextDirectionHeuristicLocale INSTANCE =
diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java
index 4d773f6..9953252 100644
--- a/core/java/android/util/LocaleUtil.java
+++ b/core/java/android/util/LocaleUtil.java
@@ -18,6 +18,7 @@
 
 import java.util.Locale;
 
+import android.view.View;
 import libcore.icu.ICU;
 
 /**
@@ -29,16 +30,6 @@
 
     private LocaleUtil() { /* cannot be instantiated */ }
 
-    /**
-     * @hide Do not use. Implementation not finished.
-     */
-    public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0;
-
-    /**
-     * @hide Do not use. Implementation not finished.
-     */
-    public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1;
-
     private static String ARAB_SCRIPT_SUBTAG = "Arab";
     private static String HEBR_SCRIPT_SUBTAG = "Hebr";
 
@@ -47,8 +38,8 @@
      *
      * @param locale the Locale for which we want the layout direction. Can be null.
      * @return the layout direction. This may be one of:
-     * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
-     * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
+     * {@link View#LAYOUT_DIRECTION_LTR} or
+     * {@link View#LAYOUT_DIRECTION_RTL}.
      *
      * Be careful: this code will need to be changed when vertical scripts will be supported
      *
@@ -61,11 +52,11 @@
 
             if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
                     scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
-                return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_RTL;
             }
         }
 
-        return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+        return View.LAYOUT_DIRECTION_LTR;
     }
 
     /**
@@ -75,8 +66,8 @@
      *
      * @param locale
      * @return the layout direction. This may be one of:
-     * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
-     * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
+     * {@link View#LAYOUT_DIRECTION_LTR} or
+     * {@link View#LAYOUT_DIRECTION_RTL}.
      *
      * Be careful: this code will need to be changed when vertical scripts will be supported
      *
@@ -86,11 +77,11 @@
         switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_RTL;
 
             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
             default:
-                return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_LTR;
         }
     }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 93299eb..2883eca 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -25,6 +25,8 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.TimeZone;
 import java.util.Date;
 
@@ -35,18 +37,26 @@
  */
 public class TimeUtils {
     /** @hide */ public TimeUtils() {}
+    private static final boolean DBG = false;
     private static final String TAG = "TimeUtils";
 
+    /** Cached results of getTineZones */
+    private static final Object sLastLockObj = new Object();
+    private static ArrayList<TimeZone> sLastZones = null;
+    private static String sLastCountry = null;
+
+    /** Cached results of getTimeZonesWithUniqueOffsets */
+    private static final Object sLastUniqueLockObj = new Object();
+    private static ArrayList<TimeZone> sLastUniqueZoneOffsets = null;
+    private static String sLastUniqueCountry = null;
+
+
     /**
      * Tries to return a time zone that would have had the specified offset
      * and DST value at the specified moment in the specified country.
      * Returns null if no suitable zone could be found.
      */
     public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
-        if (country == null) {
-            return null;
-        }
-
         TimeZone best = null;
 
         Resources r = Resources.getSystem();
@@ -58,6 +68,107 @@
         int currentOffset = current.getOffset(when);
         boolean currentDst = current.inDaylightTime(d);
 
+        for (TimeZone tz : getTimeZones(country)) {
+            // If the current time zone is from the right country
+            // and meets the other known properties, keep it
+            // instead of changing to another one.
+
+            if (tz.getID().equals(currentName)) {
+                if (currentOffset == offset && currentDst == dst) {
+                    return current;
+                }
+            }
+
+            // Otherwise, take the first zone from the right
+            // country that has the correct current offset and DST.
+            // (Keep iterating instead of returning in case we
+            // haven't encountered the current time zone yet.)
+
+            if (best == null) {
+                if (tz.getOffset(when) == offset &&
+                    tz.inDaylightTime(d) == dst) {
+                    best = tz;
+                }
+            }
+        }
+
+        return best;
+    }
+
+    /**
+     * Return list of unique time zones for the country. Do not modify
+     *
+     * @param country to find
+     * @return list of unique time zones, maybe empty but never null. Do not modify.
+     * @hide
+     */
+    public static ArrayList<TimeZone> getTimeZonesWithUniqueOffsets(String country) {
+        synchronized(sLastUniqueLockObj) {
+            if ((country != null) && country.equals(sLastUniqueCountry)) {
+                if (DBG) {
+                    Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
+                            country + "): return cached version");
+                }
+                return sLastUniqueZoneOffsets;
+            }
+        }
+
+        Collection<TimeZone> zones = getTimeZones(country);
+        ArrayList<TimeZone> uniqueTimeZones = new ArrayList<TimeZone>();
+        for (TimeZone zone : zones) {
+            // See if we already have this offset,
+            // Using slow but space efficient and these are small.
+            boolean found = false;
+            for (int i = 0; i < uniqueTimeZones.size(); i++) {
+                if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found == false) {
+                if (DBG) {
+                    Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
+                            zone.getRawOffset() + " zone.getID=" + zone.getID());
+                }
+                uniqueTimeZones.add(zone);
+            }
+        }
+
+        synchronized(sLastUniqueLockObj) {
+            // Cache the last result
+            sLastUniqueZoneOffsets = uniqueTimeZones;
+            sLastUniqueCountry = country;
+
+            return sLastUniqueZoneOffsets;
+        }
+    }
+
+    /**
+     * Returns the time zones for the country, which is the code
+     * attribute of the timezone element in time_zones_by_country.xml. Do not modify.
+     *
+     * @param country is a two character country code.
+     * @return TimeZone list, maybe empty but never null. Do not modify.
+     * @hide
+     */
+    public static ArrayList<TimeZone> getTimeZones(String country) {
+        synchronized (sLastLockObj) {
+            if ((country != null) && country.equals(sLastCountry)) {
+                if (DBG) Log.d(TAG, "getTimeZones(" + country + "): return cached version");
+                return sLastZones;
+            }
+        }
+
+        ArrayList<TimeZone> tzs = new ArrayList<TimeZone>();
+
+        if (country == null) {
+            if (DBG) Log.d(TAG, "getTimeZones(null): return empty list");
+            return tzs;
+        }
+
+        Resources r = Resources.getSystem();
+        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
+
         try {
             XmlUtils.beginDocument(parser, "timezones");
 
@@ -73,43 +184,33 @@
 
                 if (country.equals(code)) {
                     if (parser.next() == XmlPullParser.TEXT) {
-                        String maybe = parser.getText();
-
-                        // If the current time zone is from the right country
-                        // and meets the other known properties, keep it
-                        // instead of changing to another one.
-
-                        if (maybe.equals(currentName)) {
-                            if (currentOffset == offset && currentDst == dst) {
-                                return current;
-                            }
-                        }
-
-                        // Otherwise, take the first zone from the right
-                        // country that has the correct current offset and DST.
-                        // (Keep iterating instead of returning in case we
-                        // haven't encountered the current time zone yet.)
-
-                        if (best == null) {
-                            TimeZone tz = TimeZone.getTimeZone(maybe);
-
-                            if (tz.getOffset(when) == offset &&
-                                tz.inDaylightTime(d) == dst) {
-                                best = tz;
+                        String zoneIdString = parser.getText();
+                        TimeZone tz = TimeZone.getTimeZone(zoneIdString);
+                        if (tz.getID().startsWith("GMT") == false) {
+                            // tz.getID doesn't start not "GMT" so its valid
+                            tzs.add(tz);
+                            if (DBG) {
+                                Log.d(TAG, "getTimeZone('" + country + "'): found tz.getID=="
+                                    + ((tz != null) ? tz.getID() : "<no tz>"));
                             }
                         }
                     }
                 }
             }
         } catch (XmlPullParserException e) {
-            Log.e(TAG, "Got exception while getting preferred time zone.", e);
+            Log.e(TAG, "Got xml parser exception getTimeZone('" + country + "'): e=", e);
         } catch (IOException e) {
-            Log.e(TAG, "Got exception while getting preferred time zone.", e);
+            Log.e(TAG, "Got IO exception getTimeZone('" + country + "'): e=", e);
         } finally {
             parser.close();
         }
 
-        return best;
+        synchronized(sLastLockObj) {
+            // Cache the last result;
+            sLastZones = tzs;
+            sLastCountry = country;
+            return sLastZones;
+        }
     }
 
     /**
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 36582af..ee0fa86 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -718,7 +718,7 @@
     @Override
     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
                     startAngle, sweepAngle, useCenter, paint.mNativePaint);
@@ -902,7 +902,7 @@
 
     @Override
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
         } finally {
@@ -939,7 +939,7 @@
         if ((offset | count) < 0 || offset + count > pts.length) {
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
         } finally {
@@ -957,7 +957,7 @@
 
     @Override
     public void drawOval(RectF oval, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
         } finally {
@@ -977,7 +977,7 @@
 
     @Override
     public void drawPath(Path path, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             if (path.isSimplePath) {
                 if (path.rects != null) {
@@ -1048,7 +1048,7 @@
 
     @Override
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
         } finally {
@@ -1097,7 +1097,8 @@
 
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        if (left == right || top == bottom) return;
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
         } finally {
@@ -1125,7 +1126,7 @@
 
     @Override
     public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        int modifiers = setupModifiers(paint);
+        int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
                     rx, ry, paint.mNativePaint);
@@ -1341,6 +1342,30 @@
         return modifiers;
     }
 
+    private int setupModifiers(Paint paint, int flags) {
+        int modifiers = MODIFIER_NONE;
+
+        if (paint.hasShadow && (flags & MODIFIER_SHADOW) != 0) {
+            nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
+                    paint.shadowColor);
+            modifiers |= MODIFIER_SHADOW;
+        }
+
+        final Shader shader = paint.getShader();
+        if (shader != null && (flags & MODIFIER_SHADER) != 0) {
+            nSetupShader(mRenderer, shader.native_shader);
+            modifiers |= MODIFIER_SHADER;
+        }
+
+        final ColorFilter filter = paint.getColorFilter();
+        if (filter != null && (flags & MODIFIER_COLOR_FILTER) != 0) {
+            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
+            modifiers |= MODIFIER_COLOR_FILTER;
+        }
+
+        return modifiers;
+    }
+
     private int setupColorFilter(Paint paint) {
         final ColorFilter filter = paint.getColorFilter();
         if (filter != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0675a74..f7dc73c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,7 +40,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -957,28 +956,24 @@
     /**
      * Horizontal direction of this view is from Left to Right.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
 
     /**
      * Horizontal direction of this view is from Right to Left.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
 
     /**
      * Horizontal direction of this view is inherited from its parent.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
 
     /**
      * Horizontal direction of this view is from deduced from the default language
      * script for the locale. Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
 
@@ -4834,8 +4829,6 @@
      *   {@link #LAYOUT_DIRECTION_INHERIT} or
      *   {@link #LAYOUT_DIRECTION_LOCALE}.
      * @attr ref android.R.styleable#View_layoutDirection
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "LTR"),
@@ -4857,8 +4850,6 @@
      *   {@link #LAYOUT_DIRECTION_LOCALE}.
      *
      * @attr ref android.R.styleable#View_layoutDirection
-     *
-     * @hide
      */
     @RemotableViewMethod
     public void setLayoutDirection(int layoutDirection) {
@@ -4874,8 +4865,6 @@
      *
      * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
      * {@link #LAYOUT_DIRECTION_LTR} id the layout direction is not RTL.
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "RESOLVED_DIRECTION_LTR"),
@@ -4892,8 +4881,6 @@
      * layout attribute and/or the inherited value from the parent.</p>
      *
      * @return true if the layout is right-to-left.
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout")
     public boolean isLayoutRtl() {
@@ -4906,8 +4893,6 @@
      * the framework should take special note to preserve when possible.
      *
      * @return true if the view has transient state
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout")
     public boolean hasTransientState() {
@@ -4919,8 +4904,6 @@
      * framework should attempt to preserve when possible.
      *
      * @param hasTransientState true if this view has transient state
-     *
-     * @hide
      */
     public void setHasTransientState(boolean hasTransientState) {
         if (hasTransientState() == hasTransientState) return;
@@ -5048,16 +5031,17 @@
      *        the View's internal state from a previously set "pressed" state.
      */
     public void setPressed(boolean pressed) {
-        if (pressed == ((mPrivateFlags & PRESSED) == PRESSED)) {
-            return;
-        }
+        final boolean needsRefresh = pressed != ((mPrivateFlags & PRESSED) == PRESSED);
 
         if (pressed) {
             mPrivateFlags |= PRESSED;
         } else {
             mPrivateFlags &= ~PRESSED;
         }
-        refreshDrawableState();
+
+        if (needsRefresh) {
+            refreshDrawableState();
+        }
         dispatchSetPressed(pressed);
     }
 
@@ -9610,10 +9594,19 @@
 
         // Set to resolved
         mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
+        onResolvedLayoutDirectionChanged();
     }
 
     /**
-     * Force padding depending on layout direction.
+     * Called when layout direction has been resolved.
+     *
+     * The default implementation does nothing.
+     */
+    public void onResolvedLayoutDirectionChanged() {
+    }
+
+    /**
+     * Resolve padding depending on layout direction.
      */
     public void resolvePadding() {
         // If the user specified the absolute padding (either with android:padding or
@@ -9657,7 +9650,7 @@
         mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
 
         recomputePadding();
-        onResolvePadding(resolvedLayoutDirection);
+        onPaddingChanged(resolvedLayoutDirection);
     }
 
     /**
@@ -9668,15 +9661,15 @@
      * @param layoutDirection the direction of the layout
      *
      */
-    public void onResolvePadding(int layoutDirection) {
+    public void onPaddingChanged(int layoutDirection) {
     }
 
     /**
-     * Return true if layout direction resolution can be done
+     * Check if layout direction resolution can be done.
      *
-     * @hide
+     * @return true if layout direction resolution can be done otherwise return false.
      */
-    protected boolean canResolveLayoutDirection() {
+    public boolean canResolveLayoutDirection() {
         switch (getLayoutDirection()) {
             case LAYOUT_DIRECTION_INHERIT:
                 return (mParent != null);
@@ -9686,32 +9679,36 @@
     }
 
     /**
-     * Reset the resolved layout direction.
-     *
-     * Subclasses need to override this method to clear cached information that depends on the
-     * resolved layout direction, or to inform child views that inherit their layout direction.
-     * Overrides must also call the superclass implementation at the start of their implementation.
-     *
-     * @hide
+     * Reset the resolved layout direction. Will call {@link View#onResolvedLayoutDirectionReset}
+     * when reset is done.
      */
-    protected void resetResolvedLayoutDirection() {
-        // Reset the layout direction resolution
+    public void resetResolvedLayoutDirection() {
+        // Reset the current View resolution
         mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
+        onResolvedLayoutDirectionReset();
         // Reset also the text direction
         resetResolvedTextDirection();
     }
 
     /**
-     * Check if a Locale is corresponding to a RTL script.
+     * Called during reset of resolved layout direction.
+     *
+     * Subclasses need to override this method to clear cached information that depends on the
+     * resolved layout direction, or to inform child views that inherit their layout direction.
+     *
+     * The default implementation does nothing.
+     */
+    public void onResolvedLayoutDirectionReset() {
+    }
+
+    /**
+     * Check if a Locale uses an RTL script.
      *
      * @param locale Locale to check
-     * @return true if a Locale is corresponding to a RTL script.
-     *
-     * @hide
+     * @return true if the Locale uses an RTL script.
      */
     protected static boolean isLayoutDirectionRtl(Locale locale) {
-        return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE ==
-                LocaleUtil.getLayoutDirectionFromLocale(locale));
+        return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale));
     }
 
     /**
@@ -11866,8 +11863,6 @@
     * Return the layout direction of a given Drawable.
     *
     * @param who the Drawable to query
-    *
-    * @hide
     */
     public int getResolvedLayoutDirection(Drawable who) {
         return (who == mBGDrawable) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
@@ -13060,7 +13055,7 @@
 
         if (mParent != null) {
             if (mLayoutParams != null) {
-                mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
+                mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
             }
             if (!mParent.isLayoutRequested()) {
                 mParent.requestLayout();
@@ -14156,8 +14151,8 @@
     }
 
     /**
-     * Resolve the text direction. Will call {@link View#onResolveTextDirection()} when resolution
-     * is done.
+     * Resolve the text direction. Will call {@link View#onResolvedTextDirectionChanged} when
+     * resolution is done.
      */
     public void resolveTextDirection() {
         if (mResolvedTextDirection != TEXT_DIRECTION_INHERIT) {
@@ -14171,24 +14166,26 @@
         } else {
             mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
         }
-        onResolveTextDirection();
+        onResolvedTextDirectionChanged();
     }
 
     /**
      * Called when text direction has been resolved. Subclasses that care about text direction
-     * resolution should override this method. The default implementation does nothing.
+     * resolution should override this method.
+     *
+     * The default implementation does nothing.
      */
-    public void onResolveTextDirection() {
+    public void onResolvedTextDirectionChanged() {
     }
 
     /**
      * Reset resolved text direction. Text direction can be resolved with a call to
-     * getResolvedTextDirection(). Will call {@link View#onResetResolvedTextDirection()} when
+     * getResolvedTextDirection(). Will call {@link View#onResolvedTextDirectionReset} when
      * reset is done.
      */
     public void resetResolvedTextDirection() {
         mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
-        onResetResolvedTextDirection();
+        onResolvedTextDirectionReset();
     }
 
     /**
@@ -14196,7 +14193,7 @@
      * override this method and do a reset of the text direction of their children. The default
      * implementation does nothing.
      */
-    public void onResetResolvedTextDirection() {
+    public void onResolvedTextDirectionReset() {
     }
 
     //
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2848e88..0c63286 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1041,9 +1041,8 @@
 
     /**
      * {@inheritDoc}
-     *
-     * !!! TODO: write real docs
      */
+    // TODO: Write real docs
     @Override
     public boolean dispatchDragEvent(DragEvent event) {
         boolean retval = false;
@@ -2770,7 +2769,13 @@
         final View[] children = mChildren;
         final int count = mChildrenCount;
         for (int i = 0; i < count; i++) {
-            children[i].setPressed(pressed);
+            final View child = children[i];
+            // Children that are clickable on their own should not
+            // show a pressed state when their parent view does.
+            // Clearing a pressed state always propagates.
+            if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
+                child.setPressed(pressed);
+            }
         }
     }
 
@@ -4915,9 +4920,7 @@
     }
 
     @Override
-    protected void resetResolvedLayoutDirection() {
-        super.resetResolvedLayoutDirection();
-
+    public void onResolvedLayoutDirectionReset() {
         // Take care of resetting the children resolution too
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -4929,7 +4932,7 @@
     }
 
     @Override
-    public void onResetResolvedTextDirection() {
+    public void onResolvedTextDirectionReset() {
         // Take care of resetting the children resolution too
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -5118,10 +5121,8 @@
          *
          * {@link View#LAYOUT_DIRECTION_LTR}
          * {@link View#LAYOUT_DIRECTION_RTL}
-         *
-         * @hide
          */
-        protected void resolveWithDirection(int layoutDirection) {
+        public void onResolveLayoutDirection(int layoutDirection) {
         }
 
         /**
@@ -5372,12 +5373,10 @@
 
         /**
          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
-         * maybe overriden depending on layout direction.
-         *
-         * @hide
+         * may be overridden depending on layout direction.
          */
         @Override
-        protected void resolveWithDirection(int layoutDirection) {
+        public void onResolveLayoutDirection(int layoutDirection) {
             switch(layoutDirection) {
                 case View.LAYOUT_DIRECTION_RTL:
                     leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 6dbdedb..d482b35 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -105,6 +105,7 @@
     private View[] mViews;
     private ViewRootImpl[] mRoots;
     private WindowManager.LayoutParams[] mParams;
+    private boolean mNeedsEglTerminate;
 
     private final static Object sLock = new Object();
     private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
@@ -436,8 +437,6 @@
                 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                     // On low and medium end gfx devices
                     if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
-                        // Force a full memory flush
-                        HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
                         // Destroy all hardware surfaces and resources associated to
                         // known windows
                         synchronized (this) {
@@ -447,8 +446,9 @@
                                 mRoots[i].terminateHardwareResources();
                             }
                         }
-                        // Terminate the hardware renderer to free all resources
-                        ManagedEGLContext.doTerminate();
+                        // Force a full memory flush
+                        HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+                        mNeedsEglTerminate = true;
                         break;
                     }
                     // high end gfx devices fall through to next case
@@ -461,6 +461,16 @@
     /**
      * @hide
      */
+    public void terminateEgl() {
+        if (mNeedsEglTerminate) {
+            ManagedEGLContext.doTerminate();
+            mNeedsEglTerminate = false;
+        }
+    }
+
+    /**
+     * @hide
+     */
     public void trimLocalMemory() {
         synchronized (this) {
             if (mViews == null) return;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index e8c0239..dc8c71b7 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -913,7 +913,7 @@
      * 
      * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
      *        after it has been run through the interpolation function.
-     * @param t The Transofrmation object to fill in with the current
+     * @param t The Transformation object to fill in with the current
      *        transforms.
      */
     protected void applyTransformation(float interpolatedTime, Transformation t) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 89aba3c..7ec5398 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -152,6 +152,15 @@
     }
 
     /**
+     * Called when this InputConnection is no longer used by the InputMethodManager.
+     *
+     * @hide
+     */
+    protected void reportFinish() {
+        // Intentionaly empty
+    }
+
+    /**
      * Default implementation uses
      * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
      * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b6b27c1..3b6ebbe 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -708,6 +708,10 @@
     public void reportFinishInputConnection(InputConnection ic) {
         if (mServedInputConnection != ic) {
             ic.finishComposingText();
+            // To avoid modifying the public InputConnection interface
+            if (ic instanceof BaseInputConnection) {
+                ((BaseInputConnection) ic).reportFinish();
+            }
         }
     }
 
diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.java b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
index 8d7c6cf..cb9e496 100644
--- a/core/java/android/view/textservice/SentenceSuggestionsInfo.java
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
@@ -84,6 +84,13 @@
     /**
      * @hide
      */
+    public int getSuggestionsCount() {
+        return mSuggestionsInfos.length;
+    }
+
+    /**
+     * @hide
+     */
     public SuggestionsInfo getSuggestionsInfoAt(int i) {
         if (i >= 0 && i < mSuggestionsInfos.length) {
             return mSuggestionsInfos[i];
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 3491a537..9105f19 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -180,9 +180,9 @@
     /**
      * @hide
      */
-    public void getSentenceSuggestions(TextInfo textInfo, int suggestionsLimit) {
+    public void getSentenceSuggestions(TextInfo[] textInfo, int suggestionsLimit) {
         mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
-                new TextInfo[] {textInfo}, suggestionsLimit);
+                textInfo, suggestionsLimit);
     }
 
     /**
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index db66305..11bd815 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -79,8 +79,8 @@
     private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
         new ArrayList<AccessibilityWebContentKeyBinding>();
 
-    // handle to the WebView this injector is associated with.
-    private final WebView mWebView;
+    // handle to the WebViewClassic this injector is associated with.
+    private final WebViewClassic mWebView;
 
     // events scheduled for sending as soon as we receive the selected text
     private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
@@ -98,11 +98,11 @@
     private int mLastDirection;
 
     /**
-     * Creates a new injector associated with a given {@link WebView}.
+     * Creates a new injector associated with a given {@link WebViewClassic}.
      *
-     * @param webView The associated WebView.
+     * @param webView The associated WebViewClassic.
      */
-    public AccessibilityInjector(WebView webView) {
+    public AccessibilityInjector(WebViewClassic webView) {
         mWebView = webView;
         ensureWebContentKeyBindings();
     }
@@ -327,7 +327,7 @@
         AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED);
         event.setClassName(mWebView.getClass().getName());
         event.setPackageName(mWebView.getContext().getPackageName());
-        event.setEnabled(mWebView.isEnabled());
+        event.setEnabled(mWebView.getWebView().isEnabled());
         return event;
     }
 
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 8ccc59c7..f09e29d 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -70,7 +70,7 @@
     private final static int MAX_OUTSTANDING_REQUESTS = 300;
 
     private final CallbackProxy mCallbackProxy;
-    private final WebSettings mSettings;
+    private final WebSettingsClassic mSettings;
     private final Context mContext;
     private final WebViewDatabase mDatabase;
     private final WebViewCore mWebViewCore;
@@ -200,7 +200,7 @@
      * XXX: Called by WebCore thread.
      */
     public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
-            WebSettings settings, Map<String, Object> javascriptInterfaces) {
+            WebSettingsClassic settings, Map<String, Object> javascriptInterfaces) {
 
         Context appContext = context.getApplicationContext();
 
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 3a05bca..484c449 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -61,8 +61,8 @@
     private volatile WebViewClient mWebViewClient;
     // Instance of WebChromeClient for handling all chrome functions.
     private volatile WebChromeClient mWebChromeClient;
-    // Instance of WebView for handling UI requests.
-    private final WebView mWebView;
+    // Instance of WebViewClassic for handling UI requests.
+    private final WebViewClassic mWebView;
     // Client registered callback listener for download events
     private volatile DownloadListener mDownloadListener;
     // Keep track of multiple progress updates.
@@ -148,7 +148,7 @@
     /**
      * Construct a new CallbackProxy.
      */
-    public CallbackProxy(Context context, WebView w) {
+    public CallbackProxy(Context context, WebViewClassic w) {
         // Used to start a default activity.
         mContext = context;
         mWebView = w;
@@ -221,7 +221,7 @@
         }
         boolean override = false;
         if (mWebViewClient != null) {
-            override = mWebViewClient.shouldOverrideUrlLoading(mWebView,
+            override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
                     overrideUrl);
         } else {
             Intent intent = new Intent(Intent.ACTION_VIEW,
@@ -248,7 +248,7 @@
      */
     public boolean uiOverrideKeyEvent(KeyEvent event) {
         if (mWebViewClient != null) {
-            return mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
+            return mWebViewClient.shouldOverrideKeyEvent(mWebView.getWebView(), event);
         }
         return false;
     }
@@ -264,7 +264,8 @@
                 String startedUrl = msg.getData().getString("url");
                 mWebView.onPageStarted(startedUrl);
                 if (mWebViewClient != null) {
-                    mWebViewClient.onPageStarted(mWebView, startedUrl, (Bitmap) msg.obj);
+                    mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
+                            (Bitmap) msg.obj);
                 }
                 break;
 
@@ -272,26 +273,26 @@
                 String finishedUrl = (String) msg.obj;
                 mWebView.onPageFinished(finishedUrl);
                 if (mWebViewClient != null) {
-                    mWebViewClient.onPageFinished(mWebView, finishedUrl);
+                    mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
                 }
                 break;
 
             case RECEIVED_ICON:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj);
+                    mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
                 }
                 break;
 
             case RECEIVED_TOUCH_ICON_URL:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedTouchIconUrl(mWebView,
+                    mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
                             (String) msg.obj, msg.arg1 == 1);
                 }
                 break;
 
             case RECEIVED_TITLE:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onReceivedTitle(mWebView,
+                    mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
                             (String) msg.obj);
                 }
                 break;
@@ -301,7 +302,7 @@
                     int reasonCode = msg.arg1;
                     final String description  = msg.getData().getString("description");
                     final String failUrl  = msg.getData().getString("failingUrl");
-                    mWebViewClient.onReceivedError(mWebView, reasonCode,
+                    mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
                             description, failUrl);
                 }
                 break;
@@ -312,7 +313,7 @@
                 Message dontResend =
                         (Message) msg.getData().getParcelable("dontResend");
                 if (mWebViewClient != null) {
-                    mWebViewClient.onFormResubmission(mWebView, dontResend,
+                    mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
                             resend);
                 } else {
                     dontResend.sendToTarget();
@@ -335,7 +336,7 @@
                     HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
                     String host = msg.getData().getString("host");
                     String realm = msg.getData().getString("realm");
-                    mWebViewClient.onReceivedHttpAuthRequest(mWebView, handler,
+                    mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
                             host, realm);
                 }
                 break;
@@ -344,7 +345,7 @@
                 if (mWebViewClient != null) {
                     HashMap<String, Object> map =
                         (HashMap<String, Object>) msg.obj;
-                    mWebViewClient.onReceivedSslError(mWebView,
+                    mWebViewClient.onReceivedSslError(mWebView.getWebView(),
                             (SslErrorHandler) map.get("handler"),
                             (SslError) map.get("error"));
                 }
@@ -352,7 +353,7 @@
 
             case PROCEEDED_AFTER_SSL_ERROR:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onProceededAfterSslError(mWebView,
+                    mWebViewClient.onProceededAfterSslError(mWebView.getWebView(),
                             (SslError) msg.obj);
                 }
                 break;
@@ -361,7 +362,7 @@
                 if (mWebViewClient != null) {
                     HashMap<String, Object> map =
                         (HashMap<String, Object>) msg.obj;
-                    mWebViewClient.onReceivedClientCertRequest(mWebView,
+                    mWebViewClient.onReceivedClientCertRequest(mWebView.getWebView(),
                             (ClientCertRequestHandler) map.get("handler"),
                             (String) map.get("host_and_port"));
                 }
@@ -373,7 +374,7 @@
                 // changed.
                 synchronized (this) {
                     if (mWebChromeClient != null) {
-                        mWebChromeClient.onProgressChanged(mWebView,
+                        mWebChromeClient.onProgressChanged(mWebView.getWebView(),
                                 mLatestProgress);
                     }
                     mProgressUpdatePending = false;
@@ -382,14 +383,14 @@
 
             case UPDATE_VISITED:
                 if (mWebViewClient != null) {
-                    mWebViewClient.doUpdateVisitedHistory(mWebView,
+                    mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
                             (String) msg.obj, msg.arg1 != 0);
                 }
                 break;
 
             case LOAD_RESOURCE:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onLoadResource(mWebView, (String) msg.obj);
+                    mWebViewClient.onLoadResource(mWebView.getWebView(), (String) msg.obj);
                 }
                 break;
 
@@ -409,7 +410,7 @@
 
             case CREATE_WINDOW:
                 if (mWebChromeClient != null) {
-                    if (!mWebChromeClient.onCreateWindow(mWebView,
+                    if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
                                 msg.arg1 == 1, msg.arg2 == 1,
                                 (Message) msg.obj)) {
                         synchronized (this) {
@@ -422,13 +423,13 @@
 
             case REQUEST_FOCUS:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onRequestFocus(mWebView);
+                    mWebChromeClient.onRequestFocus(mWebView.getWebView());
                 }
                 break;
 
             case CLOSE_WINDOW:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.onCloseWindow((WebView) msg.obj);
+                    mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
                 }
                 break;
 
@@ -449,7 +450,7 @@
 
             case ASYNC_KEYEVENTS:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onUnhandledKeyEvent(mWebView,
+                    mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
                             (KeyEvent) msg.obj);
                 }
                 break;
@@ -516,7 +517,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsAlert(mWebView, url, message,
+                    if (!mWebChromeClient.onJsAlert(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -552,7 +553,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsConfirm(mWebView, url, message,
+                    if (!mWebChromeClient.onJsConfirm(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -597,7 +598,7 @@
                     String message = msg.getData().getString("message");
                     String defaultVal = msg.getData().getString("default");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsPrompt(mWebView, url, message,
+                    if (!mWebChromeClient.onJsPrompt(mWebView.getWebView(), url, message,
                                 defaultVal, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -653,7 +654,7 @@
                     final JsResult res = (JsResult) msg.obj;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
-                    if (!mWebChromeClient.onJsBeforeUnload(mWebView, url,
+                    if (!mWebChromeClient.onJsBeforeUnload(mWebView.getWebView(), url,
                             message, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
@@ -710,7 +711,7 @@
 
             case SCALE_CHANGED:
                 if (mWebViewClient != null) {
-                    mWebViewClient.onScaleChanged(mWebView, msg.getData()
+                    mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
                             .getFloat("old"), msg.getData().getFloat("new"));
                 }
                 break;
@@ -817,7 +818,7 @@
                     String realm = msg.getData().getString("realm");
                     String account = msg.getData().getString("account");
                     String args = msg.getData().getString("args");
-                    mWebViewClient.onReceivedLoginRequest(mWebView, realm,
+                    mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
                             account, args);
                 }
                 break;
@@ -1074,7 +1075,7 @@
         }
         // Note: This method does _not_ send a message.
         WebResourceResponse r =
-                mWebViewClient.shouldInterceptRequest(mWebView, url);
+                mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
         if (r == null) {
             sendMessage(obtainMessage(LOAD_RESOURCE, url));
         }
@@ -1219,7 +1220,8 @@
             return null;
         }
 
-        WebView.WebViewTransport transport = mWebView.new WebViewTransport();
+        WebView.WebViewTransport transport =
+            mWebView.getWebView().new WebViewTransport();
         final Message msg = obtainMessage(NOTIFY);
         msg.obj = transport;
         synchronized (this) {
@@ -1234,7 +1236,7 @@
             }
         }
 
-        WebView w = transport.getWebView();
+        WebViewClassic w = WebViewClassic.fromWebView(transport.getWebView());
         if (w != null) {
             WebViewCore core = w.getWebViewCore();
             // If WebView.destroy() has been called, core may be null.  Skip
@@ -1257,7 +1259,7 @@
         sendEmptyMessage(REQUEST_FOCUS);
     }
 
-    public void onCloseWindow(WebView window) {
+    public void onCloseWindow(WebViewClassic window) {
         // Do an unsynchronized quick check to avoid posting if no callback has
         // been set.
         if (mWebChromeClient == null) {
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 10b0885..964cf3e 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -38,7 +38,7 @@
     private View mCustomView;
     private EditText mEditText;
     private TextView mMatches;
-    private WebView mWebView;
+    private WebViewClassic mWebView;
     private InputMethodManager mInput;
     private Resources mResources;
     private boolean mMatchesFound;
@@ -90,7 +90,7 @@
      * Set the WebView to search.  Must be non null, and set before calling
      * startActionMode.
      */
-    void setWebView(WebView webView) {
+    void setWebView(WebViewClassic webView) {
         if (null == webView) {
             throw new AssertionError("WebView supplied to "
                     + "FindActionModeCallback cannot be null");
@@ -218,7 +218,7 @@
     public void onDestroyActionMode(ActionMode mode) {
         mActionMode = null;
         mWebView.notifyFindDialogDismissed();
-        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+        mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0);
     }
 
     @Override
@@ -232,7 +232,7 @@
             throw new AssertionError(
                     "No WebView for FindActionModeCallback::onActionItemClicked");
         }
-        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+        mInput.hideSoftInputFromWindow(mWebView.getWebView().getWindowToken(), 0);
         switch(item.getItemId()) {
             case com.android.internal.R.id.find_prev:
                 findNext(false);
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
index 91de1d8..225053b 100755
--- a/core/java/android/webkit/GeolocationService.java
+++ b/core/java/android/webkit/GeolocationService.java
@@ -24,7 +24,6 @@
 import android.location.LocationProvider;
 import android.os.Bundle;
 import android.util.Log;
-import android.webkit.WebView;
 import android.webkit.WebViewCore;
 
 
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index aedecf0..8e1f573 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -92,7 +92,7 @@
     private class IsPrivateBrowsingEnabledGetter {
         private boolean mIsReady;
         private boolean mIsPrivateBrowsingEnabled;
-        IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebView webView) {
+        IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebViewClassic webView) {
             new Handler(uiThreadLooper).post(new Runnable() {
                 @Override
                 public void run() {
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index bc0557e..fac549d 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -236,7 +236,7 @@
 
     @Override
     public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebView webView) {
+            HTML5VideoViewProxy proxy, WebViewClassic webView) {
         mFullScreenMode = FULLSCREEN_SURFACECREATING;
         mCurrentBufferPercentage = 0;
         mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 2d5b263..62e812e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -92,6 +92,11 @@
 
     @Override
     public void deleteSurfaceTexture() {
+        cleanupSurfaceTexture();
+        return;
+    }
+
+    public static void cleanupSurfaceTexture() {
         mSurfaceTexture = null;
         mVideoLayerUsingSurfaceTexture = -1;
         return;
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 73166cb..0d3b755 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -280,7 +280,7 @@
     // screen mode. Some are specific to one type, but currently are called
     // directly from the proxy.
     public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebView webView) {
+            HTML5VideoViewProxy proxy, WebViewClassic webView) {
     }
 
     public boolean isFullScreenMode() {
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index d306c86..1644b06 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -75,8 +75,8 @@
     int mNativePointer;
     // The handler for WebCore thread messages;
     private Handler mWebCoreHandler;
-    // The WebView instance that created this view.
-    private WebView mWebView;
+    // The WebViewClassic instance that created this view.
+    private WebViewClassic mWebView;
     // The poster image to be shown when the video is not playing.
     // This ref prevents the bitmap from being GC'ed.
     private Bitmap mPoster;
@@ -142,7 +142,7 @@
         }
 
         public static void enterFullScreenVideo(int layerId, String url,
-                HTML5VideoViewProxy proxy, WebView webView) {
+                HTML5VideoViewProxy proxy, WebViewClassic webView) {
                 // Save the inline video info and inherit it in the full screen
                 int savePosition = 0;
                 if (mHTML5VideoView != null) {
@@ -163,7 +163,7 @@
         }
 
         public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
-                WebView webView) {
+                WebViewClassic webView) {
             if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
                 WebChromeClient client = webView.getWebChromeClient();
                 if (client != null) {
@@ -551,7 +551,7 @@
      * @param webView is the WebView that hosts the video.
      * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
      */
-    private HTML5VideoViewProxy(WebView webView, int nativePtr) {
+    private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) {
         // This handler is for the main (UI) thread.
         super(Looper.getMainLooper());
         // Save the WebView object.
@@ -721,7 +721,7 @@
         return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr);
     }
 
-    /* package */ WebView getWebView() {
+    /* package */ WebViewClassic getWebView() {
         return mWebView;
     }
 
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index b498435..e6eaa14 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -43,10 +43,10 @@
     private boolean mTimerPaused;
     private boolean mHasDeferredTimers;
 
-    // keep track of the main WebView attached to the current window so that we
+    // keep track of the main WebViewClassic attached to the current window so that we
     // can get the proper Context.
-    private static WeakReference<WebView> sCurrentMainWebView =
-            new WeakReference<WebView>(null);
+    private static WeakReference<WebViewClassic> sCurrentMainWebView =
+            new WeakReference<WebViewClassic>(null);
 
     /* package */
     static final int REFRESH_PLUGINS = 100;
@@ -67,15 +67,15 @@
         nativeFinalize();
     }
 
-    static synchronized void setActiveWebView(WebView webview) {
+    static synchronized void setActiveWebView(WebViewClassic webview) {
         if (sCurrentMainWebView.get() != null) {
             // it is possible if there is a sub-WebView. Do nothing.
             return;
         }
-        sCurrentMainWebView = new WeakReference<WebView>(webview);
+        sCurrentMainWebView = new WeakReference<WebViewClassic>(webview);
     }
 
-    static synchronized void removeActiveWebView(WebView webview) {
+    static synchronized void removeActiveWebView(WebViewClassic webview) {
         if (sCurrentMainWebView.get() != webview) {
             // it is possible if there is a sub-WebView. Do nothing.
             return;
@@ -259,7 +259,7 @@
 
     synchronized private String getSignedPublicKey(int index, String challenge,
             String url) {
-        WebView current = sCurrentMainWebView.get();
+        WebViewClassic current = sCurrentMainWebView.get();
         if (current != null) {
             // generateKeyPair expects organizations which we don't have. Ignore
             // url.
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
index e906f7f..d91f860 100644
--- a/core/java/android/webkit/OverScrollGlow.java
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -29,7 +29,7 @@
  * @hide
  */
 public class OverScrollGlow {
-    private WebView mHostView;
+    private WebViewClassic mHostView;
 
     private EdgeEffect mEdgeGlowTop;
     private EdgeEffect mEdgeGlowBottom;
@@ -39,7 +39,7 @@
     private int mOverScrollDeltaX;
     private int mOverScrollDeltaY;
 
-    public OverScrollGlow(WebView host) {
+    public OverScrollGlow(WebViewClassic host) {
         mHostView = host;
         Context context = host.getContext();
         mEdgeGlowTop = new EdgeEffect(context);
@@ -80,7 +80,7 @@
                 mOverScrollDeltaX = 0;
             }
 
-            if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+            if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
                 final int pulledToY = oldY + mOverScrollDeltaY;
                 if (pulledToY < 0) {
                     mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
@@ -120,7 +120,7 @@
      * @param rangeY Maximum range for vertical scrolling
      */
     public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) {
-        if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
+        if (rangeY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
             if (y < 0 && oldY >= 0) {
                 mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
                 if (!mEdgeGlowBottom.isFinished()) {
diff --git a/core/java/android/webkit/PluginFullScreenHolder.java b/core/java/android/webkit/PluginFullScreenHolder.java
index 42ba7c9..665cd9d 100644
--- a/core/java/android/webkit/PluginFullScreenHolder.java
+++ b/core/java/android/webkit/PluginFullScreenHolder.java
@@ -35,7 +35,7 @@
 
 class PluginFullScreenHolder {
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final int mNpp;
     private final int mOrientation;
 
@@ -44,7 +44,7 @@
 
     private View mContentView;
 
-    PluginFullScreenHolder(WebView webView, int orientation, int npp) {
+    PluginFullScreenHolder(WebViewClassic webView, int orientation, int npp) {
         mWebView = webView;
         mNpp = npp;
         mOrientation = orientation;
@@ -134,7 +134,7 @@
         new WebChromeClient.CustomViewCallback() {
             public void onCustomViewHidden() {
 
-                mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
+                mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
                     .sendToTarget();
 
                 mWebView.getWebViewCore().sendMessage(
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index ab3b6d5..fe40156 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -34,7 +34,7 @@
 import android.util.Log;
 
 /**
- * Class for managing the relationship between the {@link WebView} and installed
+ * Class for managing the relationship between the {@link WebViewClassic} and installed
  * plugins in the system. You can find this class through
  * {@link PluginManager#getInstance}.
  * 
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 2a770f5..57628d3 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -26,11 +26,11 @@
 import android.view.MenuItem;
 
 class SelectActionModeCallback implements ActionMode.Callback {
-    private WebView mWebView;
+    private WebViewClassic mWebView;
     private ActionMode mActionMode;
     private boolean mIsTextSelected = true;
 
-    void setWebView(WebView webView) {
+    void setWebView(WebViewClassic webView) {
         mWebView = webView;
     }
 
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 153c1c2..34065a1 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.util.DisplayMetrics;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -24,7 +25,7 @@
 import java.util.ArrayList;
 
 class ViewManager {
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
     private boolean mHidden;
     private boolean mReadyToDraw;
@@ -74,7 +75,7 @@
         }
 
         private void attachViewOnUIThread() {
-            mWebView.addView(mView);
+            mWebView.getWebView().addView(mView);
             mChildren.add(this);
             if (!mReadyToDraw) {
                 mView.setVisibility(View.GONE);
@@ -93,16 +94,15 @@
         }
 
         private void removeViewOnUIThread() {
-            mWebView.removeView(mView);
+            mWebView.getWebView().removeView(mView);
             mChildren.remove(this);
         }
     }
 
-    ViewManager(WebView w) {
+    ViewManager(WebViewClassic w) {
         mWebView = w;
-
-        int pixelArea = w.getResources().getDisplayMetrics().widthPixels *
-                        w.getResources().getDisplayMetrics().heightPixels;
+        DisplayMetrics metrics = w.getWebView().getResources().getDisplayMetrics();
+        int pixelArea = metrics.widthPixels * metrics.heightPixels;
         /* set the threshold to be 275% larger than the screen size. The
            percentage is simply an estimation and is not based on anything but
            basic trial-and-error tests run on multiple devices.
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
index 5f91ed3..a22fc26 100644
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ b/core/java/android/webkit/ViewStateSerializer.java
@@ -34,7 +34,7 @@
 
     static final int VERSION = 1;
 
-    static boolean serializeViewState(OutputStream stream, WebView web)
+    static boolean serializeViewState(OutputStream stream, WebViewClassic web)
             throws IOException {
         int baseLayer = web.getBaseLayer();
         if (baseLayer == 0) {
@@ -48,7 +48,7 @@
                 new byte[WORKING_STREAM_STORAGE]);
     }
 
-    static DrawData deserializeViewState(InputStream stream, WebView web)
+    static DrawData deserializeViewState(InputStream stream, WebViewClassic web)
             throws IOException {
         DataInputStream dis = new DataInputStream(stream);
         int version = dis.readInt();
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index c463b40..cddd7ab 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -16,16 +16,7 @@
 
 package android.webkit;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Handler;
 import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-
-import java.util.Locale;
 
 /**
  * Manages settings state for a WebView. When a WebView is first created, it
@@ -35,7 +26,18 @@
  * been destroyed, any method call on WebSettings will throw an
  * IllegalStateException.
  */
+// This is (effectively) an abstract base class; concrete WebViewProviders must
+// create a class derived from this, and return an instance of it in the
+// WebViewProvider.getWebSettingsProvider() method implementation.
 public class WebSettings {
+    // TODO: Remove MustOverrideException and make all methods throwing it abstract instead;
+    // needs API file update.
+    private static class MustOverrideException extends RuntimeException {
+        MustOverrideException() {
+            super("abstract function called: must be overriden!");
+        }
+    }
+
     /**
      * Enum for controlling the layout of html.
      * NORMAL means no rendering changes.
@@ -141,379 +143,12 @@
         OFF
     }
 
-    // TODO: Keep this up to date
-    private static final String PREVIOUS_VERSION = "4.0.3";
-
-    // WebView associated with this WebSettings.
-    private WebView mWebView;
-    // BrowserFrame used to access the native frame pointer.
-    private BrowserFrame mBrowserFrame;
-    // Flag to prevent multiple SYNC messages at one time.
-    private boolean mSyncPending = false;
-    // Custom handler that queues messages until the WebCore thread is active.
-    private final EventHandler mEventHandler;
-
-    // Private settings so we don't have to go into native code to
-    // retrieve the values. After setXXX, postSync() needs to be called.
-    //
-    // The default values need to match those in WebSettings.cpp
-    // If the defaults change, please also update the JavaDocs so developers
-    // know what they are.
-    private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
-    private Context         mContext;
-    private int             mTextSize = 100;
-    private String          mStandardFontFamily = "sans-serif";
-    private String          mFixedFontFamily = "monospace";
-    private String          mSansSerifFontFamily = "sans-serif";
-    private String          mSerifFontFamily = "serif";
-    private String          mCursiveFontFamily = "cursive";
-    private String          mFantasyFontFamily = "fantasy";
-    private String          mDefaultTextEncoding;
-    private String          mUserAgent;
-    private boolean         mUseDefaultUserAgent;
-    private String          mAcceptLanguage;
-    private int             mMinimumFontSize = 8;
-    private int             mMinimumLogicalFontSize = 8;
-    private int             mDefaultFontSize = 16;
-    private int             mDefaultFixedFontSize = 13;
-    private int             mPageCacheCapacity = 0;
-    private boolean         mLoadsImagesAutomatically = true;
-    private boolean         mBlockNetworkImage = false;
-    private boolean         mBlockNetworkLoads;
-    private boolean         mJavaScriptEnabled = false;
-    private boolean         mHardwareAccelSkia = false;
-    private boolean         mShowVisualIndicator = false;
-    private PluginState     mPluginState = PluginState.OFF;
-    private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
-    private boolean         mUseDoubleTree = false;
-    private boolean         mUseWideViewport = false;
-    private boolean         mSupportMultipleWindows = false;
-    private boolean         mShrinksStandaloneImagesToFit = false;
-    private long            mMaximumDecodedImageSize = 0; // 0 means default
-    private boolean         mPrivateBrowsingEnabled = false;
-    private boolean         mSyntheticLinksEnabled = true;
-    // HTML5 API flags
-    private boolean         mAppCacheEnabled = false;
-    private boolean         mDatabaseEnabled = false;
-    private boolean         mDomStorageEnabled = false;
-    private boolean         mWorkersEnabled = false;  // only affects V8.
-    private boolean         mGeolocationEnabled = true;
-    private boolean         mXSSAuditorEnabled = false;
-    // HTML5 configuration parameters
-    private long            mAppCacheMaxSize = Long.MAX_VALUE;
-    private String          mAppCachePath = null;
-    private String          mDatabasePath = "";
-    // The WebCore DatabaseTracker only allows the database path to be set
-    // once. Keep track of when the path has been set.
-    private boolean         mDatabasePathHasBeenSet = false;
-    private String          mGeolocationDatabasePath = "";
-    // Don't need to synchronize the get/set methods as they
-    // are basic types, also none of these values are used in
-    // native WebCore code.
-    private ZoomDensity     mDefaultZoom = ZoomDensity.MEDIUM;
-    private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
-    private int             mOverrideCacheMode = LOAD_DEFAULT;
-    private int             mDoubleTapZoom = 100;
-    private boolean         mSaveFormData = true;
-    private boolean         mAutoFillEnabled = false;
-    private boolean         mSavePassword = true;
-    private boolean         mLightTouchEnabled = false;
-    private boolean         mNeedInitialFocus = true;
-    private boolean         mNavDump = false;
-    private boolean         mSupportZoom = true;
-    private boolean         mBuiltInZoomControls = false;
-    private boolean         mDisplayZoomControls = true;
-    private boolean         mAllowFileAccess = true;
-    private boolean         mAllowContentAccess = true;
-    private boolean         mLoadWithOverviewMode = false;
-    private boolean         mEnableSmoothTransition = false;
-    private boolean         mForceUserScalable = false;
-
-    // AutoFill Profile data
     /**
-     * @hide for now, pending API council approval.
+     * Hidden constructor to prevent clients from creating a new settings
+     * instance or deriving the class.
+     * @hide
      */
-    public static class AutoFillProfile {
-        private int mUniqueId;
-        private String mFullName;
-        private String mEmailAddress;
-        private String mCompanyName;
-        private String mAddressLine1;
-        private String mAddressLine2;
-        private String mCity;
-        private String mState;
-        private String mZipCode;
-        private String mCountry;
-        private String mPhoneNumber;
-
-        public AutoFillProfile(int uniqueId, String fullName, String email,
-                String companyName, String addressLine1, String addressLine2,
-                String city, String state, String zipCode, String country,
-                String phoneNumber) {
-            mUniqueId = uniqueId;
-            mFullName = fullName;
-            mEmailAddress = email;
-            mCompanyName = companyName;
-            mAddressLine1 = addressLine1;
-            mAddressLine2 = addressLine2;
-            mCity = city;
-            mState = state;
-            mZipCode = zipCode;
-            mCountry = country;
-            mPhoneNumber = phoneNumber;
-        }
-
-        public int getUniqueId() { return mUniqueId; }
-        public String getFullName() { return mFullName; }
-        public String getEmailAddress() { return mEmailAddress; }
-        public String getCompanyName() { return mCompanyName; }
-        public String getAddressLine1() { return mAddressLine1; }
-        public String getAddressLine2() { return mAddressLine2; }
-        public String getCity() { return mCity; }
-        public String getState() { return mState; }
-        public String getZipCode() { return mZipCode; }
-        public String getCountry() { return mCountry; }
-        public String getPhoneNumber() { return mPhoneNumber; }
-    }
-
-
-    private AutoFillProfile mAutoFillProfile;
-
-    private boolean         mUseWebViewBackgroundForOverscroll = true;
-
-    // private WebSettings, not accessible by the host activity
-    static private int      mDoubleTapToastCount = 3;
-
-    private static final String PREF_FILE = "WebViewSettings";
-    private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count";
-
-    // Class to handle messages before WebCore is ready.
-    private class EventHandler {
-        // Message id for syncing
-        static final int SYNC = 0;
-        // Message id for setting priority
-        static final int PRIORITY = 1;
-        // Message id for writing double-tap toast count
-        static final int SET_DOUBLE_TAP_TOAST_COUNT = 2;
-        // Actual WebCore thread handler
-        private Handler mHandler;
-
-        private synchronized void createHandler() {
-            // as mRenderPriority can be set before thread is running, sync up
-            setRenderPriority();
-
-            // create a new handler
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                        case SYNC:
-                            synchronized (WebSettings.this) {
-                                if (mBrowserFrame.mNativeFrame != 0) {
-                                    nativeSync(mBrowserFrame.mNativeFrame);
-                                }
-                                mSyncPending = false;
-                            }
-                            break;
-
-                        case PRIORITY: {
-                            setRenderPriority();
-                            break;
-                        }
-
-                        case SET_DOUBLE_TAP_TOAST_COUNT: {
-                            SharedPreferences.Editor editor = mContext
-                                    .getSharedPreferences(PREF_FILE,
-                                            Context.MODE_PRIVATE).edit();
-                            editor.putInt(DOUBLE_TAP_TOAST_COUNT,
-                                    mDoubleTapToastCount);
-                            editor.commit();
-                            break;
-                        }
-                    }
-                }
-            };
-        }
-
-        private void setRenderPriority() {
-            synchronized (WebSettings.this) {
-                if (mRenderPriority == RenderPriority.NORMAL) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_DEFAULT);
-                } else if (mRenderPriority == RenderPriority.HIGH) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_FOREGROUND +
-                            android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
-                } else if (mRenderPriority == RenderPriority.LOW) {
-                    android.os.Process.setThreadPriority(
-                            android.os.Process.THREAD_PRIORITY_BACKGROUND);
-                }
-            }
-        }
-
-        /**
-         * Send a message to the private queue or handler.
-         */
-        private synchronized boolean sendMessage(Message msg) {
-            if (mHandler != null) {
-                mHandler.sendMessage(msg);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    }
-
-    // User agent strings.
-    private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
-        "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
-        "Chrome/11.0.696.34 Safari/534.24";
-    private static final String IPHONE_USERAGENT =
-            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
-            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
-            + " Mobile/7A341 Safari/528.16";
-    private static Locale sLocale;
-    private static Object sLockForLocaleSettings;
-
-    /**
-     * Package constructor to prevent clients from creating a new settings
-     * instance.
-     */
-    WebSettings(Context context, WebView webview) {
-        mEventHandler = new EventHandler();
-        mContext = context;
-        mWebView = webview;
-        mDefaultTextEncoding = context.getString(com.android.internal.
-                                                 R.string.default_text_encoding);
-
-        if (sLockForLocaleSettings == null) {
-            sLockForLocaleSettings = new Object();
-            sLocale = Locale.getDefault();
-        }
-        mAcceptLanguage = getCurrentAcceptLanguage();
-        mUserAgent = getCurrentUserAgent();
-        mUseDefaultUserAgent = true;
-
-        mBlockNetworkLoads = mContext.checkPermission(
-                "android.permission.INTERNET", android.os.Process.myPid(),
-                android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED;
-    }
-
-    private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
-
-    /**
-     * Looks at sLocale and returns current AcceptLanguage String.
-     * @return Current AcceptLanguage String.
-     */
-    private String getCurrentAcceptLanguage() {
-        Locale locale;
-        synchronized(sLockForLocaleSettings) {
-            locale = sLocale;
-        }
-        StringBuilder buffer = new StringBuilder();
-        addLocaleToHttpAcceptLanguage(buffer, locale);
-
-        if (!Locale.US.equals(locale)) {
-            if (buffer.length() > 0) {
-                buffer.append(", ");
-            }
-            buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
-        }
-
-        return buffer.toString();
-    }
-
-    /**
-     * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
-     * to new standard.
-     */
-    private static String convertObsoleteLanguageCodeToNew(String langCode) {
-        if (langCode == null) {
-            return null;
-        }
-        if ("iw".equals(langCode)) {
-            // Hebrew
-            return "he";
-        } else if ("in".equals(langCode)) {
-            // Indonesian
-            return "id";
-        } else if ("ji".equals(langCode)) {
-            // Yiddish
-            return "yi";
-        }
-        return langCode;
-    }
-
-    private static void addLocaleToHttpAcceptLanguage(StringBuilder builder,
-                                                      Locale locale) {
-        String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
-        if (language != null) {
-            builder.append(language);
-            String country = locale.getCountry();
-            if (country != null) {
-                builder.append("-");
-                builder.append(country);
-            }
-        }
-    }
-
-    /**
-     * Looks at sLocale and mContext and returns current UserAgent String.
-     * @return Current UserAgent String.
-     */
-    private synchronized String getCurrentUserAgent() {
-        Locale locale;
-        synchronized(sLockForLocaleSettings) {
-            locale = sLocale;
-        }
-        StringBuffer buffer = new StringBuffer();
-        // Add version
-        final String version = Build.VERSION.RELEASE;
-        if (version.length() > 0) {
-            if (Character.isDigit(version.charAt(0))) {
-                // Release is a version, eg "3.1"
-                buffer.append(version);
-            } else {
-                // Release is a codename, eg "Honeycomb"
-                // In this case, use the previous release's version
-                buffer.append(PREVIOUS_VERSION);
-            }
-        } else {
-            // default to "1.0"
-            buffer.append("1.0");
-        }
-        buffer.append("; ");
-        final String language = locale.getLanguage();
-        if (language != null) {
-            buffer.append(convertObsoleteLanguageCodeToNew(language));
-            final String country = locale.getCountry();
-            if (country != null) {
-                buffer.append("-");
-                buffer.append(country.toLowerCase());
-            }
-        } else {
-            // default to "en"
-            buffer.append("en");
-        }
-        buffer.append(";");
-        // add the model for the release build
-        if ("REL".equals(Build.VERSION.CODENAME)) {
-            final String model = Build.MODEL;
-            if (model.length() > 0) {
-                buffer.append(" ");
-                buffer.append(model);
-            }
-        }
-        final String id = Build.ID;
-        if (id.length() > 0) {
-            buffer.append(" Build/");
-            buffer.append(id);
-        }
-        String mobile = mContext.getResources().getText(
-            com.android.internal.R.string.web_user_agent_target_content).toString();
-        final String base = mContext.getResources().getText(
-                com.android.internal.R.string.web_user_agent).toString();
-        return String.format(base, buffer, mobile);
+    protected WebSettings() {
     }
 
     /**
@@ -522,7 +157,7 @@
      */
     @Deprecated
     public void setNavDump(boolean enabled) {
-        mNavDump = enabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -531,37 +166,35 @@
      */
     @Deprecated
     public boolean getNavDump() {
-        return mNavDump;
+        throw new MustOverrideException();
     }
 
     /**
      * Set whether the WebView supports zoom
      */
     public void setSupportZoom(boolean support) {
-        mSupportZoom = support;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns whether the WebView supports zoom
      */
     public boolean supportZoom() {
-        return mSupportZoom;
+        throw new MustOverrideException();
     }
 
     /**
      * Sets whether the zoom mechanism built into WebView is used.
      */
     public void setBuiltInZoomControls(boolean enabled) {
-        mBuiltInZoomControls = enabled;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if the zoom mechanism built into WebView is being used.
      */
     public boolean getBuiltInZoomControls() {
-        return mBuiltInZoomControls;
+        throw new MustOverrideException();
     }
 
     /**
@@ -571,15 +204,14 @@
      * to work without the on screen controls
      */
     public void setDisplayZoomControls(boolean enabled) {
-        mDisplayZoomControls = enabled;
-        mWebView.updateMultiTouchSupport(mContext);
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if the on screen zoom buttons are being used.
      */
     public boolean getDisplayZoomControls() {
-        return mDisplayZoomControls;
+        throw new MustOverrideException();
     }
 
     /**
@@ -589,14 +221,14 @@
      * file:///android_res.
      */
     public void setAllowFileAccess(boolean allow) {
-        mAllowFileAccess = allow;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView supports file access.
      */
     public boolean getAllowFileAccess() {
-        return mAllowFileAccess;
+        throw new MustOverrideException();
     }
 
     /**
@@ -605,28 +237,28 @@
      * system.  The default is enabled.
      */
     public void setAllowContentAccess(boolean allow) {
-        mAllowContentAccess = allow;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView supports content url access.
      */
     public boolean getAllowContentAccess() {
-        return mAllowContentAccess;
+        throw new MustOverrideException();
     }
 
     /**
      * Set whether the WebView loads a page with overview mode.
      */
     public void setLoadWithOverviewMode(boolean overview) {
-        mLoadWithOverviewMode = overview;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if this WebView loads page with overview mode
      */
     public boolean getLoadWithOverviewMode() {
-        return mLoadWithOverviewMode;
+        throw new MustOverrideException();
     }
 
     /**
@@ -637,15 +269,14 @@
      * If it is false, WebView will keep its fidelity. The default value is false.
      */
     public void setEnableSmoothTransition(boolean enable) {
-        mEnableSmoothTransition = enable;
+        throw new MustOverrideException();
     }
-
     /**
      * Returns true if the WebView enables smooth transition while panning or
      * zooming.
      */
     public boolean enableSmoothTransition() {
-        return mEnableSmoothTransition;
+        throw new MustOverrideException();
     }
 
     /**
@@ -656,7 +287,7 @@
      */
     @Deprecated
     public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
-        mUseWebViewBackgroundForOverscroll = view;
+        throw new MustOverrideException();
     }
 
     /**
@@ -666,14 +297,14 @@
      */
     @Deprecated
     public boolean getUseWebViewBackgroundForOverscrollBackground() {
-        return mUseWebViewBackgroundForOverscroll;
+        throw new MustOverrideException();
     }
 
     /**
      * Store whether the WebView is saving form data.
      */
     public void setSaveFormData(boolean save) {
-        mSaveFormData = save;
+        throw new MustOverrideException();
     }
 
     /**
@@ -681,21 +312,21 @@
      *  entries/autofill++.  Always false in private browsing mode.
      */
     public boolean getSaveFormData() {
-        return mSaveFormData && !mPrivateBrowsingEnabled;
+        throw new MustOverrideException();
     }
 
     /**
      *  Store whether the WebView is saving password.
      */
     public void setSavePassword(boolean save) {
-        mSavePassword = save;
+        throw new MustOverrideException();
     }
 
     /**
      *  Return whether the WebView is saving password.
      */
     public boolean getSavePassword() {
-        return mSavePassword;
+        throw new MustOverrideException();
     }
 
     /**
@@ -703,14 +334,7 @@
      * @param textZoom A percent value for increasing or decreasing the text.
      */
     public synchronized void setTextZoom(int textZoom) {
-        if (mTextSize != textZoom) {
-            if (WebView.mLogEvent) {
-                EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE,
-                        mTextSize, textZoom);
-            }
-            mTextSize = textZoom;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -719,7 +343,7 @@
      * @see setTextSizeZoom
      */
     public synchronized int getTextZoom() {
-        return mTextSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -729,7 +353,7 @@
      * @deprecated Use {@link #setTextZoom(int)} instead
      */
     public synchronized void setTextSize(TextSize t) {
-        setTextZoom(t.value);
+        throw new MustOverrideException();
     }
 
     /**
@@ -741,40 +365,7 @@
      * @deprecated Use {@link #getTextZoom()} instead
      */
     public synchronized TextSize getTextSize() {
-        TextSize closestSize = null;
-        int smallestDelta = Integer.MAX_VALUE;
-        for (TextSize size : TextSize.values()) {
-            int delta = Math.abs(mTextSize - size.value);
-            if (delta == 0) {
-                return size;
-            }
-            if (delta < smallestDelta) {
-                smallestDelta = delta;
-                closestSize = size;
-            }
-        }
-        return closestSize != null ? closestSize : TextSize.NORMAL;
-    }
-
-    /**
-     * Set the double-tap zoom of the page in percent. Default is 100.
-     * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
-     * @hide
-     */
-    public void setDoubleTapZoom(int doubleTapZoom) {
-        if (mDoubleTapZoom != doubleTapZoom) {
-            mDoubleTapZoom = doubleTapZoom;
-            mWebView.updateDoubleTapZoom(doubleTapZoom);
-        }
-    }
-
-    /**
-     * Get the double-tap zoom of the page in percent.
-     * @return A percent value describing the double-tap zoom.
-     * @hide
-     */
-    public int getDoubleTapZoom() {
-        return mDoubleTapZoom;
+        throw new MustOverrideException();
     }
 
     /**
@@ -784,10 +375,7 @@
      * @see WebSettings.ZoomDensity
      */
     public void setDefaultZoom(ZoomDensity zoom) {
-        if (mDefaultZoom != zoom) {
-            mDefaultZoom = zoom;
-            mWebView.adjustDefaultZoomDensity(zoom.value);
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -797,21 +385,21 @@
      * @see WebSettings.ZoomDensity
      */
     public ZoomDensity getDefaultZoom() {
-        return mDefaultZoom;
+        throw new MustOverrideException();
     }
 
     /**
      * Enables using light touches to make a selection and activate mouseovers.
      */
     public void setLightTouchEnabled(boolean enabled) {
-        mLightTouchEnabled = enabled;
+        throw new MustOverrideException();
     }
 
     /**
      * Returns true if light touches are enabled.
      */
     public boolean getLightTouchEnabled() {
-        return mLightTouchEnabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -820,7 +408,7 @@
      */
     @Deprecated
     public synchronized void setUseDoubleTree(boolean use) {
-        return;
+        // Specified to do nothing, so no need for derived classes to override.
     }
 
     /**
@@ -829,6 +417,7 @@
      */
     @Deprecated
     public synchronized boolean getUseDoubleTree() {
+        // Returns false unconditionally, so no need for derived classes to override.
         return false;
     }
 
@@ -841,23 +430,7 @@
      */
     @Deprecated
     public synchronized void setUserAgent(int ua) {
-        String uaString = null;
-        if (ua == 1) {
-            if (DESKTOP_USERAGENT.equals(mUserAgent)) {
-                return; // do nothing
-            } else {
-                uaString = DESKTOP_USERAGENT;
-            }
-        } else if (ua == 2) {
-            if (IPHONE_USERAGENT.equals(mUserAgent)) {
-                return; // do nothing
-            } else {
-                uaString = IPHONE_USERAGENT;
-            }
-        } else if (ua != 0) {
-            return; // do nothing
-        }
-        setUserAgentString(uaString);
+        throw new MustOverrideException();
     }
 
     /**
@@ -870,31 +443,21 @@
      */
     @Deprecated
     public synchronized int getUserAgent() {
-        if (DESKTOP_USERAGENT.equals(mUserAgent)) {
-            return 1;
-        } else if (IPHONE_USERAGENT.equals(mUserAgent)) {
-            return 2;
-        } else if (mUseDefaultUserAgent) {
-            return 0;
-        }
-        return -1;
+        throw new MustOverrideException();
     }
 
     /**
      * Tell the WebView to use the wide viewport
      */
     public synchronized void setUseWideViewPort(boolean use) {
-        if (mUseWideViewport != use) {
-            mUseWideViewport = use;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
      * @return True if the WebView is using a wide viewport
      */
     public synchronized boolean getUseWideViewPort() {
-        return mUseWideViewport;
+        throw new MustOverrideException();
     }
 
     /**
@@ -903,10 +466,7 @@
      *         boolean, Message)} is implemented by the host application.
      */
     public synchronized void setSupportMultipleWindows(boolean support) {
-        if (mSupportMultipleWindows != support) {
-            mSupportMultipleWindows = support;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -915,7 +475,7 @@
      *         boolean, Message)} is implemented by the host application.
      */
     public synchronized boolean supportMultipleWindows() {
-        return mSupportMultipleWindows;
+        throw new MustOverrideException();
     }
 
     /**
@@ -925,12 +485,7 @@
      * @see WebSettings.LayoutAlgorithm
      */
     public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
-        // XXX: This will only be affective if libwebcore was built with
-        // ANDROID_LAYOUT defined.
-        if (mLayoutAlgorithm != l) {
-            mLayoutAlgorithm = l;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -940,7 +495,7 @@
      * @see WebSettings.LayoutAlgorithm
      */
     public synchronized LayoutAlgorithm getLayoutAlgorithm() {
-        return mLayoutAlgorithm;
+        throw new MustOverrideException();
     }
 
     /**
@@ -948,10 +503,7 @@
      * @param font A font family name.
      */
     public synchronized void setStandardFontFamily(String font) {
-        if (font != null && !font.equals(mStandardFontFamily)) {
-            mStandardFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -959,7 +511,7 @@
      * @return The standard font family name as a string.
      */
     public synchronized String getStandardFontFamily() {
-        return mStandardFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -967,10 +519,7 @@
      * @param font A font family name.
      */
     public synchronized void setFixedFontFamily(String font) {
-        if (font != null && !font.equals(mFixedFontFamily)) {
-            mFixedFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -978,7 +527,7 @@
      * @return The fixed font family name as a string.
      */
     public synchronized String getFixedFontFamily() {
-        return mFixedFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -986,10 +535,7 @@
      * @param font A font family name.
      */
     public synchronized void setSansSerifFontFamily(String font) {
-        if (font != null && !font.equals(mSansSerifFontFamily)) {
-            mSansSerifFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -997,7 +543,7 @@
      * @return The sans-serif font family name as a string.
      */
     public synchronized String getSansSerifFontFamily() {
-        return mSansSerifFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1005,10 +551,7 @@
      * @param font A font family name.
      */
     public synchronized void setSerifFontFamily(String font) {
-        if (font != null && !font.equals(mSerifFontFamily)) {
-            mSerifFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1016,7 +559,7 @@
      * @return The serif font family name as a string.
      */
     public synchronized String getSerifFontFamily() {
-        return mSerifFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1024,10 +567,7 @@
      * @param font A font family name.
      */
     public synchronized void setCursiveFontFamily(String font) {
-        if (font != null && !font.equals(mCursiveFontFamily)) {
-            mCursiveFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1035,7 +575,7 @@
      * @return The cursive font family name as a string.
      */
     public synchronized String getCursiveFontFamily() {
-        return mCursiveFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1043,10 +583,7 @@
      * @param font A font family name.
      */
     public synchronized void setFantasyFontFamily(String font) {
-        if (font != null && !font.equals(mFantasyFontFamily)) {
-            mFantasyFontFamily = font;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1054,7 +591,7 @@
      * @return The fantasy font family name as a string.
      */
     public synchronized String getFantasyFontFamily() {
-        return mFantasyFontFamily;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1063,11 +600,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setMinimumFontSize(int size) {
-        size = pin(size);
-        if (mMinimumFontSize != size) {
-            mMinimumFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1075,7 +608,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumFontSize() {
-        return mMinimumFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1084,11 +617,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setMinimumLogicalFontSize(int size) {
-        size = pin(size);
-        if (mMinimumLogicalFontSize != size) {
-            mMinimumLogicalFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1096,7 +625,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getMinimumLogicalFontSize() {
-        return mMinimumLogicalFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1105,11 +634,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setDefaultFontSize(int size) {
-        size = pin(size);
-        if (mDefaultFontSize != size) {
-            mDefaultFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1117,7 +642,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFontSize() {
-        return mDefaultFontSize;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1126,11 +651,7 @@
      * Any number outside the specified range will be pinned.
      */
     public synchronized void setDefaultFixedFontSize(int size) {
-        size = pin(size);
-        if (mDefaultFixedFontSize != size) {
-            mDefaultFixedFontSize = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1138,21 +659,7 @@
      * @return A non-negative integer between 1 and 72.
      */
     public synchronized int getDefaultFixedFontSize() {
-        return mDefaultFixedFontSize;
-    }
-
-    /**
-     * Set the number of pages cached by the WebKit for the history navigation.
-     * @param size A non-negative integer between 0 (no cache) and 20 (max).
-     * @hide
-     */
-    public synchronized void setPageCacheCapacity(int size) {
-        if (size < 0) size = 0;
-        if (size > 20) size = 20;
-        if (mPageCacheCapacity != size) {
-            mPageCacheCapacity = size;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1165,10 +672,7 @@
      * @param flag Whether the WebView should load image resources.
      */
     public synchronized void setLoadsImagesAutomatically(boolean flag) {
-        if (mLoadsImagesAutomatically != flag) {
-            mLoadsImagesAutomatically = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1177,7 +681,7 @@
      * @return True if the WebView loads image resources.
      */
     public synchronized boolean getLoadsImagesAutomatically() {
-        return mLoadsImagesAutomatically;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1195,10 +699,7 @@
      * @see #setBlockNetworkLoads
      */
     public synchronized void setBlockNetworkImage(boolean flag) {
-        if (mBlockNetworkImage != flag) {
-            mBlockNetworkImage = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1207,7 +708,7 @@
      * @return True if the WebView does not load image resources from the network.
      */
     public synchronized boolean getBlockNetworkImage() {
-        return mBlockNetworkImage;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1226,11 +727,7 @@
      * @see android.webkit.WebView#reload
      */
     public synchronized void setBlockNetworkLoads(boolean flag) {
-        if (mBlockNetworkLoads != flag) {
-            mBlockNetworkLoads = flag;
-            verifyNetworkAccess();
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1241,20 +738,7 @@
      * @return True if the WebView does not load any resources from the network.
      */
     public synchronized boolean getBlockNetworkLoads() {
-        return mBlockNetworkLoads;
-    }
-
-
-    private void verifyNetworkAccess() {
-        if (!mBlockNetworkLoads) {
-            if (mContext.checkPermission("android.permission.INTERNET",
-                    android.os.Process.myPid(), android.os.Process.myUid()) !=
-                        PackageManager.PERMISSION_GRANTED) {
-                throw new SecurityException
-                        ("Permission denied - " +
-                                "application missing INTERNET permission");
-            }
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1262,50 +746,7 @@
      * @param flag True if the WebView should execute javascript.
      */
     public synchronized void setJavaScriptEnabled(boolean flag) {
-        if (mJavaScriptEnabled != flag) {
-            mJavaScriptEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * Tell the WebView to use Skia's hardware accelerated rendering path
-     * @param flag True if the WebView should use Skia's hw-accel path
-     * @hide
-     */
-    public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
-        if (mHardwareAccelSkia != flag) {
-            mHardwareAccelSkia = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is using hardware accelerated skia
-     * @hide
-     */
-    public synchronized boolean getHardwareAccelSkiaEnabled() {
-        return mHardwareAccelSkia;
-    }
-
-    /**
-     * Tell the WebView to show the visual indicator
-     * @param flag True if the WebView should show the visual indicator
-     * @hide
-     */
-    public synchronized void setShowVisualIndicator(boolean flag) {
-        if (mShowVisualIndicator != flag) {
-            mShowVisualIndicator = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is showing the visual indicator
-     * @hide
-     */
-    public synchronized boolean getShowVisualIndicator() {
-        return mShowVisualIndicator;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1316,7 +757,7 @@
      */
     @Deprecated
     public synchronized void setPluginsEnabled(boolean flag) {
-        setPluginState(flag ? PluginState.ON : PluginState.OFF);
+        throw new MustOverrideException();
     }
 
     /**
@@ -1327,10 +768,7 @@
      * @param state One of the PluginState values.
      */
     public synchronized void setPluginState(PluginState state) {
-        if (mPluginState != state) {
-            mPluginState = state;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1342,6 +780,7 @@
      */
     @Deprecated
     public synchronized void setPluginsPath(String pluginsPath) {
+        // Specified to do nothing, so no need for derived classes to override.
     }
 
     /**
@@ -1352,11 +791,7 @@
      *     be saved. May be the empty string but should never be null.
      */
     public synchronized void setDatabasePath(String databasePath) {
-        if (databasePath != null && !mDatabasePathHasBeenSet) {
-            mDatabasePath = databasePath;
-            mDatabasePathHasBeenSet = true;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1367,40 +802,26 @@
      *     should never be null.
      */
     public synchronized void setGeolocationDatabasePath(String databasePath) {
-        if (databasePath != null
-                && !databasePath.equals(mGeolocationDatabasePath)) {
-            mGeolocationDatabasePath = databasePath;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Enable or disable the Application Cache API.
-     * @param flag Whether to enable the Application Cache API.
+     * Tell the WebView to enable Application Caches API.
+     * @param flag True if the WebView should enable Application Caches.
      */
     public synchronized void setAppCacheEnabled(boolean flag) {
-        if (mAppCacheEnabled != flag) {
-            mAppCacheEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Set the path used by the Application Cache API to store files. This
-     * setting is applied to all WebViews in the application. In order for the
-     * Application Cache API to function, this method must be called with a
-     * path which exists and is writable by the application. This method may
-     * only be called once: repeated calls are ignored.
-     * @param path Path to the directory that should be used to store Application
-     * Cache files.
+     * Set a custom path to the Application Caches files. The client
+     * must ensure it exists before this call.
+     * @param appCachePath String path to the directory containing Application
+     * Caches files. The appCache path can be the empty string but should not
+     * be null. Passing null for this parameter will result in a no-op.
      */
-    public synchronized void setAppCachePath(String path) {
-        // We test for a valid path and for repeated setting on the native
-        // side, but we can avoid syncing in some simple cases. 
-        if (mAppCachePath == null && path != null && !path.isEmpty()) {
-            mAppCachePath = path;
-            postSync();
-        }
+    public synchronized void setAppCachePath(String appCachePath) {
+        throw new MustOverrideException();
     }
 
     /**
@@ -1408,10 +829,7 @@
      * @param appCacheMaxSize the maximum size in bytes.
      */
     public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
-        if (appCacheMaxSize != mAppCacheMaxSize) {
-            mAppCacheMaxSize = appCacheMaxSize;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1420,10 +838,7 @@
      *     API.
      */
     public synchronized void setDatabaseEnabled(boolean flag) {
-       if (mDatabaseEnabled != flag) {
-           mDatabaseEnabled = flag;
-           postSync();
-       }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1432,10 +847,7 @@
      *     API.
      */
     public synchronized void setDomStorageEnabled(boolean flag) {
-       if (mDomStorageEnabled != flag) {
-           mDomStorageEnabled = flag;
-           postSync();
-       }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1443,16 +855,15 @@
      * @return True if the DOM Storage API's are enabled.
      */
     public synchronized boolean getDomStorageEnabled() {
-       return mDomStorageEnabled;
+        throw new MustOverrideException();
     }
-
     /**
      * Return the path to where database storage API databases are saved for
      * the current WebView.
      * @return the String path to the database storage API databases.
      */
     public synchronized String getDatabasePath() {
-        return mDatabasePath;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1460,21 +871,7 @@
      * @return True if the database storage API is enabled.
      */
     public synchronized boolean getDatabaseEnabled() {
-        return mDatabaseEnabled;
-    }
-
-    /**
-     * Tell the WebView to enable WebWorkers API.
-     * @param flag True if the WebView should enable WebWorkers.
-     * Note that this flag only affects V8. JSC does not have
-     * an equivalent setting.
-     * @hide
-     */
-    public synchronized void setWorkersEnabled(boolean flag) {
-        if (mWorkersEnabled != flag) {
-            mWorkersEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1482,22 +879,7 @@
      * @param flag Whether Geolocation should be enabled.
      */
     public synchronized void setGeolocationEnabled(boolean flag) {
-        if (mGeolocationEnabled != flag) {
-            mGeolocationEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * Sets whether XSS Auditor is enabled.
-     * @param flag Whether XSS Auditor should be enabled.
-     * @hide Only used by LayoutTestController.
-     */
-    public synchronized void setXSSAuditorEnabled(boolean flag) {
-        if (mXSSAuditorEnabled != flag) {
-            mXSSAuditorEnabled = flag;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1505,7 +887,7 @@
      * @return True if javascript is enabled.
      */
     public synchronized boolean getJavaScriptEnabled() {
-        return mJavaScriptEnabled;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1515,7 +897,7 @@
      */
     @Deprecated
     public synchronized boolean getPluginsEnabled() {
-        return mPluginState == PluginState.ON;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1523,7 +905,7 @@
      * @return A value corresponding to the enum PluginState.
      */
     public synchronized PluginState getPluginState() {
-        return mPluginState;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1535,6 +917,7 @@
      */
     @Deprecated
     public synchronized String getPluginsPath() {
+        // Unconditionally returns empty string, so no need for derived classes to override.
         return "";
     }
 
@@ -1543,12 +926,8 @@
      * javascript function window.open().
      * @param flag True if javascript can open windows automatically.
      */
-    public synchronized void setJavaScriptCanOpenWindowsAutomatically(
-            boolean flag) {
-        if (mJavaScriptCanOpenWindowsAutomatically != flag) {
-            mJavaScriptCanOpenWindowsAutomatically = flag;
-            postSync();
-        }
+    public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
+        throw new MustOverrideException();
     }
 
     /**
@@ -1558,18 +937,14 @@
      *         window.open().
      */
     public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
-        return mJavaScriptCanOpenWindowsAutomatically;
+        throw new MustOverrideException();
     }
-
     /**
      * Set the default text encoding name to use when decoding html pages.
      * @param encoding The text encoding name.
      */
     public synchronized void setDefaultTextEncodingName(String encoding) {
-        if (encoding != null && !encoding.equals(mDefaultTextEncoding)) {
-            mDefaultTextEncoding = encoding;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1577,7 +952,7 @@
      * @return The default text encoding name as a string.
      */
     public synchronized String getDefaultTextEncodingName() {
-        return mDefaultTextEncoding;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1585,66 +960,14 @@
      * it will use the system default user-agent string.
      */
     public synchronized void setUserAgentString(String ua) {
-        if (ua == null || ua.length() == 0) {
-            synchronized(sLockForLocaleSettings) {
-                Locale currentLocale = Locale.getDefault();
-                if (!sLocale.equals(currentLocale)) {
-                    sLocale = currentLocale;
-                    mAcceptLanguage = getCurrentAcceptLanguage();
-                }
-            }
-            ua = getCurrentUserAgent();
-            mUseDefaultUserAgent = true;
-        } else  {
-            mUseDefaultUserAgent = false;
-        }
-
-        if (!ua.equals(mUserAgent)) {
-            mUserAgent = ua;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
      * Return the WebView's user-agent string.
      */
     public synchronized String getUserAgentString() {
-        if (DESKTOP_USERAGENT.equals(mUserAgent) ||
-                IPHONE_USERAGENT.equals(mUserAgent) ||
-                !mUseDefaultUserAgent) {
-            return mUserAgent;
-        }
-
-        boolean doPostSync = false;
-        synchronized(sLockForLocaleSettings) {
-            Locale currentLocale = Locale.getDefault();
-            if (!sLocale.equals(currentLocale)) {
-                sLocale = currentLocale;
-                mUserAgent = getCurrentUserAgent();
-                mAcceptLanguage = getCurrentAcceptLanguage();
-                doPostSync = true;
-            }
-        }
-        if (doPostSync) {
-            postSync();
-        }
-        return mUserAgent;
-    }
-
-    /* package api to grab the Accept Language string. */
-    /*package*/ synchronized String getAcceptLanguage() {
-        synchronized(sLockForLocaleSettings) {
-            Locale currentLocale = Locale.getDefault();
-            if (!sLocale.equals(currentLocale)) {
-                sLocale = currentLocale;
-                mAcceptLanguage = getCurrentAcceptLanguage();
-            }
-        }
-        return mAcceptLanguage;
-    }
-
-    /* package */ boolean isNarrowColumnLayout() {
-        return getLayoutAlgorithm() == LayoutAlgorithm.NARROW_COLUMNS;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1654,14 +977,7 @@
      * @param flag
      */
     public void setNeedInitialFocus(boolean flag) {
-        if (mNeedInitialFocus != flag) {
-            mNeedInitialFocus = flag;
-        }
-    }
-
-    /* Package api to get the choice whether it needs to set initial focus. */
-    /* package */ boolean getNeedInitialFocus() {
-        return mNeedInitialFocus;
+        throw new MustOverrideException();
     }
 
     /**
@@ -1671,11 +987,7 @@
      * @param priority RenderPriority, can be normal, high or low.
      */
     public synchronized void setRenderPriority(RenderPriority priority) {
-        if (mRenderPriority != priority) {
-            mRenderPriority = priority;
-            mEventHandler.sendMessage(Message.obtain(null,
-                    EventHandler.PRIORITY));
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1687,10 +999,7 @@
      * @param mode One of the LOAD_ values.
      */
     public void setCacheMode(int mode) {
-        if (mode != mOverrideCacheMode) {
-            mOverrideCacheMode = mode;
-            postSync();
-        }
+        throw new MustOverrideException();
     }
 
     /**
@@ -1698,204 +1007,6 @@
      * description, see the {@link #setCacheMode(int)} function.
      */
     public int getCacheMode() {
-        return mOverrideCacheMode;
+        throw new MustOverrideException();
     }
-
-    /**
-     * If set, webkit alternately shrinks and expands images viewed outside
-     * of an HTML page to fit the screen. This conflicts with attempts by
-     * the UI to zoom in and out of an image, so it is set false by default.
-     * @param shrink Set true to let webkit shrink the standalone image to fit.
-     * {@hide}
-     */
-    public void setShrinksStandaloneImagesToFit(boolean shrink) {
-        if (mShrinksStandaloneImagesToFit != shrink) {
-            mShrinksStandaloneImagesToFit = shrink;
-            postSync();
-        }
-     }
-
-    /**
-     * Specify the maximum decoded image size. The default is
-     * 2 megs for small memory devices and 8 megs for large memory devices.
-     * @param size The maximum decoded size, or zero to set to the default.
-     * @hide
-     */
-    public void setMaximumDecodedImageSize(long size) {
-        if (mMaximumDecodedImageSize != size) {
-            mMaximumDecodedImageSize = size;
-            postSync();
-        }
-    }
-
-    /**
-     * Returns whether to use fixed viewport.  Use fixed viewport
-     * whenever wide viewport is on.
-     */
-    /* package */ boolean getUseFixedViewport() {
-        return getUseWideViewPort();
-    }
-
-    /**
-     * Returns whether private browsing is enabled.
-     */
-    /* package */ boolean isPrivateBrowsingEnabled() {
-        return mPrivateBrowsingEnabled;
-    }
-
-    /**
-     * Sets whether private browsing is enabled.
-     * @param flag Whether private browsing should be enabled.
-     */
-    /* package */ synchronized void setPrivateBrowsingEnabled(boolean flag) {
-        if (mPrivateBrowsingEnabled != flag) {
-            mPrivateBrowsingEnabled = flag;
-
-            // AutoFill is dependant on private browsing being enabled so
-            // reset it to take account of the new value of mPrivateBrowsingEnabled.
-            setAutoFillEnabled(mAutoFillEnabled);
-
-            postSync();
-        }
-    }
-
-    /**
-     * Returns whether the viewport metatag can disable zooming
-     * @hide
-     */
-    public boolean forceUserScalable() {
-        return mForceUserScalable;
-    }
-
-    /**
-     * Sets whether viewport metatag can disable zooming.
-     * @param flag Whether or not to forceably enable user scalable.
-     * @hide
-     */
-    public synchronized void setForceUserScalable(boolean flag) {
-        mForceUserScalable = flag;
-    }
-
-    synchronized void setSyntheticLinksEnabled(boolean flag) {
-        if (mSyntheticLinksEnabled != flag) {
-            mSyntheticLinksEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized void setAutoFillEnabled(boolean enabled) {
-        // AutoFill is always disabled in private browsing mode.
-        boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
-        if (mAutoFillEnabled != autoFillEnabled) {
-            mAutoFillEnabled = autoFillEnabled;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized boolean getAutoFillEnabled() {
-        return mAutoFillEnabled;
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized void setAutoFillProfile(AutoFillProfile profile) {
-        if (mAutoFillProfile != profile) {
-            mAutoFillProfile = profile;
-            postSync();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized AutoFillProfile getAutoFillProfile() {
-        return mAutoFillProfile;
-    }
-
-    int getDoubleTapToastCount() {
-        return mDoubleTapToastCount;
-    }
-
-    void setDoubleTapToastCount(int count) {
-        if (mDoubleTapToastCount != count) {
-            mDoubleTapToastCount = count;
-            // write the settings in the non-UI thread
-            mEventHandler.sendMessage(Message.obtain(null,
-                    EventHandler.SET_DOUBLE_TAP_TOAST_COUNT));
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void setProperty(String key, String value) {
-        if (mWebView.nativeSetProperty(key, value)) {
-            mWebView.contentInvalidateAll();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public String getProperty(String key) {
-        return mWebView.nativeGetProperty(key);
-    }
-
-    /**
-     * Transfer messages from the queue to the new WebCoreThread. Called from
-     * WebCore thread.
-     */
-    /*package*/
-    synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
-        mBrowserFrame = frame;
-        if (DebugFlags.WEB_SETTINGS) {
-            junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
-        }
-
-        SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE,
-                Context.MODE_PRIVATE);
-        if (mDoubleTapToastCount > 0) {
-            mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT,
-                    mDoubleTapToastCount);
-        }
-        nativeSync(frame.mNativeFrame);
-        mSyncPending = false;
-        mEventHandler.createHandler();
-    }
-
-    /**
-     * Let the Settings object know that our owner is being destroyed.
-     */
-    /*package*/
-    synchronized void onDestroyed() {
-    }
-
-    private int pin(int size) {
-        // FIXME: 72 is just an arbitrary max text size value.
-        if (size < 1) {
-            return 1;
-        } else if (size > 72) {
-            return 72;
-        }
-        return size;
-    }
-
-    /* Post a SYNC message to handle syncing the native settings. */
-    private synchronized void postSync() {
-        // Only post if a sync is not pending
-        if (!mSyncPending) {
-            mSyncPending = mEventHandler.sendMessage(
-                    Message.obtain(null, EventHandler.SYNC));
-        }
-    }
-
-    // Synchronize the native and java settings.
-    private native void nativeSync(int nativeFrame);
 }
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
new file mode 100644
index 0000000..6850eea
--- /dev/null
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -0,0 +1,1702 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+
+import java.util.Locale;
+
+/**
+ * WebSettings implementation for the WebViewClassic implementation of WebView.
+ * @hide
+ */
+public class WebSettingsClassic extends WebSettings {
+    // TODO: Keep this up to date
+    private static final String PREVIOUS_VERSION = "4.0.3";
+
+    // WebView associated with this WebSettings.
+    private WebViewClassic mWebView;
+    // BrowserFrame used to access the native frame pointer.
+    private BrowserFrame mBrowserFrame;
+    // Flag to prevent multiple SYNC messages at one time.
+    private boolean mSyncPending = false;
+    // Custom handler that queues messages until the WebCore thread is active.
+    private final EventHandler mEventHandler;
+
+    // Private settings so we don't have to go into native code to
+    // retrieve the values. After setXXX, postSync() needs to be called.
+    //
+    // The default values need to match those in WebSettings.cpp
+    // If the defaults change, please also update the JavaDocs so developers
+    // know what they are.
+    private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
+    private Context         mContext;
+    private int             mTextSize = 100;
+    private String          mStandardFontFamily = "sans-serif";
+    private String          mFixedFontFamily = "monospace";
+    private String          mSansSerifFontFamily = "sans-serif";
+    private String          mSerifFontFamily = "serif";
+    private String          mCursiveFontFamily = "cursive";
+    private String          mFantasyFontFamily = "fantasy";
+    private String          mDefaultTextEncoding;
+    private String          mUserAgent;
+    private boolean         mUseDefaultUserAgent;
+    private String          mAcceptLanguage;
+    private int             mMinimumFontSize = 8;
+    private int             mMinimumLogicalFontSize = 8;
+    private int             mDefaultFontSize = 16;
+    private int             mDefaultFixedFontSize = 13;
+    private int             mPageCacheCapacity = 0;
+    private boolean         mLoadsImagesAutomatically = true;
+    private boolean         mBlockNetworkImage = false;
+    private boolean         mBlockNetworkLoads;
+    private boolean         mJavaScriptEnabled = false;
+    private boolean         mHardwareAccelSkia = false;
+    private boolean         mShowVisualIndicator = false;
+    private PluginState     mPluginState = PluginState.OFF;
+    private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
+    private boolean         mUseDoubleTree = false;
+    private boolean         mUseWideViewport = false;
+    private boolean         mSupportMultipleWindows = false;
+    private boolean         mShrinksStandaloneImagesToFit = false;
+    private long            mMaximumDecodedImageSize = 0; // 0 means default
+    private boolean         mPrivateBrowsingEnabled = false;
+    private boolean         mSyntheticLinksEnabled = true;
+    // HTML5 API flags
+    private boolean         mAppCacheEnabled = false;
+    private boolean         mDatabaseEnabled = false;
+    private boolean         mDomStorageEnabled = false;
+    private boolean         mWorkersEnabled = false;  // only affects V8.
+    private boolean         mGeolocationEnabled = true;
+    private boolean         mXSSAuditorEnabled = false;
+    // HTML5 configuration parameters
+    private long            mAppCacheMaxSize = Long.MAX_VALUE;
+    private String          mAppCachePath = null;
+    private String          mDatabasePath = "";
+    // The WebCore DatabaseTracker only allows the database path to be set
+    // once. Keep track of when the path has been set.
+    private boolean         mDatabasePathHasBeenSet = false;
+    private String          mGeolocationDatabasePath = "";
+    // Don't need to synchronize the get/set methods as they
+    // are basic types, also none of these values are used in
+    // native WebCore code.
+    private ZoomDensity     mDefaultZoom = ZoomDensity.MEDIUM;
+    private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
+    private int             mOverrideCacheMode = LOAD_DEFAULT;
+    private int             mDoubleTapZoom = 100;
+    private boolean         mSaveFormData = true;
+    private boolean         mAutoFillEnabled = false;
+    private boolean         mSavePassword = true;
+    private boolean         mLightTouchEnabled = false;
+    private boolean         mNeedInitialFocus = true;
+    private boolean         mNavDump = false;
+    private boolean         mSupportZoom = true;
+    private boolean         mBuiltInZoomControls = false;
+    private boolean         mDisplayZoomControls = true;
+    private boolean         mAllowFileAccess = true;
+    private boolean         mAllowContentAccess = true;
+    private boolean         mLoadWithOverviewMode = false;
+    private boolean         mEnableSmoothTransition = false;
+    private boolean         mForceUserScalable = false;
+
+    // AutoFill Profile data
+    /**
+     * @hide for now, pending API council approval.
+     */
+    public static class AutoFillProfile {
+        private int mUniqueId;
+        private String mFullName;
+        private String mEmailAddress;
+        private String mCompanyName;
+        private String mAddressLine1;
+        private String mAddressLine2;
+        private String mCity;
+        private String mState;
+        private String mZipCode;
+        private String mCountry;
+        private String mPhoneNumber;
+
+        public AutoFillProfile(int uniqueId, String fullName, String email,
+                String companyName, String addressLine1, String addressLine2,
+                String city, String state, String zipCode, String country,
+                String phoneNumber) {
+            mUniqueId = uniqueId;
+            mFullName = fullName;
+            mEmailAddress = email;
+            mCompanyName = companyName;
+            mAddressLine1 = addressLine1;
+            mAddressLine2 = addressLine2;
+            mCity = city;
+            mState = state;
+            mZipCode = zipCode;
+            mCountry = country;
+            mPhoneNumber = phoneNumber;
+        }
+
+        public int getUniqueId() { return mUniqueId; }
+        public String getFullName() { return mFullName; }
+        public String getEmailAddress() { return mEmailAddress; }
+        public String getCompanyName() { return mCompanyName; }
+        public String getAddressLine1() { return mAddressLine1; }
+        public String getAddressLine2() { return mAddressLine2; }
+        public String getCity() { return mCity; }
+        public String getState() { return mState; }
+        public String getZipCode() { return mZipCode; }
+        public String getCountry() { return mCountry; }
+        public String getPhoneNumber() { return mPhoneNumber; }
+    }
+
+
+    private AutoFillProfile mAutoFillProfile;
+
+    private boolean         mUseWebViewBackgroundForOverscroll = true;
+
+    // private WebSettings, not accessible by the host activity
+    static private int      mDoubleTapToastCount = 3;
+
+    private static final String PREF_FILE = "WebViewSettings";
+    private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count";
+
+    // Class to handle messages before WebCore is ready.
+    private class EventHandler {
+        // Message id for syncing
+        static final int SYNC = 0;
+        // Message id for setting priority
+        static final int PRIORITY = 1;
+        // Message id for writing double-tap toast count
+        static final int SET_DOUBLE_TAP_TOAST_COUNT = 2;
+        // Actual WebCore thread handler
+        private Handler mHandler;
+
+        private synchronized void createHandler() {
+            // as mRenderPriority can be set before thread is running, sync up
+            setRenderPriority();
+
+            // create a new handler
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case SYNC:
+                            synchronized (WebSettingsClassic.this) {
+                                if (mBrowserFrame.mNativeFrame != 0) {
+                                    nativeSync(mBrowserFrame.mNativeFrame);
+                                }
+                                mSyncPending = false;
+                            }
+                            break;
+
+                        case PRIORITY: {
+                            setRenderPriority();
+                            break;
+                        }
+
+                        case SET_DOUBLE_TAP_TOAST_COUNT: {
+                            SharedPreferences.Editor editor = mContext
+                                    .getSharedPreferences(PREF_FILE,
+                                            Context.MODE_PRIVATE).edit();
+                            editor.putInt(DOUBLE_TAP_TOAST_COUNT,
+                                    mDoubleTapToastCount);
+                            editor.commit();
+                            break;
+                        }
+                    }
+                }
+            };
+        }
+
+        private void setRenderPriority() {
+            synchronized (WebSettingsClassic.this) {
+                if (mRenderPriority == RenderPriority.NORMAL) {
+                    android.os.Process.setThreadPriority(
+                            android.os.Process.THREAD_PRIORITY_DEFAULT);
+                } else if (mRenderPriority == RenderPriority.HIGH) {
+                    android.os.Process.setThreadPriority(
+                            android.os.Process.THREAD_PRIORITY_FOREGROUND +
+                            android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+                } else if (mRenderPriority == RenderPriority.LOW) {
+                    android.os.Process.setThreadPriority(
+                            android.os.Process.THREAD_PRIORITY_BACKGROUND);
+                }
+            }
+        }
+
+        /**
+         * Send a message to the private queue or handler.
+         */
+        private synchronized boolean sendMessage(Message msg) {
+            if (mHandler != null) {
+                mHandler.sendMessage(msg);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    // User agent strings.
+    private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
+        "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
+        "Chrome/11.0.696.34 Safari/534.24";
+    private static final String IPHONE_USERAGENT =
+            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
+            + " Mobile/7A341 Safari/528.16";
+    private static Locale sLocale;
+    private static Object sLockForLocaleSettings;
+
+    /**
+     * Package constructor to prevent clients from creating a new settings
+     * instance.
+     */
+    WebSettingsClassic(Context context, WebViewClassic webview) {
+        mEventHandler = new EventHandler();
+        mContext = context;
+        mWebView = webview;
+        mDefaultTextEncoding = context.getString(com.android.internal.
+                                                 R.string.default_text_encoding);
+
+        if (sLockForLocaleSettings == null) {
+            sLockForLocaleSettings = new Object();
+            sLocale = Locale.getDefault();
+        }
+        mAcceptLanguage = getCurrentAcceptLanguage();
+        mUserAgent = getCurrentUserAgent();
+        mUseDefaultUserAgent = true;
+
+        mBlockNetworkLoads = mContext.checkPermission(
+                "android.permission.INTERNET", android.os.Process.myPid(),
+                android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
+
+    /**
+     * Looks at sLocale and returns current AcceptLanguage String.
+     * @return Current AcceptLanguage String.
+     */
+    private String getCurrentAcceptLanguage() {
+        Locale locale;
+        synchronized(sLockForLocaleSettings) {
+            locale = sLocale;
+        }
+        StringBuilder buffer = new StringBuilder();
+        addLocaleToHttpAcceptLanguage(buffer, locale);
+
+        if (!Locale.US.equals(locale)) {
+            if (buffer.length() > 0) {
+                buffer.append(", ");
+            }
+            buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
+     * to new standard.
+     */
+    private static String convertObsoleteLanguageCodeToNew(String langCode) {
+        if (langCode == null) {
+            return null;
+        }
+        if ("iw".equals(langCode)) {
+            // Hebrew
+            return "he";
+        } else if ("in".equals(langCode)) {
+            // Indonesian
+            return "id";
+        } else if ("ji".equals(langCode)) {
+            // Yiddish
+            return "yi";
+        }
+        return langCode;
+    }
+
+    private static void addLocaleToHttpAcceptLanguage(StringBuilder builder,
+                                                      Locale locale) {
+        String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
+        if (language != null) {
+            builder.append(language);
+            String country = locale.getCountry();
+            if (country != null) {
+                builder.append("-");
+                builder.append(country);
+            }
+        }
+    }
+
+    /**
+     * Looks at sLocale and mContext and returns current UserAgent String.
+     * @return Current UserAgent String.
+     */
+    private synchronized String getCurrentUserAgent() {
+        Locale locale;
+        synchronized(sLockForLocaleSettings) {
+            locale = sLocale;
+        }
+        StringBuffer buffer = new StringBuffer();
+        // Add version
+        final String version = Build.VERSION.RELEASE;
+        if (version.length() > 0) {
+            if (Character.isDigit(version.charAt(0))) {
+                // Release is a version, eg "3.1"
+                buffer.append(version);
+            } else {
+                // Release is a codename, eg "Honeycomb"
+                // In this case, use the previous release's version
+                buffer.append(PREVIOUS_VERSION);
+            }
+        } else {
+            // default to "1.0"
+            buffer.append("1.0");
+        }
+        buffer.append("; ");
+        final String language = locale.getLanguage();
+        if (language != null) {
+            buffer.append(convertObsoleteLanguageCodeToNew(language));
+            final String country = locale.getCountry();
+            if (country != null) {
+                buffer.append("-");
+                buffer.append(country.toLowerCase());
+            }
+        } else {
+            // default to "en"
+            buffer.append("en");
+        }
+        buffer.append(";");
+        // add the model for the release build
+        if ("REL".equals(Build.VERSION.CODENAME)) {
+            final String model = Build.MODEL;
+            if (model.length() > 0) {
+                buffer.append(" ");
+                buffer.append(model);
+            }
+        }
+        final String id = Build.ID;
+        if (id.length() > 0) {
+            buffer.append(" Build/");
+            buffer.append(id);
+        }
+        String mobile = mContext.getResources().getText(
+            com.android.internal.R.string.web_user_agent_target_content).toString();
+        final String base = mContext.getResources().getText(
+                com.android.internal.R.string.web_user_agent).toString();
+        return String.format(base, buffer, mobile);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setNavDump(boolean)
+     */
+    @Override
+    @Deprecated
+    public void setNavDump(boolean enabled) {
+        mNavDump = enabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getNavDump()
+     */
+    @Override
+    @Deprecated
+    public boolean getNavDump() {
+        return mNavDump;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSupportZoom(boolean)
+     */
+    @Override
+    public void setSupportZoom(boolean support) {
+        mSupportZoom = support;
+        mWebView.updateMultiTouchSupport(mContext);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#supportZoom()
+     */
+    @Override
+    public boolean supportZoom() {
+        return mSupportZoom;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setBuiltInZoomControls(boolean)
+     */
+    @Override
+    public void setBuiltInZoomControls(boolean enabled) {
+        mBuiltInZoomControls = enabled;
+        mWebView.updateMultiTouchSupport(mContext);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getBuiltInZoomControls()
+     */
+    @Override
+    public boolean getBuiltInZoomControls() {
+        return mBuiltInZoomControls;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDisplayZoomControls(boolean)
+     */
+    @Override
+    public void setDisplayZoomControls(boolean enabled) {
+        mDisplayZoomControls = enabled;
+        mWebView.updateMultiTouchSupport(mContext);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDisplayZoomControls()
+     */
+    @Override
+    public boolean getDisplayZoomControls() {
+        return mDisplayZoomControls;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setAllowFileAccess(boolean)
+     */
+    @Override
+    public void setAllowFileAccess(boolean allow) {
+        mAllowFileAccess = allow;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getAllowFileAccess()
+     */
+    @Override
+    public boolean getAllowFileAccess() {
+        return mAllowFileAccess;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setAllowContentAccess(boolean)
+     */
+    @Override
+    public void setAllowContentAccess(boolean allow) {
+        mAllowContentAccess = allow;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getAllowContentAccess()
+     */
+    @Override
+    public boolean getAllowContentAccess() {
+        return mAllowContentAccess;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setLoadWithOverviewMode(boolean)
+     */
+    @Override
+    public void setLoadWithOverviewMode(boolean overview) {
+        mLoadWithOverviewMode = overview;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getLoadWithOverviewMode()
+     */
+    @Override
+    public boolean getLoadWithOverviewMode() {
+        return mLoadWithOverviewMode;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setEnableSmoothTransition(boolean)
+     */
+    @Override
+    public void setEnableSmoothTransition(boolean enable) {
+        mEnableSmoothTransition = enable;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#enableSmoothTransition()
+     */
+    @Override
+    public boolean enableSmoothTransition() {
+        return mEnableSmoothTransition;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setUseWebViewBackgroundForOverscrollBackground(boolean)
+     */
+    @Override
+    @Deprecated
+    public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+        mUseWebViewBackgroundForOverscroll = view;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getUseWebViewBackgroundForOverscrollBackground()
+     */
+    @Override
+    @Deprecated
+    public boolean getUseWebViewBackgroundForOverscrollBackground() {
+        return mUseWebViewBackgroundForOverscroll;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSaveFormData(boolean)
+     */
+    @Override
+    public void setSaveFormData(boolean save) {
+        mSaveFormData = save;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getSaveFormData()
+     */
+    @Override
+    public boolean getSaveFormData() {
+        return mSaveFormData && !mPrivateBrowsingEnabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSavePassword(boolean)
+     */
+    @Override
+    public void setSavePassword(boolean save) {
+        mSavePassword = save;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getSavePassword()
+     */
+    @Override
+    public boolean getSavePassword() {
+        return mSavePassword;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setTextZoom(int)
+     */
+    @Override
+    public synchronized void setTextZoom(int textZoom) {
+        if (mTextSize != textZoom) {
+            if (WebViewClassic.mLogEvent) {
+                EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE,
+                        mTextSize, textZoom);
+            }
+            mTextSize = textZoom;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getTextZoom()
+     */
+    @Override
+    public synchronized int getTextZoom() {
+        return mTextSize;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setTextSize(android.webkit.WebSettingsClassic.TextSize)
+     */
+    @Override
+    public synchronized void setTextSize(TextSize t) {
+        setTextZoom(t.value);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getTextSize()
+     */
+    @Override
+    public synchronized TextSize getTextSize() {
+        TextSize closestSize = null;
+        int smallestDelta = Integer.MAX_VALUE;
+        for (TextSize size : TextSize.values()) {
+            int delta = Math.abs(mTextSize - size.value);
+            if (delta == 0) {
+                return size;
+            }
+            if (delta < smallestDelta) {
+                smallestDelta = delta;
+                closestSize = size;
+            }
+        }
+        return closestSize != null ? closestSize : TextSize.NORMAL;
+    }
+
+    /**
+     * Set the double-tap zoom of the page in percent. Default is 100.
+     * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
+     * @hide
+     */
+    public void setDoubleTapZoom(int doubleTapZoom) {
+        if (mDoubleTapZoom != doubleTapZoom) {
+            mDoubleTapZoom = doubleTapZoom;
+            mWebView.updateDoubleTapZoom(doubleTapZoom);
+        }
+    }
+
+    /**
+     * Get the double-tap zoom of the page in percent.
+     * @return A percent value describing the double-tap zoom.
+     * @hide
+     */
+    public int getDoubleTapZoom() {
+        return mDoubleTapZoom;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDefaultZoom(android.webkit.WebSettingsClassic.ZoomDensity)
+     */
+    @Override
+    public void setDefaultZoom(ZoomDensity zoom) {
+        if (mDefaultZoom != zoom) {
+            mDefaultZoom = zoom;
+            mWebView.adjustDefaultZoomDensity(zoom.value);
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDefaultZoom()
+     */
+    @Override
+    public ZoomDensity getDefaultZoom() {
+        return mDefaultZoom;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setLightTouchEnabled(boolean)
+     */
+    @Override
+    public void setLightTouchEnabled(boolean enabled) {
+        mLightTouchEnabled = enabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getLightTouchEnabled()
+     */
+    @Override
+    public boolean getLightTouchEnabled() {
+        return mLightTouchEnabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setUseDoubleTree(boolean)
+     */
+    @Override
+    @Deprecated
+    public synchronized void setUseDoubleTree(boolean use) {
+        return;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getUseDoubleTree()
+     */
+    @Override
+    @Deprecated
+    public synchronized boolean getUseDoubleTree() {
+        return false;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setUserAgent(int)
+     */
+    @Override
+    @Deprecated
+    public synchronized void setUserAgent(int ua) {
+        String uaString = null;
+        if (ua == 1) {
+            if (DESKTOP_USERAGENT.equals(mUserAgent)) {
+                return; // do nothing
+            } else {
+                uaString = DESKTOP_USERAGENT;
+            }
+        } else if (ua == 2) {
+            if (IPHONE_USERAGENT.equals(mUserAgent)) {
+                return; // do nothing
+            } else {
+                uaString = IPHONE_USERAGENT;
+            }
+        } else if (ua != 0) {
+            return; // do nothing
+        }
+        setUserAgentString(uaString);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getUserAgent()
+     */
+    @Override
+    @Deprecated
+    public synchronized int getUserAgent() {
+        if (DESKTOP_USERAGENT.equals(mUserAgent)) {
+            return 1;
+        } else if (IPHONE_USERAGENT.equals(mUserAgent)) {
+            return 2;
+        } else if (mUseDefaultUserAgent) {
+            return 0;
+        }
+        return -1;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setUseWideViewPort(boolean)
+     */
+    @Override
+    public synchronized void setUseWideViewPort(boolean use) {
+        if (mUseWideViewport != use) {
+            mUseWideViewport = use;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getUseWideViewPort()
+     */
+    @Override
+    public synchronized boolean getUseWideViewPort() {
+        return mUseWideViewport;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSupportMultipleWindows(boolean)
+     */
+    @Override
+    public synchronized void setSupportMultipleWindows(boolean support) {
+        if (mSupportMultipleWindows != support) {
+            mSupportMultipleWindows = support;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#supportMultipleWindows()
+     */
+    @Override
+    public synchronized boolean supportMultipleWindows() {
+        return mSupportMultipleWindows;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setLayoutAlgorithm(android.webkit.WebSettingsClassic.LayoutAlgorithm)
+     */
+    @Override
+    public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
+        // XXX: This will only be affective if libwebcore was built with
+        // ANDROID_LAYOUT defined.
+        if (mLayoutAlgorithm != l) {
+            mLayoutAlgorithm = l;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getLayoutAlgorithm()
+     */
+    @Override
+    public synchronized LayoutAlgorithm getLayoutAlgorithm() {
+        return mLayoutAlgorithm;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setStandardFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setStandardFontFamily(String font) {
+        if (font != null && !font.equals(mStandardFontFamily)) {
+            mStandardFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getStandardFontFamily()
+     */
+    @Override
+    public synchronized String getStandardFontFamily() {
+        return mStandardFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setFixedFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setFixedFontFamily(String font) {
+        if (font != null && !font.equals(mFixedFontFamily)) {
+            mFixedFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getFixedFontFamily()
+     */
+    @Override
+    public synchronized String getFixedFontFamily() {
+        return mFixedFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSansSerifFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setSansSerifFontFamily(String font) {
+        if (font != null && !font.equals(mSansSerifFontFamily)) {
+            mSansSerifFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getSansSerifFontFamily()
+     */
+    @Override
+    public synchronized String getSansSerifFontFamily() {
+        return mSansSerifFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setSerifFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setSerifFontFamily(String font) {
+        if (font != null && !font.equals(mSerifFontFamily)) {
+            mSerifFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getSerifFontFamily()
+     */
+    @Override
+    public synchronized String getSerifFontFamily() {
+        return mSerifFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setCursiveFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setCursiveFontFamily(String font) {
+        if (font != null && !font.equals(mCursiveFontFamily)) {
+            mCursiveFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getCursiveFontFamily()
+     */
+    @Override
+    public synchronized String getCursiveFontFamily() {
+        return mCursiveFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setFantasyFontFamily(java.lang.String)
+     */
+    @Override
+    public synchronized void setFantasyFontFamily(String font) {
+        if (font != null && !font.equals(mFantasyFontFamily)) {
+            mFantasyFontFamily = font;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getFantasyFontFamily()
+     */
+    @Override
+    public synchronized String getFantasyFontFamily() {
+        return mFantasyFontFamily;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setMinimumFontSize(int)
+     */
+    @Override
+    public synchronized void setMinimumFontSize(int size) {
+        size = pin(size);
+        if (mMinimumFontSize != size) {
+            mMinimumFontSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getMinimumFontSize()
+     */
+    @Override
+    public synchronized int getMinimumFontSize() {
+        return mMinimumFontSize;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setMinimumLogicalFontSize(int)
+     */
+    @Override
+    public synchronized void setMinimumLogicalFontSize(int size) {
+        size = pin(size);
+        if (mMinimumLogicalFontSize != size) {
+            mMinimumLogicalFontSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getMinimumLogicalFontSize()
+     */
+    @Override
+    public synchronized int getMinimumLogicalFontSize() {
+        return mMinimumLogicalFontSize;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDefaultFontSize(int)
+     */
+    @Override
+    public synchronized void setDefaultFontSize(int size) {
+        size = pin(size);
+        if (mDefaultFontSize != size) {
+            mDefaultFontSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDefaultFontSize()
+     */
+    @Override
+    public synchronized int getDefaultFontSize() {
+        return mDefaultFontSize;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDefaultFixedFontSize(int)
+     */
+    @Override
+    public synchronized void setDefaultFixedFontSize(int size) {
+        size = pin(size);
+        if (mDefaultFixedFontSize != size) {
+            mDefaultFixedFontSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDefaultFixedFontSize()
+     */
+    @Override
+    public synchronized int getDefaultFixedFontSize() {
+        return mDefaultFixedFontSize;
+    }
+
+    /**
+     * Set the number of pages cached by the WebKit for the history navigation.
+     * @param size A non-negative integer between 0 (no cache) and 20 (max).
+     * @hide
+     */
+    public synchronized void setPageCacheCapacity(int size) {
+        if (size < 0) size = 0;
+        if (size > 20) size = 20;
+        if (mPageCacheCapacity != size) {
+            mPageCacheCapacity = size;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setLoadsImagesAutomatically(boolean)
+     */
+    @Override
+    public synchronized void setLoadsImagesAutomatically(boolean flag) {
+        if (mLoadsImagesAutomatically != flag) {
+            mLoadsImagesAutomatically = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getLoadsImagesAutomatically()
+     */
+    @Override
+    public synchronized boolean getLoadsImagesAutomatically() {
+        return mLoadsImagesAutomatically;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setBlockNetworkImage(boolean)
+     */
+    @Override
+    public synchronized void setBlockNetworkImage(boolean flag) {
+        if (mBlockNetworkImage != flag) {
+            mBlockNetworkImage = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getBlockNetworkImage()
+     */
+    @Override
+    public synchronized boolean getBlockNetworkImage() {
+        return mBlockNetworkImage;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setBlockNetworkLoads(boolean)
+     */
+    @Override
+    public synchronized void setBlockNetworkLoads(boolean flag) {
+        if (mBlockNetworkLoads != flag) {
+            mBlockNetworkLoads = flag;
+            verifyNetworkAccess();
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getBlockNetworkLoads()
+     */
+    @Override
+    public synchronized boolean getBlockNetworkLoads() {
+        return mBlockNetworkLoads;
+    }
+
+
+    private void verifyNetworkAccess() {
+        if (!mBlockNetworkLoads) {
+            if (mContext.checkPermission("android.permission.INTERNET",
+                    android.os.Process.myPid(), android.os.Process.myUid()) !=
+                        PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException
+                        ("Permission denied - " +
+                                "application missing INTERNET permission");
+            }
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setJavaScriptEnabled(boolean)
+     */
+    @Override
+    public synchronized void setJavaScriptEnabled(boolean flag) {
+        if (mJavaScriptEnabled != flag) {
+            mJavaScriptEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * Tell the WebView to use Skia's hardware accelerated rendering path
+     * @param flag True if the WebView should use Skia's hw-accel path
+     * @hide
+     */
+    public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
+        if (mHardwareAccelSkia != flag) {
+            mHardwareAccelSkia = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @return True if the WebView is using hardware accelerated skia
+     * @hide
+     */
+    public synchronized boolean getHardwareAccelSkiaEnabled() {
+        return mHardwareAccelSkia;
+    }
+
+    /**
+     * Tell the WebView to show the visual indicator
+     * @param flag True if the WebView should show the visual indicator
+     * @hide
+     */
+    public synchronized void setShowVisualIndicator(boolean flag) {
+        if (mShowVisualIndicator != flag) {
+            mShowVisualIndicator = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @return True if the WebView is showing the visual indicator
+     * @hide
+     */
+    public synchronized boolean getShowVisualIndicator() {
+        return mShowVisualIndicator;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setPluginsEnabled(boolean)
+     */
+    @Override
+    @Deprecated
+    public synchronized void setPluginsEnabled(boolean flag) {
+        setPluginState(flag ? PluginState.ON : PluginState.OFF);
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setPluginState(android.webkit.WebSettingsClassic.PluginState)
+     */
+    @Override
+    public synchronized void setPluginState(PluginState state) {
+        if (mPluginState != state) {
+            mPluginState = state;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setPluginsPath(java.lang.String)
+     */
+    @Override
+    @Deprecated
+    public synchronized void setPluginsPath(String pluginsPath) {
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDatabasePath(java.lang.String)
+     */
+    @Override
+    public synchronized void setDatabasePath(String databasePath) {
+        if (databasePath != null && !mDatabasePathHasBeenSet) {
+            mDatabasePath = databasePath;
+            mDatabasePathHasBeenSet = true;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setGeolocationDatabasePath(java.lang.String)
+     */
+    @Override
+    public synchronized void setGeolocationDatabasePath(String databasePath) {
+        if (databasePath != null
+                && !databasePath.equals(mGeolocationDatabasePath)) {
+            mGeolocationDatabasePath = databasePath;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setAppCacheEnabled(boolean)
+     */
+    @Override
+    public synchronized void setAppCacheEnabled(boolean flag) {
+        if (mAppCacheEnabled != flag) {
+            mAppCacheEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setAppCachePath(java.lang.String)
+     */
+    @Override
+    public synchronized void setAppCachePath(String path) {
+        // We test for a valid path and for repeated setting on the native
+        // side, but we can avoid syncing in some simple cases. 
+        if (mAppCachePath == null && path != null && !path.isEmpty()) {
+            mAppCachePath = path;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setAppCacheMaxSize(long)
+     */
+    @Override
+    public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
+        if (appCacheMaxSize != mAppCacheMaxSize) {
+            mAppCacheMaxSize = appCacheMaxSize;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDatabaseEnabled(boolean)
+     */
+    @Override
+    public synchronized void setDatabaseEnabled(boolean flag) {
+       if (mDatabaseEnabled != flag) {
+           mDatabaseEnabled = flag;
+           postSync();
+       }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDomStorageEnabled(boolean)
+     */
+    @Override
+    public synchronized void setDomStorageEnabled(boolean flag) {
+       if (mDomStorageEnabled != flag) {
+           mDomStorageEnabled = flag;
+           postSync();
+       }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDomStorageEnabled()
+     */
+    @Override
+    public synchronized boolean getDomStorageEnabled() {
+       return mDomStorageEnabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDatabasePath()
+     */
+    @Override
+    public synchronized String getDatabasePath() {
+        return mDatabasePath;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDatabaseEnabled()
+     */
+    @Override
+    public synchronized boolean getDatabaseEnabled() {
+        return mDatabaseEnabled;
+    }
+
+    /**
+     * Tell the WebView to enable WebWorkers API.
+     * @param flag True if the WebView should enable WebWorkers.
+     * Note that this flag only affects V8. JSC does not have
+     * an equivalent setting.
+     * @hide
+     */
+    public synchronized void setWorkersEnabled(boolean flag) {
+        if (mWorkersEnabled != flag) {
+            mWorkersEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setGeolocationEnabled(boolean)
+     */
+    @Override
+    public synchronized void setGeolocationEnabled(boolean flag) {
+        if (mGeolocationEnabled != flag) {
+            mGeolocationEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * Sets whether XSS Auditor is enabled.
+     * @param flag Whether XSS Auditor should be enabled.
+     * @hide Only used by LayoutTestController.
+     */
+    public synchronized void setXSSAuditorEnabled(boolean flag) {
+        if (mXSSAuditorEnabled != flag) {
+            mXSSAuditorEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getJavaScriptEnabled()
+     */
+    @Override
+    public synchronized boolean getJavaScriptEnabled() {
+        return mJavaScriptEnabled;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getPluginsEnabled()
+     */
+    @Override
+    @Deprecated
+    public synchronized boolean getPluginsEnabled() {
+        return mPluginState == PluginState.ON;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getPluginState()
+     */
+    @Override
+    public synchronized PluginState getPluginState() {
+        return mPluginState;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getPluginsPath()
+     */
+    @Override
+    @Deprecated
+    public synchronized String getPluginsPath() {
+        return "";
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setJavaScriptCanOpenWindowsAutomatically(boolean)
+     */
+    @Override
+    public synchronized void setJavaScriptCanOpenWindowsAutomatically(
+            boolean flag) {
+        if (mJavaScriptCanOpenWindowsAutomatically != flag) {
+            mJavaScriptCanOpenWindowsAutomatically = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getJavaScriptCanOpenWindowsAutomatically()
+     */
+    @Override
+    public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
+        return mJavaScriptCanOpenWindowsAutomatically;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setDefaultTextEncodingName(java.lang.String)
+     */
+    @Override
+    public synchronized void setDefaultTextEncodingName(String encoding) {
+        if (encoding != null && !encoding.equals(mDefaultTextEncoding)) {
+            mDefaultTextEncoding = encoding;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getDefaultTextEncodingName()
+     */
+    @Override
+    public synchronized String getDefaultTextEncodingName() {
+        return mDefaultTextEncoding;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setUserAgentString(java.lang.String)
+     */
+    @Override
+    public synchronized void setUserAgentString(String ua) {
+        if (ua == null || ua.length() == 0) {
+            synchronized(sLockForLocaleSettings) {
+                Locale currentLocale = Locale.getDefault();
+                if (!sLocale.equals(currentLocale)) {
+                    sLocale = currentLocale;
+                    mAcceptLanguage = getCurrentAcceptLanguage();
+                }
+            }
+            ua = getCurrentUserAgent();
+            mUseDefaultUserAgent = true;
+        } else  {
+            mUseDefaultUserAgent = false;
+        }
+
+        if (!ua.equals(mUserAgent)) {
+            mUserAgent = ua;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getUserAgentString()
+     */
+    @Override
+    public synchronized String getUserAgentString() {
+        if (DESKTOP_USERAGENT.equals(mUserAgent) ||
+                IPHONE_USERAGENT.equals(mUserAgent) ||
+                !mUseDefaultUserAgent) {
+            return mUserAgent;
+        }
+
+        boolean doPostSync = false;
+        synchronized(sLockForLocaleSettings) {
+            Locale currentLocale = Locale.getDefault();
+            if (!sLocale.equals(currentLocale)) {
+                sLocale = currentLocale;
+                mUserAgent = getCurrentUserAgent();
+                mAcceptLanguage = getCurrentAcceptLanguage();
+                doPostSync = true;
+            }
+        }
+        if (doPostSync) {
+            postSync();
+        }
+        return mUserAgent;
+    }
+
+    /* package api to grab the Accept Language string. */
+    /*package*/ synchronized String getAcceptLanguage() {
+        synchronized(sLockForLocaleSettings) {
+            Locale currentLocale = Locale.getDefault();
+            if (!sLocale.equals(currentLocale)) {
+                sLocale = currentLocale;
+                mAcceptLanguage = getCurrentAcceptLanguage();
+            }
+        }
+        return mAcceptLanguage;
+    }
+
+    /* package */ boolean isNarrowColumnLayout() {
+        return getLayoutAlgorithm() == LayoutAlgorithm.NARROW_COLUMNS;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setNeedInitialFocus(boolean)
+     */
+    @Override
+    public void setNeedInitialFocus(boolean flag) {
+        if (mNeedInitialFocus != flag) {
+            mNeedInitialFocus = flag;
+        }
+    }
+
+    /* Package api to get the choice whether it needs to set initial focus. */
+    /* package */ boolean getNeedInitialFocus() {
+        return mNeedInitialFocus;
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setRenderPriority(android.webkit.WebSettingsClassic.RenderPriority)
+     */
+    @Override
+    public synchronized void setRenderPriority(RenderPriority priority) {
+        if (mRenderPriority != priority) {
+            mRenderPriority = priority;
+            mEventHandler.sendMessage(Message.obtain(null,
+                    EventHandler.PRIORITY));
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#setCacheMode(int)
+     */
+    @Override
+    public void setCacheMode(int mode) {
+        if (mode != mOverrideCacheMode) {
+            mOverrideCacheMode = mode;
+            postSync();
+        }
+    }
+
+    /**
+     * @see android.webkit.WebSettings#getCacheMode()
+     */
+    @Override
+    public int getCacheMode() {
+        return mOverrideCacheMode;
+    }
+
+    /**
+     * If set, webkit alternately shrinks and expands images viewed outside
+     * of an HTML page to fit the screen. This conflicts with attempts by
+     * the UI to zoom in and out of an image, so it is set false by default.
+     * @param shrink Set true to let webkit shrink the standalone image to fit.
+     * {@hide}
+     */
+    public void setShrinksStandaloneImagesToFit(boolean shrink) {
+        if (mShrinksStandaloneImagesToFit != shrink) {
+            mShrinksStandaloneImagesToFit = shrink;
+            postSync();
+        }
+     }
+
+    /**
+     * Specify the maximum decoded image size. The default is
+     * 2 megs for small memory devices and 8 megs for large memory devices.
+     * @param size The maximum decoded size, or zero to set to the default.
+     * @hide
+     */
+    public void setMaximumDecodedImageSize(long size) {
+        if (mMaximumDecodedImageSize != size) {
+            mMaximumDecodedImageSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * Returns whether to use fixed viewport.  Use fixed viewport
+     * whenever wide viewport is on.
+     */
+    /* package */ boolean getUseFixedViewport() {
+        return getUseWideViewPort();
+    }
+
+    /**
+     * Returns whether private browsing is enabled.
+     */
+    /* package */ boolean isPrivateBrowsingEnabled() {
+        return mPrivateBrowsingEnabled;
+    }
+
+    /**
+     * Sets whether private browsing is enabled.
+     * @param flag Whether private browsing should be enabled.
+     */
+    /* package */ synchronized void setPrivateBrowsingEnabled(boolean flag) {
+        if (mPrivateBrowsingEnabled != flag) {
+            mPrivateBrowsingEnabled = flag;
+
+            // AutoFill is dependant on private browsing being enabled so
+            // reset it to take account of the new value of mPrivateBrowsingEnabled.
+            setAutoFillEnabled(mAutoFillEnabled);
+
+            postSync();
+        }
+    }
+
+    /**
+     * Returns whether the viewport metatag can disable zooming
+     * @hide
+     */
+    public boolean forceUserScalable() {
+        return mForceUserScalable;
+    }
+
+    /**
+     * Sets whether viewport metatag can disable zooming.
+     * @param flag Whether or not to forceably enable user scalable.
+     * @hide
+     */
+    public synchronized void setForceUserScalable(boolean flag) {
+        mForceUserScalable = flag;
+    }
+
+    synchronized void setSyntheticLinksEnabled(boolean flag) {
+        if (mSyntheticLinksEnabled != flag) {
+            mSyntheticLinksEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized void setAutoFillEnabled(boolean enabled) {
+        // AutoFill is always disabled in private browsing mode.
+        boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
+        if (mAutoFillEnabled != autoFillEnabled) {
+            mAutoFillEnabled = autoFillEnabled;
+            postSync();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized boolean getAutoFillEnabled() {
+        return mAutoFillEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized void setAutoFillProfile(AutoFillProfile profile) {
+        if (mAutoFillProfile != profile) {
+            mAutoFillProfile = profile;
+            postSync();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized AutoFillProfile getAutoFillProfile() {
+        return mAutoFillProfile;
+    }
+
+    int getDoubleTapToastCount() {
+        return mDoubleTapToastCount;
+    }
+
+    void setDoubleTapToastCount(int count) {
+        if (mDoubleTapToastCount != count) {
+            mDoubleTapToastCount = count;
+            // write the settings in the non-UI thread
+            mEventHandler.sendMessage(Message.obtain(null,
+                    EventHandler.SET_DOUBLE_TAP_TOAST_COUNT));
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void setProperty(String key, String value) {
+        if (mWebView.nativeSetProperty(key, value)) {
+            mWebView.contentInvalidateAll();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public String getProperty(String key) {
+        return mWebView.nativeGetProperty(key);
+    }
+
+    /**
+     * Transfer messages from the queue to the new WebCoreThread. Called from
+     * WebCore thread.
+     */
+    /*package*/
+    synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
+        mBrowserFrame = frame;
+        if (DebugFlags.WEB_SETTINGS) {
+            junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
+        }
+
+        SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE,
+                Context.MODE_PRIVATE);
+        if (mDoubleTapToastCount > 0) {
+            mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT,
+                    mDoubleTapToastCount);
+        }
+        nativeSync(frame.mNativeFrame);
+        mSyncPending = false;
+        mEventHandler.createHandler();
+    }
+
+    /**
+     * Let the Settings object know that our owner is being destroyed.
+     */
+    /*package*/
+    synchronized void onDestroyed() {
+    }
+
+    private int pin(int size) {
+        // FIXME: 72 is just an arbitrary max text size value.
+        if (size < 1) {
+            return 1;
+        } else if (size > 72) {
+            return 72;
+        }
+        return size;
+    }
+
+    /* Post a SYNC message to handle syncing the native settings. */
+    private synchronized void postSync() {
+        // Only post if a sync is not pending
+        if (!mSyncPending) {
+            mSyncPending = mEventHandler.sendMessage(
+                    Message.obtain(null, EventHandler.SYNC));
+        }
+    }
+
+    // Synchronize the native and java settings.
+    private native void nativeSync(int nativeFrame);
+}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 510c168..a01c42d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -78,7 +78,7 @@
 
     private int mRingInset;
 
-    private WebView         mWebView;
+    private WebViewClassic  mWebView;
     private boolean         mSingle;
     private int             mWidthSpec;
     private int             mHeightSpec;
@@ -177,7 +177,7 @@
      * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ WebTextView(Context context, WebView webView, int autoFillQueryId) {
+    /* package */ WebTextView(Context context, WebViewClassic webView, int autoFillQueryId) {
         super(context, null, com.android.internal.R.attr.webTextViewStyle);
         mWebView = webView;
         mMaxLength = -1;
@@ -833,9 +833,9 @@
         }
         mInsideRemove = true;
         boolean isFocused = hasFocus();
-        mWebView.removeView(this);
+        mWebView.getWebView().removeView(this);
         if (isFocused) {
-            mWebView.requestFocus();
+            mWebView.getWebView().requestFocus();
         }
         mInsideRemove = false;
         mHandler.removeCallbacksAndMessages(null);
@@ -997,7 +997,7 @@
         }
         if (getParent() == null) {
             // Insert the view so that it's drawn first (at index 0)
-            mWebView.addView(this, 0, lp);
+            mWebView.getWebView().addView(this, 0, lp);
         } else if (needsUpdate) {
             setLayoutParams(lp);
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b9dfb2e..a561577 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,123 +16,35 @@
 
 package android.webkit;
 
-import android.animation.ObjectAnimator;
 import android.annotation.Widget;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.database.DataSetObserver;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Picture;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
-import android.net.Proxy;
-import android.net.ProxyProperties;
-import android.net.Uri;
 import android.net.http.SslCertificate;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.StrictMode;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.security.KeyChain;
-import android.speech.tts.TextToSpeech;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.EventLog;
 import android.util.Log;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
-import android.view.HardwareCanvas;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.ViewParent;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewTreeObserver;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.webkit.WebTextView.AutoCompleteAdapter;
-import android.webkit.WebViewCore.DrawData;
-import android.webkit.WebViewCore.EventHub;
-import android.webkit.WebViewCore.TextFieldInitData;
-import android.webkit.WebViewCore.TouchEventData;
-import android.webkit.WebViewCore.TouchHighlightData;
-import android.webkit.WebViewCore.WebKitHitTest;
 import android.widget.AbsoluteLayout;
-import android.widget.Adapter;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckedTextView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.OverScroller;
-import android.widget.Toast;
-
-import junit.framework.Assert;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * <p>A View that displays web pages. This class is the basis upon which you
@@ -342,300 +254,25 @@
  *
  *
  */
+/*
+ * Implementation notes.
+ * The WebView is a thin API class that delegates its public API to a backend WebViewProvider
+ * class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons.
+ * Methods are delegated to the provider implementation: all public API methods introduced in this
+ * file are fully delegated, whereas public and protected methods from the View base classes are
+ * only delegated where a specific need exists for them to do so.
+ */
 @Widget
 public class WebView extends AbsoluteLayout
         implements ViewTreeObserver.OnGlobalFocusChangeListener,
         ViewGroup.OnHierarchyChangeListener {
 
-    private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
-        @Override
-        public void onGlobalLayout() {
-            if (isShown()) {
-                setGLRectViewport();
-            }
-        }
-    }
+    // Default Provider factory class name.
+    private static final String DEFAULT_WEB_VIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
 
-    private class InnerScrollChangedListener implements ViewTreeObserver.OnScrollChangedListener {
-        @Override
-        public void onScrollChanged() {
-            if (isShown()) {
-                setGLRectViewport();
-            }
-        }
-    }
-
-    /**
-     * InputConnection used for ContentEditable. This captures changes
-     * to the text and sends them either as key strokes or text changes.
-     */
-    private class WebViewInputConnection extends BaseInputConnection {
-        // Used for mapping characters to keys typed.
-        private KeyCharacterMap mKeyCharacterMap;
-        private boolean mIsKeySentByMe;
-        private int mInputType;
-        private int mImeOptions;
-        private String mHint;
-
-        public WebViewInputConnection() {
-            super(WebView.this, true);
-        }
-
-        @Override
-        public boolean sendKeyEvent(KeyEvent event) {
-            // Some IMEs send key events directly using sendKeyEvents.
-            // WebViewInputConnection should treat these as text changes.
-            if (!mIsKeySentByMe) {
-                if (event.getAction() == KeyEvent.ACTION_UP) {
-                    if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
-                        return deleteSurroundingText(1, 0);
-                    } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
-                        return deleteSurroundingText(0, 1);
-                    } else if (event.getUnicodeChar() != 0){
-                        String newComposingText =
-                                Character.toString((char)event.getUnicodeChar());
-                        return commitText(newComposingText, 1);
-                    }
-                } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
-                        (event.getKeyCode() == KeyEvent.KEYCODE_DEL
-                        || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
-                        || event.getUnicodeChar() != 0)) {
-                    return true; // only act on action_down
-                }
-            }
-            return super.sendKeyEvent(event);
-        }
-
-        public void setTextAndKeepSelection(CharSequence text) {
-            Editable editable = getEditable();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            editable.replace(0, editable.length(), text);
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                // Since the text has changed, do not allow the IME to replace the
-                // existing text as though it were a completion.
-                imm.restartInput(WebView.this);
-            }
-            // Keep the previous selection.
-            selectionStart = Math.min(selectionStart, editable.length());
-            selectionEnd = Math.min(selectionEnd, editable.length());
-            setSelection(selectionStart, selectionEnd);
-        }
-
-        @Override
-        public boolean setComposingText(CharSequence text, int newCursorPosition) {
-            Editable editable = getEditable();
-            int start = getComposingSpanStart(editable);
-            int end = getComposingSpanEnd(editable);
-            if (start < 0 || end < 0) {
-                start = Selection.getSelectionStart(editable);
-                end = Selection.getSelectionEnd(editable);
-            }
-            if (end < start) {
-                int temp = end;
-                end = start;
-                start = temp;
-            }
-            setNewText(start, end, text);
-            return super.setComposingText(text, newCursorPosition);
-        }
-
-        @Override
-        public boolean commitText(CharSequence text, int newCursorPosition) {
-            setComposingText(text, newCursorPosition);
-            int cursorPosition = Selection.getSelectionEnd(getEditable());
-            setComposingRegion(cursorPosition, cursorPosition);
-            return true;
-        }
-
-        @Override
-        public boolean deleteSurroundingText(int leftLength, int rightLength) {
-            Editable editable = getEditable();
-            int cursorPosition = Selection.getSelectionEnd(editable);
-            int startDelete = Math.max(0, cursorPosition - leftLength);
-            int endDelete = Math.min(editable.length(),
-                    cursorPosition + rightLength);
-            setNewText(startDelete, endDelete, "");
-            return super.deleteSurroundingText(leftLength, rightLength);
-        }
-
-        public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
-            int type = initData.mType;
-            int inputType = InputType.TYPE_CLASS_TEXT
-                    | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
-            int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
-                    | EditorInfo.IME_FLAG_NO_FULLSCREEN;
-            if (!initData.mIsSpellCheckEnabled) {
-                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
-            }
-            if (WebTextView.TEXT_AREA != type
-                    && initData.mIsTextFieldNext) {
-                imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
-            }
-            switch (type) {
-                case WebTextView.NORMAL_TEXT_FIELD:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.TEXT_AREA:
-                    inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
-                            | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
-                            | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
-                    imeOptions |= EditorInfo.IME_ACTION_NONE;
-                    break;
-                case WebTextView.PASSWORD:
-                    inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.SEARCH:
-                    imeOptions |= EditorInfo.IME_ACTION_SEARCH;
-                    break;
-                case WebTextView.EMAIL:
-                    // inputType needs to be overwritten because of the different text variation.
-                    inputType = InputType.TYPE_CLASS_TEXT
-                            | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-                case WebTextView.NUMBER:
-                    // inputType needs to be overwritten because of the different class.
-                    inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
-                            | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
-                    // Number and telephone do not have both a Tab key and an
-                    // action, so set the action to NEXT
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
-                    break;
-                case WebTextView.TELEPHONE:
-                    // inputType needs to be overwritten because of the different class.
-                    inputType = InputType.TYPE_CLASS_PHONE;
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
-                    break;
-                case WebTextView.URL:
-                    // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
-                    // exclude it for now.
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    inputType |= InputType.TYPE_TEXT_VARIATION_URI;
-                    break;
-                default:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
-                    break;
-            }
-            mHint = initData.mLabel;
-            mInputType = inputType;
-            mImeOptions = imeOptions;
-        }
-
-        public void setupEditorInfo(EditorInfo outAttrs) {
-            outAttrs.inputType = mInputType;
-            outAttrs.imeOptions = mImeOptions;
-            outAttrs.hintText = mHint;
-            outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
-        }
-
-        /**
-         * Sends a text change to webkit indirectly. If it is a single-
-         * character add or delete, it sends it as a key stroke. If it cannot
-         * be represented as a key stroke, it sends it as a field change.
-         * @param start The start offset (inclusive) of the text being changed.
-         * @param end The end offset (exclusive) of the text being changed.
-         * @param text The new text to replace the changed text.
-         */
-        private void setNewText(int start, int end, CharSequence text) {
-            mIsKeySentByMe = true;
-            Editable editable = getEditable();
-            CharSequence original = editable.subSequence(start, end);
-            boolean isCharacterAdd = false;
-            boolean isCharacterDelete = false;
-            int textLength = text.length();
-            int originalLength = original.length();
-            if (textLength > originalLength) {
-                isCharacterAdd = (textLength == originalLength + 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                originalLength);
-            } else if (originalLength > textLength) {
-                isCharacterDelete = (textLength == originalLength - 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                textLength);
-            }
-            if (isCharacterAdd) {
-                sendCharacter(text.charAt(textLength - 1));
-            } else if (isCharacterDelete) {
-                sendDeleteKey();
-            } else if ((textLength != originalLength) ||
-                    !TextUtils.regionMatches(text, 0, original, 0,
-                            textLength)) {
-                // Send a message so that key strokes and text replacement
-                // do not come out of order.
-                Message replaceMessage = mPrivateHandler.obtainMessage(
-                        REPLACE_TEXT, start,  end, text.toString());
-                mPrivateHandler.sendMessage(replaceMessage);
-            }
-            mIsKeySentByMe = false;
-        }
-
-        /**
-         * Send a single character to the WebView as a key down and up event.
-         * @param c The character to be sent.
-         */
-        private void sendCharacter(char c) {
-            if (mKeyCharacterMap == null) {
-                mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-            }
-            char[] chars = new char[1];
-            chars[0] = c;
-            KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
-            if (events != null) {
-                for (KeyEvent event : events) {
-                    sendKeyEvent(event);
-                }
-            } else {
-                Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
-                mPrivateHandler.sendMessage(msg);
-            }
-        }
-
-        /**
-         * Send the delete character as a key down and up event.
-         */
-        private void sendDeleteKey() {
-            long eventTime = SystemClock.uptimeMillis();
-            sendKeyEvent(new KeyEvent(eventTime, eventTime,
-                    KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                    KeyEvent.FLAG_SOFT_KEYBOARD));
-            sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
-                    KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL, 0, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                    KeyEvent.FLAG_SOFT_KEYBOARD));
-        }
-    }
-
-
-    // The listener to capture global layout change event.
-    private InnerGlobalLayoutListener mGlobalLayoutListener = null;
-
-    // The listener to capture scroll event.
-    private InnerScrollChangedListener mScrollChangedListener = null;
-
-    // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
-    // the screen all-the-time. Good for profiling our drawing code
-    static private final boolean AUTO_REDRAW_HACK = false;
-    // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
-    private boolean mAutoRedraw;
-
-    // Reference to the AlertDialog displayed by InvokeListBox.
-    // It's used to dismiss the dialog in destroy if not done before.
-    private AlertDialog mListBoxDialog = null;
-
-    static final String LOGTAG = "webview";
-
-    private ZoomManager mZoomManager;
-
-    private final Rect mGLRectViewport = new Rect();
-    private final Rect mViewRectViewport = new Rect();
-    private final RectF mVisibleContentRect = new RectF();
-    private boolean mGLViewportEmpty = false;
-    WebViewInputConnection mInputConnection = null;
-    private int mFieldPointer;
+    private static final String LOGTAG = "webview_proxy";
+    // TODO: flip DEBUG to always be disabled.
+    private static final boolean DEBUG = true;
 
     /**
      *  Transportation object for returning WebView across thread boundaries.
@@ -660,526 +297,6 @@
         }
     }
 
-    private static class OnTrimMemoryListener implements ComponentCallbacks2 {
-        private static OnTrimMemoryListener sInstance = null;
-
-        static void init(Context c) {
-            if (sInstance == null) {
-                sInstance = new OnTrimMemoryListener(c.getApplicationContext());
-            }
-        }
-
-        private OnTrimMemoryListener(Context c) {
-            c.registerComponentCallbacks(this);
-        }
-
-        @Override
-        public void onConfigurationChanged(Configuration newConfig) {
-            // Ignore
-        }
-
-        @Override
-        public void onLowMemory() {
-            // Ignore
-        }
-
-        @Override
-        public void onTrimMemory(int level) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.d("WebView", "onTrimMemory: " + level);
-            }
-            WebView.nativeOnTrimMemory(level);
-        }
-
-    }
-
-    // A final CallbackProxy shared by WebViewCore and BrowserFrame.
-    private final CallbackProxy mCallbackProxy;
-
-    private final WebViewDatabase mDatabase;
-
-    // SSL certificate for the main top-level page (if secure)
-    private SslCertificate mCertificate;
-
-    // Native WebView pointer that is 0 until the native object has been
-    // created.
-    private int mNativeClass;
-    // This would be final but it needs to be set to null when the WebView is
-    // destroyed.
-    private WebViewCore mWebViewCore;
-    // Handler for dispatching UI messages.
-    /* package */ final Handler mPrivateHandler = new PrivateHandler();
-    private WebTextView mWebTextView;
-    // Used to ignore changes to webkit text that arrives to the UI side after
-    // more key events.
-    private int mTextGeneration;
-
-    /* package */ void incrementTextGeneration() { mTextGeneration++; }
-
-    // Used by WebViewCore to create child views.
-    /* package */ final ViewManager mViewManager;
-
-    // Used to display in full screen mode
-    PluginFullScreenHolder mFullScreenHolder;
-
-    /**
-     * Position of the last touch event in pixels.
-     * Use integer to prevent loss of dragging delta calculation accuracy;
-     * which was done in float and converted to integer, and resulted in gradual
-     * and compounding touch position and view dragging mismatch.
-     */
-    private int mLastTouchX;
-    private int mLastTouchY;
-    private int mStartTouchX;
-    private int mStartTouchY;
-    private float mAverageAngle;
-
-    /**
-     * Time of the last touch event.
-     */
-    private long mLastTouchTime;
-
-    /**
-     * Time of the last time sending touch event to WebViewCore
-     */
-    private long mLastSentTouchTime;
-
-    /**
-     * The minimum elapsed time before sending another ACTION_MOVE event to
-     * WebViewCore. This really should be tuned for each type of the devices.
-     * For example in Google Map api test case, it takes Dream device at least
-     * 150ms to do a full cycle in the WebViewCore by processing a touch event,
-     * triggering the layout and drawing the picture. While the same process
-     * takes 60+ms on the current high speed device. If we make
-     * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
-     * to WebViewCore queue and the real layout and draw events will be pushed
-     * to further, which slows down the refresh rate. Choose 50 to favor the
-     * current high speed devices. For Dream like devices, 100 is a better
-     * choice. Maybe make this in the buildspec later.
-     * (Update 12/14/2010: changed to 0 since current device should be able to
-     * handle the raw events and Map team voted to have the raw events too.
-     */
-    private static final int TOUCH_SENT_INTERVAL = 0;
-    private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
-
-    /**
-     * Helper class to get velocity for fling
-     */
-    VelocityTracker mVelocityTracker;
-    private int mMaximumFling;
-    private float mLastVelocity;
-    private float mLastVelX;
-    private float mLastVelY;
-
-    // The id of the native layer being scrolled.
-    private int mCurrentScrollingLayerId;
-    private Rect mScrollingLayerRect = new Rect();
-
-    // only trigger accelerated fling if the new velocity is at least
-    // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
-    private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
-
-    /**
-     * Touch mode
-     */
-    private int mTouchMode = TOUCH_DONE_MODE;
-    private static final int TOUCH_INIT_MODE = 1;
-    private static final int TOUCH_DRAG_START_MODE = 2;
-    private static final int TOUCH_DRAG_MODE = 3;
-    private static final int TOUCH_SHORTPRESS_START_MODE = 4;
-    private static final int TOUCH_SHORTPRESS_MODE = 5;
-    private static final int TOUCH_DOUBLE_TAP_MODE = 6;
-    private static final int TOUCH_DONE_MODE = 7;
-    private static final int TOUCH_PINCH_DRAG = 8;
-    private static final int TOUCH_DRAG_LAYER_MODE = 9;
-
-    // Whether to forward the touch events to WebCore
-    // Can only be set by WebKit via JNI.
-    private boolean mForwardTouchEvents = false;
-
-    // Whether to prevent default during touch. The initial value depends on
-    // mForwardTouchEvents. If WebCore wants all the touch events, it says yes
-    // for touch down. Otherwise UI will wait for the answer of the first
-    // confirmed move before taking over the control.
-    private static final int PREVENT_DEFAULT_NO = 0;
-    private static final int PREVENT_DEFAULT_MAYBE_YES = 1;
-    private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2;
-    private static final int PREVENT_DEFAULT_YES = 3;
-    private static final int PREVENT_DEFAULT_IGNORE = 4;
-    private int mPreventDefault = PREVENT_DEFAULT_IGNORE;
-
-    // true when the touch movement exceeds the slop
-    private boolean mConfirmMove;
-
-    // if true, touch events will be first processed by WebCore, if prevent
-    // default is not set, the UI will continue handle them.
-    private boolean mDeferTouchProcess;
-
-    // to avoid interfering with the current touch events, track them
-    // separately. Currently no snapping or fling in the deferred process mode
-    private int mDeferTouchMode = TOUCH_DONE_MODE;
-    private float mLastDeferTouchX;
-    private float mLastDeferTouchY;
-
-    // To keep track of whether the current drag was initiated by a WebTextView,
-    // so that we know not to hide the cursor
-    boolean mDragFromTextInput;
-
-    // Whether or not to draw the cursor ring.
-    private boolean mDrawCursorRing = true;
-
-    // true if onPause has been called (and not onResume)
-    private boolean mIsPaused;
-
-    private HitTestResult mInitialHitTestResult;
-    private WebKitHitTest mFocusedNode;
-
-    /**
-     * Customizable constant
-     */
-    // pre-computed square of ViewConfiguration.getScaledTouchSlop()
-    private int mTouchSlopSquare;
-    // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
-    private int mDoubleTapSlopSquare;
-    // pre-computed density adjusted navigation slop
-    private int mNavSlop;
-    // This should be ViewConfiguration.getTapTimeout()
-    // But system time out is 100ms, which is too short for the browser.
-    // In the browser, if it switches out of tap too soon, jump tap won't work.
-    // In addition, a double tap on a trackpad will always have a duration of
-    // 300ms, so this value must be at least that (otherwise we will timeout the
-    // first tap and convert it to a long press).
-    private static final int TAP_TIMEOUT = 300;
-    // This should be ViewConfiguration.getLongPressTimeout()
-    // But system time out is 500ms, which is too short for the browser.
-    // With a short timeout, it's difficult to treat trigger a short press.
-    private static final int LONG_PRESS_TIMEOUT = 1000;
-    // needed to avoid flinging after a pause of no movement
-    private static final int MIN_FLING_TIME = 250;
-    // draw unfiltered after drag is held without movement
-    private static final int MOTIONLESS_TIME = 100;
-    // The amount of content to overlap between two screens when going through
-    // pages with the space bar, in pixels.
-    private static final int PAGE_SCROLL_OVERLAP = 24;
-
-    /**
-     * These prevent calling requestLayout if either dimension is fixed. This
-     * depends on the layout parameters and the measure specs.
-     */
-    boolean mWidthCanMeasure;
-    boolean mHeightCanMeasure;
-
-    // Remember the last dimensions we sent to the native side so we can avoid
-    // sending the same dimensions more than once.
-    int mLastWidthSent;
-    int mLastHeightSent;
-    // Since view height sent to webkit could be fixed to avoid relayout, this
-    // value records the last sent actual view height.
-    int mLastActualHeightSent;
-
-    private int mContentWidth;   // cache of value from WebViewCore
-    private int mContentHeight;  // cache of value from WebViewCore
-
-    // Need to have the separate control for horizontal and vertical scrollbar
-    // style than the View's single scrollbar style
-    private boolean mOverlayHorizontalScrollbar = true;
-    private boolean mOverlayVerticalScrollbar = false;
-
-    // our standard speed. this way small distances will be traversed in less
-    // time than large distances, but we cap the duration, so that very large
-    // distances won't take too long to get there.
-    private static final int STD_SPEED = 480;  // pixels per second
-    // time for the longest scroll animation
-    private static final int MAX_DURATION = 750;   // milliseconds
-    private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
-
-    // Used by OverScrollGlow
-    OverScroller mScroller;
-
-    private boolean mInOverScrollMode = false;
-    private static Paint mOverScrollBackground;
-    private static Paint mOverScrollBorder;
-
-    private boolean mWrapContent;
-    private static final int MOTIONLESS_FALSE           = 0;
-    private static final int MOTIONLESS_PENDING         = 1;
-    private static final int MOTIONLESS_TRUE            = 2;
-    private static final int MOTIONLESS_IGNORE          = 3;
-    private int mHeldMotionless;
-
-    // An instance for injecting accessibility in WebViews with disabled
-    // JavaScript or ones for which no accessibility script exists
-    private AccessibilityInjector mAccessibilityInjector;
-
-    // flag indicating if accessibility script is injected so we
-    // know to handle Shift and arrows natively first
-    private boolean mAccessibilityScriptInjected;
-
-
-    /**
-     * How long the caret handle will last without being touched.
-     */
-    private static final long CARET_HANDLE_STAMINA_MS = 3000;
-
-    private Drawable mSelectHandleLeft;
-    private Drawable mSelectHandleRight;
-    private Drawable mSelectHandleCenter;
-    private Rect mSelectCursorBase = new Rect();
-    private int mSelectCursorBaseLayerId;
-    private Rect mSelectCursorExtent = new Rect();
-    private int mSelectCursorExtentLayerId;
-    private Rect mSelectDraggingCursor;
-    private Point mSelectDraggingOffset = new Point();
-    private boolean mIsCaretSelection;
-    static final int HANDLE_ID_START = 0;
-    static final int HANDLE_ID_END = 1;
-    static final int HANDLE_ID_BASE = 2;
-    static final int HANDLE_ID_EXTENT = 3;
-
-    static boolean sDisableNavcache = false;
-    static boolean sEnableWebTextView = false;
-    // the color used to highlight the touch rectangles
-    static final int HIGHLIGHT_COLOR = 0x6633b5e5;
-    // the region indicating where the user touched on the screen
-    private Region mTouchHighlightRegion = new Region();
-    // the paint for the touch highlight
-    private Paint mTouchHightlightPaint = new Paint();
-    // debug only
-    private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
-    private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
-    private Paint mTouchCrossHairColor;
-    private int mTouchHighlightX;
-    private int mTouchHighlightY;
-    private long mTouchHighlightRequested;
-
-    // Basically this proxy is used to tell the Video to update layer tree at
-    // SetBaseLayer time and to pause when WebView paused.
-    private HTML5VideoViewProxy mHTML5VideoViewProxy;
-
-    // If we are using a set picture, don't send view updates to webkit
-    private boolean mBlockWebkitViewMessages = false;
-
-    // cached value used to determine if we need to switch drawing models
-    private boolean mHardwareAccelSkia = false;
-
-    /*
-     * Private message ids
-     */
-    private static final int REMEMBER_PASSWORD          = 1;
-    private static final int NEVER_REMEMBER_PASSWORD    = 2;
-    private static final int SWITCH_TO_SHORTPRESS       = 3;
-    private static final int SWITCH_TO_LONGPRESS        = 4;
-    private static final int RELEASE_SINGLE_TAP         = 5;
-    private static final int REQUEST_FORM_DATA          = 6;
-    private static final int DRAG_HELD_MOTIONLESS       = 8;
-    private static final int AWAKEN_SCROLL_BARS         = 9;
-    private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
-    private static final int SCROLL_SELECT_TEXT         = 11;
-
-
-    private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
-    private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
-
-    /*
-     * Package message ids
-     */
-    static final int SCROLL_TO_MSG_ID                   = 101;
-    static final int NEW_PICTURE_MSG_ID                 = 105;
-    static final int UPDATE_TEXT_ENTRY_MSG_ID           = 106;
-    static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
-    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
-    static final int UPDATE_ZOOM_RANGE                  = 109;
-    static final int UNHANDLED_NAV_KEY                  = 110;
-    static final int CLEAR_TEXT_ENTRY                   = 111;
-    static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
-    static final int SHOW_RECT_MSG_ID                   = 113;
-    static final int LONG_PRESS_CENTER                  = 114;
-    static final int PREVENT_TOUCH_ID                   = 115;
-    static final int WEBCORE_NEED_TOUCH_EVENTS          = 116;
-    // obj=Rect in doc coordinates
-    static final int INVAL_RECT_MSG_ID                  = 117;
-    static final int REQUEST_KEYBOARD                   = 118;
-    static final int DO_MOTION_UP                       = 119;
-    static final int SHOW_FULLSCREEN                    = 120;
-    static final int HIDE_FULLSCREEN                    = 121;
-    static final int DOM_FOCUS_CHANGED                  = 122;
-    static final int REPLACE_BASE_CONTENT               = 123;
-    static final int FORM_DID_BLUR                      = 124;
-    static final int RETURN_LABEL                       = 125;
-    static final int UPDATE_MATCH_COUNT                 = 126;
-    static final int CENTER_FIT_RECT                    = 127;
-    static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
-    static final int SET_SCROLLBAR_MODES                = 129;
-    static final int SELECTION_STRING_CHANGED           = 130;
-    static final int HIT_TEST_RESULT                    = 131;
-    static final int SAVE_WEBARCHIVE_FINISHED           = 132;
-
-    static final int SET_AUTOFILLABLE                   = 133;
-    static final int AUTOFILL_COMPLETE                  = 134;
-
-    static final int SELECT_AT                          = 135;
-    static final int SCREEN_ON                          = 136;
-    static final int ENTER_FULLSCREEN_VIDEO             = 137;
-    static final int UPDATE_SELECTION                   = 138;
-    static final int UPDATE_ZOOM_DENSITY                = 139;
-    static final int EXIT_FULLSCREEN_VIDEO              = 140;
-
-    static final int COPY_TO_CLIPBOARD                  = 141;
-    static final int INIT_EDIT_FIELD                    = 142;
-    static final int REPLACE_TEXT                       = 143;
-    static final int CLEAR_CARET_HANDLE                 = 144;
-    static final int KEY_PRESS                          = 145;
-
-    private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
-    private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
-
-    static final String[] HandlerPrivateDebugString = {
-        "REMEMBER_PASSWORD", //              = 1;
-        "NEVER_REMEMBER_PASSWORD", //        = 2;
-        "SWITCH_TO_SHORTPRESS", //           = 3;
-        "SWITCH_TO_LONGPRESS", //            = 4;
-        "RELEASE_SINGLE_TAP", //             = 5;
-        "REQUEST_FORM_DATA", //              = 6;
-        "RESUME_WEBCORE_PRIORITY", //        = 7;
-        "DRAG_HELD_MOTIONLESS", //           = 8;
-        "AWAKEN_SCROLL_BARS", //             = 9;
-        "PREVENT_DEFAULT_TIMEOUT", //        = 10;
-        "SCROLL_SELECT_TEXT" //              = 11;
-    };
-
-    static final String[] HandlerPackageDebugString = {
-        "SCROLL_TO_MSG_ID", //               = 101;
-        "102", //                            = 102;
-        "103", //                            = 103;
-        "104", //                            = 104;
-        "NEW_PICTURE_MSG_ID", //             = 105;
-        "UPDATE_TEXT_ENTRY_MSG_ID", //       = 106;
-        "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
-        "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
-        "UPDATE_ZOOM_RANGE", //              = 109;
-        "UNHANDLED_NAV_KEY", //              = 110;
-        "CLEAR_TEXT_ENTRY", //               = 111;
-        "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
-        "SHOW_RECT_MSG_ID", //               = 113;
-        "LONG_PRESS_CENTER", //              = 114;
-        "PREVENT_TOUCH_ID", //               = 115;
-        "WEBCORE_NEED_TOUCH_EVENTS", //      = 116;
-        "INVAL_RECT_MSG_ID", //              = 117;
-        "REQUEST_KEYBOARD", //               = 118;
-        "DO_MOTION_UP", //                   = 119;
-        "SHOW_FULLSCREEN", //                = 120;
-        "HIDE_FULLSCREEN", //                = 121;
-        "DOM_FOCUS_CHANGED", //              = 122;
-        "REPLACE_BASE_CONTENT", //           = 123;
-        "FORM_DID_BLUR", //                  = 124;
-        "RETURN_LABEL", //                   = 125;
-        "UPDATE_MATCH_COUNT", //             = 126;
-        "CENTER_FIT_RECT", //                = 127;
-        "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
-        "SET_SCROLLBAR_MODES", //            = 129;
-        "SELECTION_STRING_CHANGED", //       = 130;
-        "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
-        "SAVE_WEBARCHIVE_FINISHED", //       = 132;
-        "SET_AUTOFILLABLE", //               = 133;
-        "AUTOFILL_COMPLETE", //              = 134;
-        "SELECT_AT", //                      = 135;
-        "SCREEN_ON", //                      = 136;
-        "ENTER_FULLSCREEN_VIDEO", //         = 137;
-        "UPDATE_SELECTION", //               = 138;
-        "UPDATE_ZOOM_DENSITY" //             = 139;
-    };
-
-    // If the site doesn't use the viewport meta tag to specify the viewport,
-    // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
-    static final int DEFAULT_VIEWPORT_WIDTH = 980;
-
-    // normally we try to fit the content to the minimum preferred width
-    // calculated by the Webkit. To avoid the bad behavior when some site's
-    // minimum preferred width keeps growing when changing the viewport width or
-    // the minimum preferred width is huge, an upper limit is needed.
-    static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
-
-    // initial scale in percent. 0 means using default.
-    private int mInitialScaleInPercent = 0;
-
-    // Whether or not a scroll event should be sent to webkit.  This is only set
-    // to false when restoring the scroll position.
-    private boolean mSendScrollEvent = true;
-
-    private int mSnapScrollMode = SNAP_NONE;
-    private static final int SNAP_NONE = 0;
-    private static final int SNAP_LOCK = 1; // not a separate state
-    private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
-    private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
-    private boolean mSnapPositive;
-
-    // keep these in sync with their counterparts in WebView.cpp
-    private static final int DRAW_EXTRAS_NONE = 0;
-    private static final int DRAW_EXTRAS_SELECTION = 1;
-    private static final int DRAW_EXTRAS_CURSOR_RING = 2;
-
-    // keep this in sync with WebCore:ScrollbarMode in WebKit
-    private static final int SCROLLBAR_AUTO = 0;
-    private static final int SCROLLBAR_ALWAYSOFF = 1;
-    // as we auto fade scrollbar, this is ignored.
-    private static final int SCROLLBAR_ALWAYSON = 2;
-    private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
-    private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
-
-    // constants for determining script injection strategy
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
-
-    // the alias via which accessibility JavaScript interface is exposed
-    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
-
-    // Template for JavaScript that injects a screen-reader.
-    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
-        "javascript:(function() {" +
-        "    var chooser = document.createElement('script');" +
-        "    chooser.type = 'text/javascript';" +
-        "    chooser.src = '%1s';" +
-        "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
-        "  })();";
-
-    // Regular expression that matches the "axs" URL parameter.
-    // The value of 0 means the accessibility script is opted out
-    // The value of 1 means the accessibility script is already injected
-    private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
-
-    // TextToSpeech instance exposed to JavaScript to the injected screenreader.
-    private TextToSpeech mTextToSpeech;
-
-    // variable to cache the above pattern in case accessibility is enabled.
-    private Pattern mMatchAxsUrlParameterPattern;
-
-    /**
-     * Max distance to overscroll by in pixels.
-     * This how far content can be pulled beyond its normal bounds by the user.
-     */
-    private int mOverscrollDistance;
-
-    /**
-     * Max distance to overfling by in pixels.
-     * This is how far flinged content can move beyond the end of its normal bounds.
-     */
-    private int mOverflingDistance;
-
-    private OverScrollGlow mOverScrollGlow;
-
-    // Used to match key downs and key ups
-    private Vector<Integer> mKeysPressed;
-
-    /* package */ static boolean mLogEvent = true;
-
-    // for event log
-    private long mLastTouchUpTime = 0;
-
-    private WebViewCore.AutoFillData mAutoFillData;
-
-    private static boolean sNotificationsEnabled = true;
-
     /**
      * URI scheme for telephone number
      */
@@ -1193,26 +310,6 @@
      */
     public static final String SCHEME_GEO = "geo:0,0?q=";
 
-    private int mBackgroundColor = Color.WHITE;
-
-    private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
-    private int mAutoScrollX = 0;
-    private int mAutoScrollY = 0;
-    private int mMinAutoScrollX = 0;
-    private int mMaxAutoScrollX = 0;
-    private int mMinAutoScrollY = 0;
-    private int mMaxAutoScrollY = 0;
-    private Rect mScrollingLayerBounds = new Rect();
-    private boolean mSentAutoScrollMessage = false;
-
-    // used for serializing asynchronously handled touch events.
-    private final TouchEventQueue mTouchEventQueue = new TouchEventQueue();
-
-    // Used to track whether picture updating was paused due to a window focus change.
-    private boolean mPictureUpdatePausedForFocusChange = false;
-
-    // Used to notify listeners of a new picture.
-    private PictureListener mPictureListener;
     /**
      * Interface to listen for new pictures as they change.
      * @deprecated This interface is now obsolete.
@@ -1280,15 +377,24 @@
         private int mType;
         private String mExtra;
 
-        HitTestResult() {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public HitTestResult() {
             mType = UNKNOWN_TYPE;
         }
 
-        private void setType(int type) {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public void setType(int type) {
             mType = type;
         }
 
-        private void setExtra(String extra) {
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        public void setExtra(String extra) {
             mExtra = extra;
         }
 
@@ -1311,15 +417,6 @@
     }
 
     /**
-     * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
-     */
-    static class FocusNodeHref {
-        static final String TITLE = "title";
-        static final String URL = "url";
-        static final String SRC = "src";
-    }
-
-    /**
      * Construct a new WebView with a Context object.
      * @param context A Context object used to access application assets.
      */
@@ -1369,418 +466,20 @@
      * @param javaScriptInterfaces is a Map of interface names, as keys, and
      * object implementing those interfaces, as values.
      * @param privateBrowsing If true the web view will be initialized in private mode.
-     * @hide This is an implementation detail.
+     * @hide This is used internally by dumprendertree, as it requires the javaScript interfaces to
+     * be added synchronously, before a subsequent loadUrl call takes effect.
      */
+    @SuppressWarnings("deprecation")  // for super() call into deprecated base class constructor.
     protected WebView(Context context, AttributeSet attrs, int defStyle,
             Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
         super(context, attrs, defStyle);
-        checkThread();
-
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
         }
+        checkThread();
 
-        // Used by the chrome stack to find application paths
-        JniUtil.setContext(context);
-
-        mCallbackProxy = new CallbackProxy(context, this);
-        mViewManager = new ViewManager(this);
-        L10nUtils.setApplicationContext(context.getApplicationContext());
-        mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
-        mDatabase = WebViewDatabase.getInstance(context);
-        mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
-        mZoomManager = new ZoomManager(this, mCallbackProxy);
-
-        /* The init method must follow the creation of certain member variables,
-         * such as the mZoomManager.
-         */
-        init();
-        setupPackageListener(context);
-        setupProxyListener(context);
-        setupTrustStorageListener(context);
-        updateMultiTouchSupport(context);
-
-        if (privateBrowsing) {
-            startPrivateBrowsing();
-        }
-
-        mAutoFillData = new WebViewCore.AutoFillData();
-    }
-
-    private static class TrustStorageListener extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
-                handleCertTrustChanged();
-            }
-        }
-    }
-    private static TrustStorageListener sTrustStorageListener;
-
-    /**
-     * Handles update to the trust storage.
-     */
-    private static void handleCertTrustChanged() {
-        // send a message for indicating trust storage change
-        WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
-    }
-
-    /*
-     * @param context This method expects this to be a valid context.
-     */
-    private static void setupTrustStorageListener(Context context) {
-        if (sTrustStorageListener != null ) {
-            return;
-        }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
-        sTrustStorageListener = new TrustStorageListener();
-        Intent current =
-            context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
-        if (current != null) {
-            handleCertTrustChanged();
-        }
-    }
-
-    private static class ProxyReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
-                handleProxyBroadcast(intent);
-            }
-        }
-    }
-
-    /*
-     * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
-     */
-    private static ProxyReceiver sProxyReceiver;
-
-    /*
-     * @param context This method expects this to be a valid context
-     */
-    private static synchronized void setupProxyListener(Context context) {
-        if (sProxyReceiver != null || sNotificationsEnabled == false) {
-            return;
-        }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
-        sProxyReceiver = new ProxyReceiver();
-        Intent currentProxy = context.getApplicationContext().registerReceiver(
-                sProxyReceiver, filter);
-        if (currentProxy != null) {
-            handleProxyBroadcast(currentProxy);
-        }
-    }
-
-    /*
-     * @param context This method expects this to be a valid context
-     */
-    private static synchronized void disableProxyListener(Context context) {
-        if (sProxyReceiver == null)
-            return;
-
-        context.getApplicationContext().unregisterReceiver(sProxyReceiver);
-        sProxyReceiver = null;
-    }
-
-    private static void handleProxyBroadcast(Intent intent) {
-        ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
-        if (proxyProperties == null || proxyProperties.getHost() == null) {
-            WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
-            return;
-        }
-        WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
-    }
-
-    /*
-     * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
-     * or ACTION_PACKAGE_REMOVED.
-     */
-    private static boolean sPackageInstallationReceiverAdded = false;
-
-    /*
-     * A set of Google packages we monitor for the
-     * navigator.isApplicationInstalled() API. Add additional packages as
-     * needed.
-     */
-    private static Set<String> sGoogleApps;
-    static {
-        sGoogleApps = new HashSet<String>();
-        sGoogleApps.add("com.google.android.youtube");
-    }
-
-    private static class PackageListener extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final String packageName = intent.getData().getSchemeSpecificPart();
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
-                // if it is replacing, refreshPlugins() when adding
-                return;
-            }
-
-            if (sGoogleApps.contains(packageName)) {
-                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
-                } else {
-                    WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
-                }
-            }
-
-            PluginManager pm = PluginManager.getInstance(context);
-            if (pm.containsPluginPermissionAndSignatures(packageName)) {
-                pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
-            }
-        }
-    }
-
-    private void setupPackageListener(Context context) {
-
-        /*
-         * we must synchronize the instance check and the creation of the
-         * receiver to ensure that only ONE receiver exists for all WebView
-         * instances.
-         */
-        synchronized (WebView.class) {
-
-            // if the receiver already exists then we do not need to register it
-            // again
-            if (sPackageInstallationReceiverAdded) {
-                return;
-            }
-
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addDataScheme("package");
-            BroadcastReceiver packageListener = new PackageListener();
-            context.getApplicationContext().registerReceiver(packageListener, filter);
-            sPackageInstallationReceiverAdded = true;
-        }
-
-        // check if any of the monitored apps are already installed
-        AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
-
-            @Override
-            protected Set<String> doInBackground(Void... unused) {
-                Set<String> installedPackages = new HashSet<String>();
-                PackageManager pm = mContext.getPackageManager();
-                for (String name : sGoogleApps) {
-                    try {
-                        pm.getPackageInfo(name,
-                                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
-                        installedPackages.add(name);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        // package not found
-                    }
-                }
-                return installedPackages;
-            }
-
-            // Executes on the UI thread
-            @Override
-            protected void onPostExecute(Set<String> installedPackages) {
-                if (mWebViewCore != null) {
-                    mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
-                }
-            }
-        };
-        task.execute();
-    }
-
-    void updateMultiTouchSupport(Context context) {
-        mZoomManager.updateMultiTouchSupport(context);
-    }
-
-    private void init() {
-        OnTrimMemoryListener.init(getContext());
-        sDisableNavcache = nativeDisableNavcache();
-        setWillNotDraw(false);
-        setFocusable(true);
-        setFocusableInTouchMode(true);
-        setClickable(true);
-        setLongClickable(true);
-
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        int slop = configuration.getScaledTouchSlop();
-        mTouchSlopSquare = slop * slop;
-        slop = configuration.getScaledDoubleTapSlop();
-        mDoubleTapSlopSquare = slop * slop;
-        final float density = getContext().getResources().getDisplayMetrics().density;
-        // use one line height, 16 based on our current default font, for how
-        // far we allow a touch be away from the edge of a link
-        mNavSlop = (int) (16 * density);
-        mZoomManager.init(density);
-        mMaximumFling = configuration.getScaledMaximumFlingVelocity();
-
-        // Compute the inverse of the density squared.
-        DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
-
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
-
-        setScrollBarStyle(super.getScrollBarStyle());
-        // Initially use a size of two, since the user is likely to only hold
-        // down two keys at a time (shift + another key)
-        mKeysPressed = new Vector<Integer>(2);
-        mHTML5VideoViewProxy = null ;
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return true;
-    }
-
-    /**
-     * Adds accessibility APIs to JavaScript.
-     *
-     * Note: This method is responsible to performing the necessary
-     *       check if the accessibility APIs should be exposed.
-     */
-    private void addAccessibilityApisToJavaScript() {
-        if (AccessibilityManager.getInstance(mContext).isEnabled()
-                && getSettings().getJavaScriptEnabled()) {
-            // exposing the TTS for now ...
-            final Context ctx = getContext();
-            if (ctx != null) {
-                final String packageName = ctx.getPackageName();
-                if (packageName != null) {
-                    mTextToSpeech = new TextToSpeech(getContext(), null, null,
-                            packageName + ".**webview**", true);
-                    addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes accessibility APIs from JavaScript.
-     */
-    private void removeAccessibilityApisFromJavaScript() {
-        // exposing the TTS for now ...
-        if (mTextToSpeech != null) {
-            removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
-            mTextToSpeech.shutdown();
-            mTextToSpeech = null;
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setScrollable(isScrollableForAccessibility());
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setScrollable(isScrollableForAccessibility());
-        event.setScrollX(mScrollX);
-        event.setScrollY(mScrollY);
-        final int convertedContentWidth = contentToViewX(getContentWidth());
-        final int adjustedViewWidth = getWidth() - mPaddingLeft - mPaddingRight;
-        event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
-        final int convertedContentHeight = contentToViewY(getContentHeight());
-        final int adjustedViewHeight = getHeight() - mPaddingTop - mPaddingBottom;
-        event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
-    }
-
-    private boolean isScrollableForAccessibility() {
-        return (contentToViewX(getContentWidth()) > getWidth() - mPaddingLeft - mPaddingRight
-                || contentToViewY(getContentHeight()) > getHeight() - mPaddingTop - mPaddingBottom);
-    }
-
-    @Override
-    public void setOverScrollMode(int mode) {
-        super.setOverScrollMode(mode);
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mOverScrollGlow == null) {
-                mOverScrollGlow = new OverScrollGlow(this);
-            }
-        } else {
-            mOverScrollGlow = null;
-        }
-    }
-
-    /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
-        final float density = mContext.getResources().getDisplayMetrics().density
-                * 100 / zoomDensity;
-        updateDefaultZoomDensity(density);
-    }
-
-    /* package */ void updateDefaultZoomDensity(float density) {
-        mNavSlop = (int) (16 * density);
-        mZoomManager.updateDefaultZoomDensity(density);
-    }
-
-    /* package */ boolean onSavePassword(String schemePlusHost, String username,
-            String password, final Message resumeMsg) {
-       boolean rVal = false;
-       if (resumeMsg == null) {
-           // null resumeMsg implies saving password silently
-           mDatabase.setUsernamePassword(schemePlusHost, username, password);
-       } else {
-            final Message remember = mPrivateHandler.obtainMessage(
-                    REMEMBER_PASSWORD);
-            remember.getData().putString("host", schemePlusHost);
-            remember.getData().putString("username", username);
-            remember.getData().putString("password", password);
-            remember.obj = resumeMsg;
-
-            final Message neverRemember = mPrivateHandler.obtainMessage(
-                    NEVER_REMEMBER_PASSWORD);
-            neverRemember.getData().putString("host", schemePlusHost);
-            neverRemember.getData().putString("username", username);
-            neverRemember.getData().putString("password", password);
-            neverRemember.obj = resumeMsg;
-
-            new AlertDialog.Builder(getContext())
-                    .setTitle(com.android.internal.R.string.save_password_label)
-                    .setMessage(com.android.internal.R.string.save_password_message)
-                    .setPositiveButton(com.android.internal.R.string.save_password_notnow,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            resumeMsg.sendToTarget();
-                        }
-                    })
-                    .setNeutralButton(com.android.internal.R.string.save_password_remember,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            remember.sendToTarget();
-                        }
-                    })
-                    .setNegativeButton(com.android.internal.R.string.save_password_never,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            neverRemember.sendToTarget();
-                        }
-                    })
-                    .setOnCancelListener(new OnCancelListener() {
-                        @Override
-                        public void onCancel(DialogInterface dialog) {
-                            resumeMsg.sendToTarget();
-                        }
-                    }).show();
-            // Return true so that WebViewCore will pause while the dialog is
-            // up.
-            rVal = true;
-        }
-       return rVal;
-    }
-
-    @Override
-    public void setScrollBarStyle(int style) {
-        if (style == View.SCROLLBARS_INSIDE_INSET
-                || style == View.SCROLLBARS_OUTSIDE_INSET) {
-            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
-        } else {
-            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
-        }
-        super.setScrollBarStyle(style);
+        ensureProviderCreated();
+        mProvider.init(javaScriptInterfaces, privateBrowsing);
     }
 
     /**
@@ -1789,7 +488,7 @@
      */
     public void setHorizontalScrollbarOverlay(boolean overlay) {
         checkThread();
-        mOverlayHorizontalScrollbar = overlay;
+        mProvider.setHorizontalScrollbarOverlay(overlay);
     }
 
     /**
@@ -1798,7 +497,7 @@
      */
     public void setVerticalScrollbarOverlay(boolean overlay) {
         checkThread();
-        mOverlayVerticalScrollbar = overlay;
+        mProvider.setVerticalScrollbarOverlay(overlay);
     }
 
     /**
@@ -1807,7 +506,7 @@
      */
     public boolean overlayHorizontalScrollbar() {
         checkThread();
-        return mOverlayHorizontalScrollbar;
+        return mProvider.overlayHorizontalScrollbar();
     }
 
     /**
@@ -1816,80 +515,17 @@
      */
     public boolean overlayVerticalScrollbar() {
         checkThread();
-        return mOverlayVerticalScrollbar;
-    }
-
-    /*
-     * Return the width of the view where the content of WebView should render
-     * to.
-     * Note: this can be called from WebCoreThread.
-     */
-    /* package */ int getViewWidth() {
-        if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
-            return getWidth();
-        } else {
-            return Math.max(0, getWidth() - getVerticalScrollbarWidth());
-        }
-    }
-
-    /**
-     * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
-     * scrolling
-     * @hide
-     */
-    protected int getTitleHeight() {
-        return mTitleBar != null ? mTitleBar.getHeight() : 0;
+        return mProvider.overlayVerticalScrollbar();
     }
 
     /**
      * Return the visible height (in pixels) of the embedded title bar (if any).
      *
-     * @return This method is obsolete and always returns 0.
      * @deprecated This method is now obsolete.
      */
-    @Deprecated
     public int getVisibleTitleHeight() {
-        // Actually, this method returns the height of the embedded title bar if one is set via the
-        // hidden setEmbeddedTitleBar method.
         checkThread();
-        return getVisibleTitleHeightImpl();
-    }
-
-    private int getVisibleTitleHeightImpl() {
-        // need to restrict mScrollY due to over scroll
-        return Math.max(getTitleHeight() - Math.max(0, mScrollY),
-                getOverlappingActionModeHeight());
-    }
-
-    private int mCachedOverlappingActionModeHeight = -1;
-
-    private int getOverlappingActionModeHeight() {
-        if (mFindCallback == null) {
-            return 0;
-        }
-        if (mCachedOverlappingActionModeHeight < 0) {
-            getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
-            mCachedOverlappingActionModeHeight = Math.max(0,
-                    mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
-        }
-        return mCachedOverlappingActionModeHeight;
-    }
-
-    /*
-     * Return the height of the view where the content of WebView should render
-     * to.  Note that this excludes mTitleBar, if there is one.
-     * Note: this can be called from WebCoreThread.
-     */
-    /* package */ int getViewHeight() {
-        return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
-    }
-
-    int getViewHeightWithTitle() {
-        int height = getHeight();
-        if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
-            height -= getHorizontalScrollbarHeight();
-        }
-        return height;
+        return mProvider.getVisibleTitleHeight();
     }
 
     /**
@@ -1898,7 +534,7 @@
      */
     public SslCertificate getCertificate() {
         checkThread();
-        return mCertificate;
+        return mProvider.getCertificate();
     }
 
     /**
@@ -1906,11 +542,7 @@
      */
     public void setCertificate(SslCertificate certificate) {
         checkThread();
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "setCertificate=" + certificate);
-        }
-        // here, the certificate can be null (if the site is not secure)
-        mCertificate = certificate;
+        mProvider.setCertificate(certificate);
     }
 
     //-------------------------------------------------------------------------
@@ -1926,7 +558,7 @@
      */
     public void savePassword(String host, String username, String password) {
         checkThread();
-        mDatabase.setUsernamePassword(host, username, password);
+        mProvider.savePassword(host, username, password);
     }
 
     /**
@@ -1941,7 +573,7 @@
     public void setHttpAuthUsernamePassword(String host, String realm,
             String username, String password) {
         checkThread();
-        mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
+        mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
     }
 
     /**
@@ -1955,38 +587,7 @@
      */
     public String[] getHttpAuthUsernamePassword(String host, String realm) {
         checkThread();
-        return mDatabase.getHttpAuthUsernamePassword(host, realm);
-    }
-
-    /**
-     * Remove Find or Select ActionModes, if active.
-     */
-    private void clearActionModes() {
-        if (mSelectCallback != null) {
-            mSelectCallback.finish();
-        }
-        if (mFindCallback != null) {
-            mFindCallback.finish();
-        }
-    }
-
-    /**
-     * Called to clear state when moving from one page to another, or changing
-     * in some other way that makes elements associated with the current page
-     * (such as WebTextView or ActionModes) no longer relevant.
-     */
-    private void clearHelpers() {
-        clearTextEntry();
-        clearActionModes();
-        dismissFullScreenMode();
-        cancelSelectDialog();
-    }
-
-    private void cancelSelectDialog() {
-        if (mListBoxDialog != null) {
-            mListBoxDialog.cancel();
-            mListBoxDialog = null;
-        }
+        return mProvider.getHttpAuthUsernamePassword(host, realm);
     }
 
     /**
@@ -1996,45 +597,7 @@
      */
     public void destroy() {
         checkThread();
-        destroyImpl();
-    }
-
-    private void destroyImpl() {
-        clearHelpers();
-        if (mListBoxDialog != null) {
-            mListBoxDialog.dismiss();
-            mListBoxDialog = null;
-        }
-        // remove so that it doesn't cause events
-        if (mWebTextView != null) {
-            mWebTextView.remove();
-            mWebTextView = null;
-        }
-        if (mNativeClass != 0) nativeStopGL();
-        if (mWebViewCore != null) {
-            // Set the handlers to null before destroying WebViewCore so no
-            // more messages will be posted.
-            mCallbackProxy.setWebViewClient(null);
-            mCallbackProxy.setWebChromeClient(null);
-            // Tell WebViewCore to destroy itself
-            synchronized (this) {
-                WebViewCore webViewCore = mWebViewCore;
-                mWebViewCore = null; // prevent using partial webViewCore
-                webViewCore.destroy();
-            }
-            // Remove any pending messages that might not be serviced yet.
-            mPrivateHandler.removeCallbacksAndMessages(null);
-            mCallbackProxy.removeCallbacksAndMessages(null);
-            // Wake up the WebCore thread just in case it is waiting for a
-            // JavaScript dialog.
-            synchronized (mCallbackProxy) {
-                mCallbackProxy.notify();
-            }
-        }
-        if (mNativeClass != 0) {
-            nativeDestroy();
-            mNativeClass = 0;
-        }
+        mProvider.destroy();
     }
 
     /**
@@ -2046,12 +609,7 @@
     @Deprecated
     public static void enablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
-            sNotificationsEnabled = true;
-            Context context = JniUtil.getContext();
-            if (context != null)
-                setupProxyListener(context);
-        }
+        getFactory().getStatics().setPlatformNotificationsEnabled(true);
     }
 
     /**
@@ -2063,24 +621,7 @@
     @Deprecated
     public static void disablePlatformNotifications() {
         checkThread();
-        synchronized (WebView.class) {
-            sNotificationsEnabled = false;
-            Context context = JniUtil.getContext();
-            if (context != null)
-                disableProxyListener(context);
-        }
-    }
-
-    /**
-     * Sets JavaScript engine flags.
-     *
-     * @param flags JS engine flags in a String
-     *
-     * @hide This is an implementation detail.
-     */
-    public void setJsFlags(String flags) {
-        checkThread();
-        mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
+        getFactory().getStatics().setPlatformNotificationsEnabled(false);
     }
 
     /**
@@ -2091,22 +632,10 @@
      */
     public void setNetworkAvailable(boolean networkUp) {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
-                networkUp ? 1 : 0, 0);
+        mProvider.setNetworkAvailable(networkUp);
     }
 
     /**
-     * Inform WebView about the current network type.
-     * {@hide}
-     */
-    public void setNetworkType(String type, String subtype) {
-        checkThread();
-        Map<String, String> map = new HashMap<String, String>();
-        map.put("type", type);
-        map.put("subtype", subtype);
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
-    }
-    /**
      * Save the state of this WebView used in
      * {@link android.app.Activity#onSaveInstanceState}. Please note that this
      * method no longer stores the display data for this WebView. The previous
@@ -2121,49 +650,7 @@
      */
     public WebBackForwardList saveState(Bundle outState) {
         checkThread();
-        if (outState == null) {
-            return null;
-        }
-        // We grab a copy of the back/forward list because a client of WebView
-        // may have invalidated the history list by calling clearHistory.
-        WebBackForwardList list = copyBackForwardList();
-        final int currentIndex = list.getCurrentIndex();
-        final int size = list.getSize();
-        // We should fail saving the state if the list is empty or the index is
-        // not in a valid range.
-        if (currentIndex < 0 || currentIndex >= size || size == 0) {
-            return null;
-        }
-        outState.putInt("index", currentIndex);
-        // FIXME: This should just be a byte[][] instead of ArrayList but
-        // Parcel.java does not have the code to handle multi-dimensional
-        // arrays.
-        ArrayList<byte[]> history = new ArrayList<byte[]>(size);
-        for (int i = 0; i < size; i++) {
-            WebHistoryItem item = list.getItemAtIndex(i);
-            if (null == item) {
-                // FIXME: this shouldn't happen
-                // need to determine how item got set to null
-                Log.w(LOGTAG, "saveState: Unexpected null history item.");
-                return null;
-            }
-            byte[] data = item.getFlattenedData();
-            if (data == null) {
-                // It would be very odd to not have any data for a given history
-                // item. And we will fail to rebuild the history list without
-                // flattened data.
-                return null;
-            }
-            history.add(data);
-        }
-        outState.putSerializable("history", history);
-        if (mCertificate != null) {
-            outState.putBundle("certificate",
-                               SslCertificate.saveState(mCertificate));
-        }
-        outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
-        mZoomManager.saveZoomState(outState);
-        return list;
+        return mProvider.saveState(outState);
     }
 
     /**
@@ -2178,59 +665,7 @@
     @Deprecated
     public boolean savePicture(Bundle b, final File dest) {
         checkThread();
-        if (dest == null || b == null) {
-            return false;
-        }
-        final Picture p = capturePicture();
-        // Use a temporary file while writing to ensure the destination file
-        // contains valid data.
-        final File temp = new File(dest.getPath() + ".writing");
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                FileOutputStream out = null;
-                try {
-                    out = new FileOutputStream(temp);
-                    p.writeToStream(out);
-                    // Writing the picture succeeded, rename the temporary file
-                    // to the destination.
-                    temp.renameTo(dest);
-                } catch (Exception e) {
-                    // too late to do anything about it.
-                } finally {
-                    if (out != null) {
-                        try {
-                            out.close();
-                        } catch (Exception e) {
-                            // Can't do anything about that
-                        }
-                    }
-                    temp.delete();
-                }
-            }
-        }).start();
-        // now update the bundle
-        b.putInt("scrollX", mScrollX);
-        b.putInt("scrollY", mScrollY);
-        mZoomManager.saveZoomState(b);
-        return true;
-    }
-
-    private void restoreHistoryPictureFields(Picture p, Bundle b) {
-        int sx = b.getInt("scrollX", 0);
-        int sy = b.getInt("scrollY", 0);
-
-        mDrawHistory = true;
-        mHistoryPicture = p;
-
-        mScrollX = sx;
-        mScrollY = sy;
-        mZoomManager.restoreZoomState(b);
-        final float scale = mZoomManager.getScale();
-        mHistoryWidth = Math.round(p.getWidth() * scale);
-        mHistoryHeight = Math.round(p.getHeight() * scale);
-
-        invalidate();
+        return mProvider.savePicture(b, dest);
     }
 
     /**
@@ -2246,91 +681,7 @@
     @Deprecated
     public boolean restorePicture(Bundle b, File src) {
         checkThread();
-        if (src == null || b == null) {
-            return false;
-        }
-        if (!src.exists()) {
-            return false;
-        }
-        try {
-            final FileInputStream in = new FileInputStream(src);
-            final Bundle copy = new Bundle(b);
-            new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        final Picture p = Picture.createFromStream(in);
-                        if (p != null) {
-                            // Post a runnable on the main thread to update the
-                            // history picture fields.
-                            mPrivateHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    restoreHistoryPictureFields(p, copy);
-                                }
-                            });
-                        }
-                    } finally {
-                        try {
-                            in.close();
-                        } catch (Exception e) {
-                            // Nothing we can do now.
-                        }
-                    }
-                }
-            }).start();
-        } catch (FileNotFoundException e){
-            e.printStackTrace();
-        }
-        return true;
-    }
-
-    /**
-     * Saves the view data to the output stream. The output is highly
-     * version specific, and may not be able to be loaded by newer versions
-     * of WebView.
-     * @param stream The {@link OutputStream} to save to
-     * @return True if saved successfully
-     * @hide
-     */
-    public boolean saveViewState(OutputStream stream) {
-        try {
-            return ViewStateSerializer.serializeViewState(stream, this);
-        } catch (IOException e) {
-            Log.w(LOGTAG, "Failed to saveViewState", e);
-        }
-        return false;
-    }
-
-    /**
-     * Loads the view data from the input stream. See
-     * {@link #saveViewState(OutputStream)} for more information.
-     * @param stream The {@link InputStream} to load from
-     * @return True if loaded successfully
-     * @hide
-     */
-    public boolean loadViewState(InputStream stream) {
-        try {
-            mLoadedPicture = ViewStateSerializer.deserializeViewState(stream, this);
-            mBlockWebkitViewMessages = true;
-            setNewPicture(mLoadedPicture, true);
-            mLoadedPicture.mViewState = null;
-            return true;
-        } catch (IOException e) {
-            Log.w(LOGTAG, "Failed to loadViewState", e);
-        }
-        return false;
-    }
-
-    /**
-     * Clears the view state set with {@link #loadViewState(InputStream)}.
-     * This WebView will then switch to showing the content from webkit
-     * @hide
-     */
-    public void clearViewState() {
-        mBlockWebkitViewMessages = false;
-        mLoadedPicture = null;
-        invalidate();
+        return mProvider.restorePicture(b, src);
     }
 
     /**
@@ -2349,55 +700,7 @@
      */
     public WebBackForwardList restoreState(Bundle inState) {
         checkThread();
-        WebBackForwardList returnList = null;
-        if (inState == null) {
-            return returnList;
-        }
-        if (inState.containsKey("index") && inState.containsKey("history")) {
-            mCertificate = SslCertificate.restoreState(
-                inState.getBundle("certificate"));
-
-            final WebBackForwardList list = mCallbackProxy.getBackForwardList();
-            final int index = inState.getInt("index");
-            // We can't use a clone of the list because we need to modify the
-            // shared copy, so synchronize instead to prevent concurrent
-            // modifications.
-            synchronized (list) {
-                final List<byte[]> history =
-                        (List<byte[]>) inState.getSerializable("history");
-                final int size = history.size();
-                // Check the index bounds so we don't crash in native code while
-                // restoring the history index.
-                if (index < 0 || index >= size) {
-                    return null;
-                }
-                for (int i = 0; i < size; i++) {
-                    byte[] data = history.remove(0);
-                    if (data == null) {
-                        // If we somehow have null data, we cannot reconstruct
-                        // the item and thus our history list cannot be rebuilt.
-                        return null;
-                    }
-                    WebHistoryItem item = new WebHistoryItem(data);
-                    list.addHistoryItem(item);
-                }
-                // Grab the most recent copy to return to the caller.
-                returnList = copyBackForwardList();
-                // Update the copy to have the correct index.
-                returnList.setCurrentIndex(index);
-            }
-            // Restore private browsing setting.
-            if (inState.getBoolean("privateBrowsingEnabled")) {
-                getSettings().setPrivateBrowsingEnabled(true);
-            }
-            mZoomManager.restoreZoomState(inState);
-            // Remove all pending messages because we are restoring previous
-            // state.
-            mWebViewCore.removeMessages();
-            // Send a restore state message.
-            mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
-        }
-        return returnList;
+        return mProvider.restoreState(inState);
     }
 
     /**
@@ -2412,16 +715,7 @@
      */
     public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
         checkThread();
-        loadUrlImpl(url, additionalHttpHeaders);
-    }
-
-    private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
-        switchOutDrawHistory();
-        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
-        arg.mUrl = url;
-        arg.mExtraHeaders = extraHeaders;
-        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
-        clearHelpers();
+        mProvider.loadUrl(url, additionalHttpHeaders);
     }
 
     /**
@@ -2430,14 +724,7 @@
      */
     public void loadUrl(String url) {
         checkThread();
-        loadUrlImpl(url);
-    }
-
-    private void loadUrlImpl(String url) {
-        if (url == null) {
-            return;
-        }
-        loadUrlImpl(url, null);
+        mProvider.loadUrl(url);
     }
 
     /**
@@ -2450,16 +737,7 @@
      */
     public void postUrl(String url, byte[] postData) {
         checkThread();
-        if (URLUtil.isNetworkUrl(url)) {
-            switchOutDrawHistory();
-            WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
-            arg.mUrl = url;
-            arg.mPostData = postData;
-            mWebViewCore.sendMessage(EventHub.POST_URL, arg);
-            clearHelpers();
-        } else {
-            loadUrlImpl(url);
-        }
+        mProvider.postUrl(url, postData);
     }
 
     /**
@@ -2490,18 +768,7 @@
      */
     public void loadData(String data, String mimeType, String encoding) {
         checkThread();
-        loadDataImpl(data, mimeType, encoding);
-    }
-
-    private void loadDataImpl(String data, String mimeType, String encoding) {
-        StringBuilder dataUrl = new StringBuilder("data:");
-        dataUrl.append(mimeType);
-        if ("base64".equals(encoding)) {
-            dataUrl.append(";base64");
-        }
-        dataUrl.append(",");
-        dataUrl.append(data);
-        loadUrlImpl(dataUrl.toString());
+        mProvider.loadData(data, mimeType, encoding);
     }
 
     /**
@@ -2529,20 +796,7 @@
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String historyUrl) {
         checkThread();
-
-        if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
-            loadDataImpl(data, mimeType, encoding);
-            return;
-        }
-        switchOutDrawHistory();
-        WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
-        arg.mBaseUrl = baseUrl;
-        arg.mData = data;
-        arg.mMimeType = mimeType;
-        arg.mEncoding = encoding;
-        arg.mHistoryUrl = historyUrl;
-        mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
-        clearHelpers();
+        mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
     }
 
     /**
@@ -2552,20 +806,7 @@
      */
     public void saveWebArchive(String filename) {
         checkThread();
-        saveWebArchiveImpl(filename, false, null);
-    }
-
-    /* package */ static class SaveWebArchiveMessage {
-        SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
-            mBasename = basename;
-            mAutoname = autoname;
-            mCallback = callback;
-        }
-
-        /* package */ final String mBasename;
-        /* package */ final boolean mAutoname;
-        /* package */ final ValueCallback<String> mCallback;
-        /* package */ String mResultFile;
+        mProvider.saveWebArchive(filename);
     }
 
     /**
@@ -2582,13 +823,7 @@
      */
     public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
         checkThread();
-        saveWebArchiveImpl(basename, autoname, callback);
-    }
-
-    private void saveWebArchiveImpl(String basename, boolean autoname,
-            ValueCallback<String> callback) {
-        mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
-            new SaveWebArchiveMessage(basename, autoname, callback));
+        mProvider.saveWebArchive(basename, autoname, callback);
     }
 
     /**
@@ -2596,10 +831,7 @@
      */
     public void stopLoading() {
         checkThread();
-        // TODO: should we clear all the messages in the queue before sending
-        // STOP_LOADING?
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.STOP_LOADING);
+        mProvider.stopLoading();
     }
 
     /**
@@ -2607,9 +839,7 @@
      */
     public void reload() {
         checkThread();
-        clearHelpers();
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.RELOAD);
+        mProvider.reload();
     }
 
     /**
@@ -2618,14 +848,7 @@
      */
     public boolean canGoBack() {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() > 0;
-            }
-        }
+        return mProvider.canGoBack();
     }
 
     /**
@@ -2633,7 +856,7 @@
      */
     public void goBack() {
         checkThread();
-        goBackOrForwardImpl(-1);
+        mProvider.goBack();
     }
 
     /**
@@ -2642,14 +865,7 @@
      */
     public boolean canGoForward() {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() < l.getSize() - 1;
-            }
-        }
+        return mProvider.canGoForward();
     }
 
     /**
@@ -2657,7 +873,7 @@
      */
     public void goForward() {
         checkThread();
-        goBackOrForwardImpl(1);
+        mProvider.goForward();
     }
 
     /**
@@ -2668,15 +884,7 @@
      */
     public boolean canGoBackOrForward(int steps) {
         checkThread();
-        WebBackForwardList l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                int newIndex = l.getCurrentIndex() + steps;
-                return newIndex >= 0 && newIndex < l.getSize();
-            }
-        }
+        return mProvider.canGoBackOrForward(steps);
     }
 
     /**
@@ -2688,19 +896,7 @@
      */
     public void goBackOrForward(int steps) {
         checkThread();
-        goBackOrForwardImpl(steps);
-    }
-
-    private void goBackOrForwardImpl(int steps) {
-        goBackOrForward(steps, false);
-    }
-
-    private void goBackOrForward(int steps, boolean ignoreSnapshot) {
-        if (steps != 0) {
-            clearHelpers();
-            mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
-                    ignoreSnapshot ? 1 : 0);
-        }
+        mProvider.goBackOrForward(steps);
     }
 
     /**
@@ -2708,20 +904,7 @@
      */
     public boolean isPrivateBrowsingEnabled() {
         checkThread();
-        return getSettings().isPrivateBrowsingEnabled();
-    }
-
-    private void startPrivateBrowsing() {
-        getSettings().setPrivateBrowsingEnabled(true);
-    }
-
-    private boolean extendScroll(int y) {
-        int finalY = mScroller.getFinalY();
-        int newY = pinLocY(finalY + y);
-        if (newY == finalY) return false;
-        mScroller.setFinalY(newY);
-        mScroller.extendDuration(computeDuration(0, y));
-        return true;
+        return mProvider.isPrivateBrowsingEnabled();
     }
 
     /**
@@ -2731,24 +914,7 @@
      */
     public boolean pageUp(boolean top) {
         checkThread();
-        if (mNativeClass == 0) {
-            return false;
-        }
-        nativeClearCursor(); // start next trackball movement from page edge
-        if (top) {
-            // go to the top of the document
-            return pinScrollTo(mScrollX, 0, true, 0);
-        }
-        // Page up
-        int h = getHeight();
-        int y;
-        if (h > 2 * PAGE_SCROLL_OVERLAP) {
-            y = -h + PAGE_SCROLL_OVERLAP;
-        } else {
-            y = -h / 2;
-        }
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
-                : extendScroll(y);
+        return mProvider.pageUp(top);
     }
 
     /**
@@ -2758,23 +924,7 @@
      */
     public boolean pageDown(boolean bottom) {
         checkThread();
-        if (mNativeClass == 0) {
-            return false;
-        }
-        nativeClearCursor(); // start next trackball movement from page edge
-        if (bottom) {
-            return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
-        }
-        // Page down.
-        int h = getHeight();
-        int y;
-        if (h > 2 * PAGE_SCROLL_OVERLAP) {
-            y = h - PAGE_SCROLL_OVERLAP;
-        } else {
-            y = h / 2;
-        }
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
-                : extendScroll(y);
+        return mProvider.pageDown(bottom);
     }
 
     /**
@@ -2783,10 +933,7 @@
      */
     public void clearView() {
         checkThread();
-        mContentWidth = 0;
-        mContentHeight = 0;
-        setBaseLayer(0, null, false, false);
-        mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
+        mProvider.clearView();
     }
 
     /**
@@ -2800,29 +947,7 @@
      */
     public Picture capturePicture() {
         checkThread();
-        if (mNativeClass == 0) return null;
-        Picture result = new Picture();
-        nativeCopyBaseContentToPicture(result);
-        return result;
-    }
-
-    /**
-     *  Return true if the browser is displaying a TextView for text input.
-     */
-    private boolean inEditingMode() {
-        return mWebTextView != null && mWebTextView.getParent() != null;
-    }
-
-    /**
-     * Remove the WebTextView.
-     */
-    private void clearTextEntry() {
-        if (inEditingMode()) {
-            mWebTextView.remove();
-        } else {
-            // The keyboard may be open with the WebView as the served view
-            hideSoftKeyboard();
-        }
+        return mProvider.capturePicture();
     }
 
     /**
@@ -2831,16 +956,7 @@
      */
     public float getScale() {
         checkThread();
-        return mZoomManager.getScale();
-    }
-
-    /**
-     * Compute the reading level scale of the WebView
-     * @param scale The current scale.
-     * @return The reading level scale.
-     */
-    /*package*/ float computeReadingLevelScale(float scale) {
-        return mZoomManager.computeReadingLevelScale(scale);
+        return mProvider.getScale();
     }
 
     /**
@@ -2855,7 +971,7 @@
      */
     public void setInitialScale(int scaleInPercent) {
         checkThread();
-        mZoomManager.setInitialScaleInPercent(scaleInPercent);
+        mProvider.setInitialScale(scaleInPercent);
     }
 
     /**
@@ -2865,12 +981,7 @@
      */
     public void invokeZoomPicker() {
         checkThread();
-        if (!getSettings().supportZoom()) {
-            Log.w(LOGTAG, "This WebView doesn't support zoom.");
-            return;
-        }
-        clearHelpers();
-        mZoomManager.invokeZoomPicker();
+        mProvider.invokeZoomPicker();
     }
 
     /**
@@ -2893,101 +1004,9 @@
      */
     public HitTestResult getHitTestResult() {
         checkThread();
-        return hitTestResult(mInitialHitTestResult);
+        return mProvider.getHitTestResult();
     }
 
-    private HitTestResult hitTestResult(HitTestResult fallback) {
-        if (mNativeClass == 0 || sDisableNavcache) {
-            return fallback;
-        }
-
-        HitTestResult result = new HitTestResult();
-        if (nativeHasCursorNode()) {
-            if (nativeCursorIsTextInput()) {
-                result.setType(HitTestResult.EDIT_TEXT_TYPE);
-            } else {
-                String text = nativeCursorText();
-                if (text != null) {
-                    if (text.startsWith(SCHEME_TEL)) {
-                        result.setType(HitTestResult.PHONE_TYPE);
-                        result.setExtra(URLDecoder.decode(text
-                                .substring(SCHEME_TEL.length())));
-                    } else if (text.startsWith(SCHEME_MAILTO)) {
-                        result.setType(HitTestResult.EMAIL_TYPE);
-                        result.setExtra(text.substring(SCHEME_MAILTO.length()));
-                    } else if (text.startsWith(SCHEME_GEO)) {
-                        result.setType(HitTestResult.GEO_TYPE);
-                        result.setExtra(URLDecoder.decode(text
-                                .substring(SCHEME_GEO.length())));
-                    } else if (nativeCursorIsAnchor()) {
-                        result.setType(HitTestResult.SRC_ANCHOR_TYPE);
-                        result.setExtra(text);
-                    }
-                }
-            }
-        } else if (fallback != null) {
-            /* If webkit causes a rebuild while the long press is in progress,
-             * the cursor node may be reset, even if it is still around. This
-             * uses the cursor node saved when the touch began. Since the
-             * nativeImageURI below only changes the result if it is successful,
-             * this uses the data beneath the touch if available or the original
-             * tap data otherwise.
-             */
-            Log.v(LOGTAG, "hitTestResult use fallback");
-            result = fallback;
-        }
-        int type = result.getType();
-        if (type == HitTestResult.UNKNOWN_TYPE
-                || type == HitTestResult.SRC_ANCHOR_TYPE) {
-            // Now check to see if it is an image.
-            int contentX = viewToContentX(mLastTouchX + mScrollX);
-            int contentY = viewToContentY(mLastTouchY + mScrollY);
-            String text = nativeImageURI(contentX, contentY);
-            if (text != null) {
-                result.setType(type == HitTestResult.UNKNOWN_TYPE ?
-                        HitTestResult.IMAGE_TYPE :
-                        HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
-                result.setExtra(text);
-            }
-        }
-        return result;
-    }
-
-    int getBlockLeftEdge(int x, int y, float readingScale) {
-        if (!sDisableNavcache) {
-            return nativeGetBlockLeftEdge(x, y, readingScale);
-        }
-
-        float invReadingScale = 1.0f / readingScale;
-        int readingWidth = (int) (getViewWidth() * invReadingScale);
-        int left = NO_LEFTEDGE;
-        if (mFocusedNode != null) {
-            final int length = mFocusedNode.mEnclosingParentRects.length;
-            for (int i = 0; i < length; i++) {
-                Rect rect = mFocusedNode.mEnclosingParentRects[i];
-                if (rect.width() < mFocusedNode.mHitTestSlop) {
-                    // ignore bounding boxes that are too small
-                    continue;
-                } else if (left != NO_LEFTEDGE && rect.width() > readingWidth) {
-                    // stop when bounding box doesn't fit the screen width
-                    // at reading scale
-                    break;
-                }
-
-                left = rect.left;
-            }
-        }
-
-        return left;
-    }
-
-    // Called by JNI when the DOM has changed the focus.  Clear the focus so
-    // that new keys will go to the newly focused field
-    private void domChangedFocus() {
-        if (inEditingMode()) {
-            mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
-        }
-    }
     /**
      * Request the anchor or image element URL at the last tapped point.
      * If hrefMsg is null, this method returns immediately and does not
@@ -3004,32 +1023,7 @@
      */
     public void requestFocusNodeHref(Message hrefMsg) {
         checkThread();
-        if (hrefMsg == null) {
-            return;
-        }
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
-                && mFocusedNode.mHitTestY == contentY) {
-            hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
-            hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
-            hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
-            hrefMsg.sendToTarget();
-            return;
-        }
-        if (nativeHasCursorNode()) {
-            Rect cursorBounds = nativeGetCursorRingBounds();
-            if (!cursorBounds.contains(contentX, contentY)) {
-                int slop = viewToContentDimension(mNavSlop);
-                cursorBounds.inset(-slop, -slop);
-                if (cursorBounds.contains(contentX, contentY)) {
-                    contentX = cursorBounds.centerX();
-                    contentY = cursorBounds.centerY();
-                }
-            }
-        }
-        mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
-                contentX, contentY, hrefMsg);
+        mProvider.requestFocusNodeHref(hrefMsg);
     }
 
     /**
@@ -3041,503 +1035,7 @@
      */
     public void requestImageRef(Message msg) {
         checkThread();
-        if (0 == mNativeClass) return; // client isn't initialized
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        String ref = nativeImageURI(contentX, contentY);
-        Bundle data = msg.getData();
-        data.putString("url", ref);
-        msg.setData(data);
-        msg.sendToTarget();
-    }
-
-    static int pinLoc(int x, int viewMax, int docMax) {
-//        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
-        if (docMax < viewMax) {   // the doc has room on the sides for "blank"
-            // pin the short document to the top/left of the screen
-            x = 0;
-//            Log.d(LOGTAG, "--- center " + x);
-        } else if (x < 0) {
-            x = 0;
-//            Log.d(LOGTAG, "--- zero");
-        } else if (x + viewMax > docMax) {
-            x = docMax - viewMax;
-//            Log.d(LOGTAG, "--- pin " + x);
-        }
-        return x;
-    }
-
-    // Expects x in view coordinates
-    int pinLocX(int x) {
-        if (mInOverScrollMode) return x;
-        return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
-    }
-
-    // Expects y in view coordinates
-    int pinLocY(int y) {
-        if (mInOverScrollMode) return y;
-        return pinLoc(y, getViewHeightWithTitle(),
-                      computeRealVerticalScrollRange() + getTitleHeight());
-    }
-
-    /**
-     * A title bar which is embedded in this WebView, and scrolls along with it
-     * vertically, but not horizontally.
-     */
-    private View mTitleBar;
-
-    /**
-     * the title bar rendering gravity
-     */
-    private int mTitleGravity;
-
-    /**
-     * Add or remove a title bar to be embedded into the WebView, and scroll
-     * along with it vertically, while remaining in view horizontally. Pass
-     * null to remove the title bar from the WebView, and return to drawing
-     * the WebView normally without translating to account for the title bar.
-     * @hide
-     */
-    public void setEmbeddedTitleBar(View v) {
-        if (mTitleBar == v) return;
-        if (mTitleBar != null) {
-            removeView(mTitleBar);
-        }
-        if (null != v) {
-            addView(v, new AbsoluteLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
-        }
-        mTitleBar = v;
-    }
-
-    /**
-     * Set where to render the embedded title bar
-     * NO_GRAVITY at the top of the page
-     * TOP        at the top of the screen
-     * @hide
-     */
-    public void setTitleBarGravity(int gravity) {
-        mTitleGravity = gravity;
-        // force refresh
-        invalidate();
-    }
-
-    /**
-     * Given a distance in view space, convert it to content space. Note: this
-     * does not reflect translation, just scaling, so this should not be called
-     * with coordinates, but should be called for dimensions like width or
-     * height.
-     */
-    private int viewToContentDimension(int d) {
-        return Math.round(d * mZoomManager.getInvScale());
-    }
-
-    /**
-     * Given an x coordinate in view space, convert it to content space.  Also
-     * may be used for absolute heights (such as for the WebTextView's
-     * textSize, which is unaffected by the height of the title bar).
-     */
-    /*package*/ int viewToContentX(int x) {
-        return viewToContentDimension(x);
-    }
-
-    /**
-     * Given a y coordinate in view space, convert it to content space.
-     * Takes into account the height of the title bar if there is one
-     * embedded into the WebView.
-     */
-    /*package*/ int viewToContentY(int y) {
-        return viewToContentDimension(y - getTitleHeight());
-    }
-
-    /**
-     * Given a x coordinate in view space, convert it to content space.
-     * Returns the result as a float.
-     */
-    private float viewToContentXf(int x) {
-        return x * mZoomManager.getInvScale();
-    }
-
-    /**
-     * Given a y coordinate in view space, convert it to content space.
-     * Takes into account the height of the title bar if there is one
-     * embedded into the WebView. Returns the result as a float.
-     */
-    private float viewToContentYf(int y) {
-        return (y - getTitleHeight()) * mZoomManager.getInvScale();
-    }
-
-    /**
-     * Given a distance in content space, convert it to view space. Note: this
-     * does not reflect translation, just scaling, so this should not be called
-     * with coordinates, but should be called for dimensions like width or
-     * height.
-     */
-    /*package*/ int contentToViewDimension(int d) {
-        return Math.round(d * mZoomManager.getScale());
-    }
-
-    /**
-     * Given an x coordinate in content space, convert it to view
-     * space.
-     */
-    /*package*/ int contentToViewX(int x) {
-        return contentToViewDimension(x);
-    }
-
-    /**
-     * Given a y coordinate in content space, convert it to view
-     * space.  Takes into account the height of the title bar.
-     */
-    /*package*/ int contentToViewY(int y) {
-        return contentToViewDimension(y) + getTitleHeight();
-    }
-
-    private Rect contentToViewRect(Rect x) {
-        return new Rect(contentToViewX(x.left), contentToViewY(x.top),
-                        contentToViewX(x.right), contentToViewY(x.bottom));
-    }
-
-    /*  To invalidate a rectangle in content coordinates, we need to transform
-        the rect into view coordinates, so we can then call invalidate(...).
-
-        Normally, we would just call contentToView[XY](...), which eventually
-        calls Math.round(coordinate * mActualScale). However, for invalidates,
-        we need to account for the slop that occurs with antialiasing. To
-        address that, we are a little more liberal in the size of the rect that
-        we invalidate.
-
-        This liberal calculation calls floor() for the top/left, and ceil() for
-        the bottom/right coordinates. This catches the possible extra pixels of
-        antialiasing that we might have missed with just round().
-     */
-
-    // Called by JNI to invalidate the View, given rectangle coordinates in
-    // content space
-    private void viewInvalidate(int l, int t, int r, int b) {
-        final float scale = mZoomManager.getScale();
-        final int dy = getTitleHeight();
-        invalidate((int)Math.floor(l * scale),
-                   (int)Math.floor(t * scale) + dy,
-                   (int)Math.ceil(r * scale),
-                   (int)Math.ceil(b * scale) + dy);
-    }
-
-    // Called by JNI to invalidate the View after a delay, given rectangle
-    // coordinates in content space
-    private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
-        final float scale = mZoomManager.getScale();
-        final int dy = getTitleHeight();
-        postInvalidateDelayed(delay,
-                              (int)Math.floor(l * scale),
-                              (int)Math.floor(t * scale) + dy,
-                              (int)Math.ceil(r * scale),
-                              (int)Math.ceil(b * scale) + dy);
-    }
-
-    private void invalidateContentRect(Rect r) {
-        viewInvalidate(r.left, r.top, r.right, r.bottom);
-    }
-
-    // stop the scroll animation, and don't let a subsequent fling add
-    // to the existing velocity
-    private void abortAnimation() {
-        mScroller.abortAnimation();
-        mLastVelocity = 0;
-    }
-
-    /* call from webcoreview.draw(), so we're still executing in the UI thread
-    */
-    private void recordNewContentSize(int w, int h, boolean updateLayout) {
-
-        // premature data from webkit, ignore
-        if ((w | h) == 0) {
-            return;
-        }
-
-        // don't abort a scroll animation if we didn't change anything
-        if (mContentWidth != w || mContentHeight != h) {
-            // record new dimensions
-            mContentWidth = w;
-            mContentHeight = h;
-            // If history Picture is drawn, don't update scroll. They will be
-            // updated when we get out of that mode.
-            if (!mDrawHistory) {
-                // repin our scroll, taking into account the new content size
-                updateScrollCoordinates(pinLocX(mScrollX), pinLocY(mScrollY));
-                if (!mScroller.isFinished()) {
-                    // We are in the middle of a scroll.  Repin the final scroll
-                    // position.
-                    mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
-                    mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
-                }
-            }
-        }
-        contentSizeChanged(updateLayout);
-    }
-
-    // Used to avoid sending many visible rect messages.
-    private Rect mLastVisibleRectSent = new Rect();
-    private Rect mLastGlobalRect = new Rect();
-    private Rect mVisibleRect = new Rect();
-    private Rect mGlobalVisibleRect = new Rect();
-    private Point mScrollOffset = new Point();
-
-    Rect sendOurVisibleRect() {
-        if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
-        calcOurContentVisibleRect(mVisibleRect);
-        // Rect.equals() checks for null input.
-        if (!mVisibleRect.equals(mLastVisibleRectSent)) {
-            if (!mBlockWebkitViewMessages) {
-                mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
-                mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
-                mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
-                        nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, mScrollOffset);
-            }
-            mLastVisibleRectSent.set(mVisibleRect);
-            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-        }
-        if (getGlobalVisibleRect(mGlobalVisibleRect)
-                && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
-                        + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
-                        + mGlobalVisibleRect.bottom);
-            }
-            // TODO: the global offset is only used by windowRect()
-            // in ChromeClientAndroid ; other clients such as touch
-            // and mouse events could return view + screen relative points.
-            if (!mBlockWebkitViewMessages) {
-                mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
-            }
-            mLastGlobalRect.set(mGlobalVisibleRect);
-        }
-        return mVisibleRect;
-    }
-
-    private Point mGlobalVisibleOffset = new Point();
-    // Sets r to be the visible rectangle of our webview in view coordinates
-    private void calcOurVisibleRect(Rect r) {
-        getGlobalVisibleRect(r, mGlobalVisibleOffset);
-        r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
-    }
-
-    // Sets r to be our visible rectangle in content coordinates
-    private void calcOurContentVisibleRect(Rect r) {
-        calcOurVisibleRect(r);
-        r.left = viewToContentX(r.left);
-        // viewToContentY will remove the total height of the title bar.  Add
-        // the visible height back in to account for the fact that if the title
-        // bar is partially visible, the part of the visible rect which is
-        // displaying our content is displaced by that amount.
-        r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
-        r.right = viewToContentX(r.right);
-        r.bottom = viewToContentY(r.bottom);
-    }
-
-    private Rect mContentVisibleRect = new Rect();
-    // Sets r to be our visible rectangle in content coordinates. We use this
-    // method on the native side to compute the position of the fixed layers.
-    // Uses floating coordinates (necessary to correctly place elements when
-    // the scale factor is not 1)
-    private void calcOurContentVisibleRectF(RectF r) {
-        calcOurVisibleRect(mContentVisibleRect);
-        r.left = viewToContentXf(mContentVisibleRect.left);
-        // viewToContentY will remove the total height of the title bar.  Add
-        // the visible height back in to account for the fact that if the title
-        // bar is partially visible, the part of the visible rect which is
-        // displaying our content is displaced by that amount.
-        r.top = viewToContentYf(mContentVisibleRect.top + getVisibleTitleHeightImpl());
-        r.right = viewToContentXf(mContentVisibleRect.right);
-        r.bottom = viewToContentYf(mContentVisibleRect.bottom);
-    }
-
-    static class ViewSizeData {
-        int mWidth;
-        int mHeight;
-        float mHeightWidthRatio;
-        int mActualViewHeight;
-        int mTextWrapWidth;
-        int mAnchorX;
-        int mAnchorY;
-        float mScale;
-        boolean mIgnoreHeight;
-    }
-
-    /**
-     * Compute unzoomed width and height, and if they differ from the last
-     * values we sent, send them to webkit (to be used as new viewport)
-     *
-     * @param force ensures that the message is sent to webkit even if the width
-     * or height has not changed since the last message
-     *
-     * @return true if new values were sent
-     */
-    boolean sendViewSizeZoom(boolean force) {
-        if (mBlockWebkitViewMessages) return false;
-        if (mZoomManager.isPreventingWebkitUpdates()) return false;
-
-        int viewWidth = getViewWidth();
-        int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
-        // This height could be fixed and be different from actual visible height.
-        int viewHeight = getViewHeightWithTitle() - getTitleHeight();
-        int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
-        // Make the ratio more accurate than (newHeight / newWidth), since the
-        // latter both are calculated and rounded.
-        float heightWidthRatio = (float) viewHeight / viewWidth;
-        /*
-         * Because the native side may have already done a layout before the
-         * View system was able to measure us, we have to send a height of 0 to
-         * remove excess whitespace when we grow our width. This will trigger a
-         * layout and a change in content size. This content size change will
-         * mean that contentSizeChanged will either call this method directly or
-         * indirectly from onSizeChanged.
-         */
-        if (newWidth > mLastWidthSent && mWrapContent) {
-            newHeight = 0;
-            heightWidthRatio = 0;
-        }
-        // Actual visible content height.
-        int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
-        // Avoid sending another message if the dimensions have not changed.
-        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
-                actualViewHeight != mLastActualHeightSent) {
-            ViewSizeData data = new ViewSizeData();
-            data.mWidth = newWidth;
-            data.mHeight = newHeight;
-            data.mHeightWidthRatio = heightWidthRatio;
-            data.mActualViewHeight = actualViewHeight;
-            data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
-            data.mScale = mZoomManager.getScale();
-            data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
-                    && !mHeightCanMeasure;
-            data.mAnchorX = mZoomManager.getDocumentAnchorX();
-            data.mAnchorY = mZoomManager.getDocumentAnchorY();
-            mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
-            mLastWidthSent = newWidth;
-            mLastHeightSent = newHeight;
-            mLastActualHeightSent = actualViewHeight;
-            mZoomManager.clearDocumentAnchor();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Update the double-tap zoom.
-     */
-    /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
-        mZoomManager.updateDoubleTapZoom(doubleTapZoom);
-    }
-
-    private int computeRealHorizontalScrollRange() {
-        if (mDrawHistory) {
-            return mHistoryWidth;
-        } else {
-            // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentWidth * mZoomManager.getScale());
-        }
-    }
-
-    @Override
-    protected int computeHorizontalScrollRange() {
-        int range = computeRealHorizontalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollX = mScrollX;
-        final int overscrollRight = computeMaxScrollX();
-        if (scrollX < 0) {
-            range -= scrollX;
-        } else if (scrollX > overscrollRight) {
-            range += scrollX - overscrollRight;
-        }
-
-        return range;
-    }
-
-    @Override
-    protected int computeHorizontalScrollOffset() {
-        return Math.max(mScrollX, 0);
-    }
-
-    private int computeRealVerticalScrollRange() {
-        if (mDrawHistory) {
-            return mHistoryHeight;
-        } else {
-            // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentHeight * mZoomManager.getScale());
-        }
-    }
-
-    @Override
-    protected int computeVerticalScrollRange() {
-        int range = computeRealVerticalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollY = mScrollY;
-        final int overscrollBottom = computeMaxScrollY();
-        if (scrollY < 0) {
-            range -= scrollY;
-        } else if (scrollY > overscrollBottom) {
-            range += scrollY - overscrollBottom;
-        }
-
-        return range;
-    }
-
-    @Override
-    protected int computeVerticalScrollOffset() {
-        return Math.max(mScrollY - getTitleHeight(), 0);
-    }
-
-    @Override
-    protected int computeVerticalScrollExtent() {
-        return getViewHeight();
-    }
-
-    /** @hide */
-    @Override
-    protected void onDrawVerticalScrollBar(Canvas canvas,
-                                           Drawable scrollBar,
-                                           int l, int t, int r, int b) {
-        if (mScrollY < 0) {
-            t -= mScrollY;
-        }
-        scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
-        scrollBar.draw(canvas);
-    }
-
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
-            boolean clampedY) {
-        // Special-case layer scrolling so that we do not trigger normal scroll
-        // updating.
-        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-            scrollLayerTo(scrollX, scrollY);
-            return;
-        }
-        mInOverScrollMode = false;
-        int maxX = computeMaxScrollX();
-        int maxY = computeMaxScrollY();
-        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;
-        }
-        if (scrollY < 0 || scrollY > maxY) {
-            mInOverScrollMode = true;
-        }
-
-        int oldX = mScrollX;
-        int oldY = mScrollY;
-
-        super.scrollTo(scrollX, scrollY);
-
-        if (mOverScrollGlow != null) {
-            mOverScrollGlow.pullGlow(mScrollX, mScrollY, oldX, oldY, maxX, maxY);
-        }
+        mProvider.requestImageRef(msg);
     }
 
     /**
@@ -3548,8 +1046,7 @@
      */
     public String getUrl() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getUrl() : null;
+        return mProvider.getUrl();
     }
 
     /**
@@ -3562,8 +1059,7 @@
      */
     public String getOriginalUrl() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getOriginalUrl() : null;
+        return mProvider.getOriginalUrl();
     }
 
     /**
@@ -3573,8 +1069,7 @@
      */
     public String getTitle() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTitle() : null;
+        return mProvider.getTitle();
     }
 
     /**
@@ -3584,8 +1079,7 @@
      */
     public Bitmap getFavicon() {
         checkThread();
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getFavicon() : null;
+        return mProvider.getFavicon();
     }
 
     /**
@@ -3595,8 +1089,7 @@
      * @hide
      */
     public String getTouchIconUrl() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTouchIconUrl() : null;
+        return mProvider.getTouchIconUrl();
     }
 
     /**
@@ -3605,7 +1098,7 @@
      */
     public int getProgress() {
         checkThread();
-        return mCallbackProxy.getProgress();
+        return mProvider.getProgress();
     }
 
     /**
@@ -3613,7 +1106,7 @@
      */
     public int getContentHeight() {
         checkThread();
-        return mContentHeight;
+        return mProvider.getContentHeight();
     }
 
     /**
@@ -3621,14 +1114,7 @@
      * @hide
      */
     public int getContentWidth() {
-        return mContentWidth;
-    }
-
-    /**
-     * @hide
-     */
-    public int getPageBackgroundColor() {
-        return nativeGetBackgroundColor();
+        return mProvider.getContentWidth();
     }
 
     /**
@@ -3638,7 +1124,7 @@
      */
     public void pauseTimers() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
+        mProvider.pauseTimers();
     }
 
     /**
@@ -3647,7 +1133,7 @@
      */
     public void resumeTimers() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
+        mProvider.resumeTimers();
     }
 
     /**
@@ -3660,38 +1146,7 @@
      */
     public void onPause() {
         checkThread();
-        if (!mIsPaused) {
-            mIsPaused = true;
-            mWebViewCore.sendMessage(EventHub.ON_PAUSE);
-            // We want to pause the current playing video when switching out
-            // from the current WebView/tab.
-            if (mHTML5VideoViewProxy != null) {
-                mHTML5VideoViewProxy.pauseAndDispatch();
-            }
-            if (mNativeClass != 0) {
-                nativeSetPauseDrawing(mNativeClass, true);
-            }
-
-            cancelSelectDialog();
-            WebCoreThreadWatchdog.pause();
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        updateDrawingState();
-    }
-
-    void updateDrawingState() {
-        if (mNativeClass == 0 || mIsPaused) return;
-        if (getWindowVisibility() != VISIBLE) {
-            nativeSetPauseDrawing(mNativeClass, true);
-        } else if (getVisibility() != VISIBLE) {
-            nativeSetPauseDrawing(mNativeClass, true);
-        } else {
-            nativeSetPauseDrawing(mNativeClass, false);
-        }
+        mProvider.onPause();
     }
 
     /**
@@ -3699,22 +1154,7 @@
      */
     public void onResume() {
         checkThread();
-        if (mIsPaused) {
-            mIsPaused = false;
-            mWebViewCore.sendMessage(EventHub.ON_RESUME);
-            if (mNativeClass != 0) {
-                nativeSetPauseDrawing(mNativeClass, false);
-            }
-        }
-        // Ensure that the watchdog has a currently valid Context to be able to display
-        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
-        // thread was blocked and the Activity is started again, we may reuse the blocked
-        // thread, but we'll have a new Activity.
-        WebCoreThreadWatchdog.updateContext(mContext);
-        // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
-        // to ensure that the Watchdog thread is running for the new WebView, so call
-        // it outside the if block above.
-        WebCoreThreadWatchdog.resume();
+        mProvider.onResume();
     }
 
     /**
@@ -3723,7 +1163,7 @@
      * @hide
      */
     public boolean isPaused() {
-        return mIsPaused;
+        return mProvider.isPaused();
     }
 
     /**
@@ -3732,7 +1172,7 @@
      */
     public void freeMemory() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
+        mProvider.freeMemory();
     }
 
     /**
@@ -3743,11 +1183,7 @@
      */
     public void clearCache(boolean includeDiskFiles) {
         checkThread();
-        // Note: this really needs to be a static method as it clears cache for all
-        // WebView. But we need mWebViewCore to send message to WebCore thread, so
-        // we can't make this static.
-        mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
-                includeDiskFiles ? 1 : 0, 0);
+        mProvider.clearCache(includeDiskFiles);
     }
 
     /**
@@ -3756,9 +1192,7 @@
      */
     public void clearFormData() {
         checkThread();
-        if (inEditingMode()) {
-            mWebTextView.setAdapterCustom(null);
-        }
+        mProvider.clearFormData();
     }
 
     /**
@@ -3766,8 +1200,7 @@
      */
     public void clearHistory() {
         checkThread();
-        mCallbackProxy.getBackForwardList().setClearPending();
-        mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
+        mProvider.clearHistory();
     }
 
     /**
@@ -3776,7 +1209,7 @@
      */
     public void clearSslPreferences() {
         checkThread();
-        mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
+        mProvider.clearSslPreferences();
     }
 
     /**
@@ -3789,7 +1222,8 @@
      */
     public WebBackForwardList copyBackForwardList() {
         checkThread();
-        return mCallbackProxy.getBackForwardList().clone();
+        return mProvider.copyBackForwardList();
+
     }
 
     /*
@@ -3801,8 +1235,7 @@
      */
     public void findNext(boolean forward) {
         checkThread();
-        if (0 == mNativeClass) return; // client isn't initialized
-        mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
+        mProvider.findNext(forward);
     }
 
     /*
@@ -3812,40 +1245,8 @@
      *              that were found.
      */
     public int findAll(String find) {
-        return findAllBody(find, false);
-    }
-
-    /**
-     * @hide
-     */
-    public void findAllAsync(String find) {
-        findAllBody(find, true);
-    }
-
-    private int findAllBody(String find, boolean isAsync) {
         checkThread();
-        if (0 == mNativeClass) return 0; // client isn't initialized
-        mLastFind = find;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        WebViewCore.FindAllRequest request = new
-            WebViewCore.FindAllRequest(find);
-        if (isAsync) {
-            mWebViewCore.sendMessage(EventHub.FIND_ALL, request);
-            return 0; // no need to wait for response
-        }
-        synchronized(request) {
-            try {
-                mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL,
-                    request);
-                while (request.mMatchCount == -1) {
-                    request.wait();
-                }
-            }
-            catch (InterruptedException e) {
-                return 0;
-            }
-        }
-        return request.mMatchCount;
+        return mProvider.findAll(find);
     }
 
     /**
@@ -3860,56 +1261,10 @@
      */
     public boolean showFindDialog(String text, boolean showIme) {
         checkThread();
-        FindActionModeCallback callback = new FindActionModeCallback(mContext);
-        if (getParent() == null || startActionMode(callback) == null) {
-            // Could not start the action mode, so end Find on page
-            return false;
-        }
-        mCachedOverlappingActionModeHeight = -1;
-        mFindCallback = callback;
-        setFindIsUp(true);
-        mFindCallback.setWebView(this);
-        if (showIme) {
-            mFindCallback.showSoftInput();
-        } else if (text != null) {
-            mFindCallback.setText(text);
-            mFindCallback.findAll();
-            return true;
-        }
-        if (text == null) {
-            text = mLastFind;
-        }
-        if (text != null) {
-            mFindCallback.setText(text);
-            mFindCallback.findAll();
-        }
-        return true;
+        return mProvider.showFindDialog(text, showIme);
     }
 
     /**
-     * Keep track of the find callback so that we can remove its titlebar if
-     * necessary.
-     */
-    private FindActionModeCallback mFindCallback;
-
-    /**
-     * Toggle whether the find dialog is showing, for both native and Java.
-     */
-    private void setFindIsUp(boolean isUp) {
-        mFindIsUp = isUp;
-        if (0 == mNativeClass) return; // client isn't initialized
-        nativeSetFindIsUp(isUp);
-    }
-
-    // Used to know whether the find dialog is open.  Affects whether
-    // or not we draw the highlights for matches.
-    private boolean mFindIsUp;
-
-    // Keep track of the last string sent, so we can search again when find is
-    // reopened.
-    private String mLastFind;
-
-    /**
      * Return the first substring consisting of the address of a physical
      * location. Currently, only addresses in the United States are detected,
      * and consist of:
@@ -3931,33 +1286,7 @@
      */
     public static String findAddress(String addr) {
         checkThread();
-        return findAddress(addr, false);
-    }
-
-    /**
-     * @hide
-     * Return the first substring consisting of the address of a physical
-     * location. Currently, only addresses in the United States are detected,
-     * and consist of:
-     * - a house number
-     * - a street name
-     * - a street type (Road, Circle, etc), either spelled out or abbreviated
-     * - a city name
-     * - a state or territory, either spelled out or two-letter abbr.
-     * - an optional 5 digit or 9 digit zip code.
-     *
-     * Names are optionally capitalized, and the zip code, if present,
-     * must be valid for the state. The street type must be a standard USPS
-     * spelling or abbreviation. The state or territory must also be spelled
-     * or abbreviated using USPS standards. The house number may not exceed
-     * five digits.
-     * @param addr The string to search for addresses.
-     * @param caseInsensitive addr Set to true to make search ignore case.
-     *
-     * @return the address, or if no address is found, return null.
-     */
-    public static String findAddress(String addr, boolean caseInsensitive) {
-        return WebViewCore.nativeFindAddress(addr, caseInsensitive);
+        return getFactory().getStatics().findAddress(addr);
     }
 
     /*
@@ -3965,28 +1294,7 @@
      */
     public void clearMatches() {
         checkThread();
-        if (mNativeClass == 0)
-            return;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
-    }
-
-
-    /**
-     * Called when the find ActionMode ends.
-     */
-    void notifyFindDialogDismissed() {
-        mFindCallback = null;
-        mCachedOverlappingActionModeHeight = -1;
-        if (mWebViewCore == null) {
-            return;
-        }
-        clearMatches();
-        setFindIsUp(false);
-        // Now that the dialog has been removed, ensure that we scroll to a
-        // location that is not beyond the end of the page.
-        pinScrollTo(mScrollX, mScrollY, false, 0);
-        invalidate();
+        mProvider.clearMatches();
     }
 
     /**
@@ -3997,427 +1305,7 @@
      */
     public void documentHasImages(Message response) {
         checkThread();
-        if (response == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
-    }
-
-    /**
-     * Request the scroller to abort any ongoing animation
-     *
-     * @hide
-     */
-    public void stopScroll() {
-        mScroller.forceFinished(true);
-        mLastVelocity = 0;
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            int oldX = mScrollX;
-            int oldY = mScrollY;
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
-            invalidate();  // So we draw again
-
-            if (!mScroller.isFinished()) {
-                int rangeX = computeMaxScrollX();
-                int rangeY = computeMaxScrollY();
-                int overflingDistance = mOverflingDistance;
-
-                // Use the layer's scroll data if needed.
-                if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-                    oldX = mScrollingLayerRect.left;
-                    oldY = mScrollingLayerRect.top;
-                    rangeX = mScrollingLayerRect.right;
-                    rangeY = mScrollingLayerRect.bottom;
-                    // No overscrolling for layers.
-                    overflingDistance = 0;
-                }
-
-                overScrollBy(x - oldX, y - oldY, oldX, oldY,
-                        rangeX, rangeY,
-                        overflingDistance, overflingDistance, false);
-
-                if (mOverScrollGlow != null) {
-                    mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
-                }
-            } else {
-                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    mScrollX = x;
-                    mScrollY = y;
-                } else {
-                    // Update the layer position instead of WebView.
-                    scrollLayerTo(x, y);
-                }
-                abortAnimation();
-                nativeSetIsScrolling(false);
-                if (!mBlockWebkitViewMessages) {
-                    WebViewCore.resumePriority();
-                    if (!mSelectingText) {
-                        WebViewCore.resumeUpdatePicture(mWebViewCore);
-                    }
-                }
-                if (oldX != mScrollX || oldY != mScrollY) {
-                    sendOurVisibleRect();
-                }
-            }
-        } else {
-            super.computeScroll();
-        }
-    }
-
-    private void scrollLayerTo(int x, int y) {
-        if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) {
-            return;
-        }
-        if (mSelectingText) {
-            int dx = mScrollingLayerRect.left - x;
-            int dy = mScrollingLayerRect.top - y;
-            if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorBase.offset(dx, dy);
-            }
-            if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorExtent.offset(dx, dy);
-            }
-        }
-        nativeScrollLayer(mCurrentScrollingLayerId, x, y);
-        mScrollingLayerRect.left = x;
-        mScrollingLayerRect.top = y;
-        mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
-                mScrollingLayerRect);
-        onScrollChanged(mScrollX, mScrollY, mScrollX, mScrollY);
-        invalidate();
-    }
-
-    private static int computeDuration(int dx, int dy) {
-        int distance = Math.max(Math.abs(dx), Math.abs(dy));
-        int duration = distance * 1000 / STD_SPEED;
-        return Math.min(duration, MAX_DURATION);
-    }
-
-    // helper to pin the scrollBy parameters (already in view coordinates)
-    // returns true if the scroll was changed
-    private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
-        return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
-    }
-    // helper to pin the scrollTo parameters (already in view coordinates)
-    // returns true if the scroll was changed
-    private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
-        x = pinLocX(x);
-        y = pinLocY(y);
-        int dx = x - mScrollX;
-        int dy = y - mScrollY;
-
-        if ((dx | dy) == 0) {
-            return false;
-        }
-        abortAnimation();
-        if (animate) {
-            //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
-            mScroller.startScroll(mScrollX, mScrollY, dx, dy,
-                    animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
-            awakenScrollBars(mScroller.getDuration());
-            invalidate();
-        } else {
-            scrollTo(x, y);
-        }
-        return true;
-    }
-
-    // Scale from content to view coordinates, and pin.
-    // Also called by jni webview.cpp
-    private boolean setContentScrollBy(int cx, int cy, boolean animate) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            // TODO: as we switchOutDrawHistory when trackball or navigation
-            // keys are hit, this should be safe. Right?
-            return false;
-        }
-        cx = contentToViewDimension(cx);
-        cy = contentToViewDimension(cy);
-        if (mHeightCanMeasure) {
-            // move our visible rect according to scroll request
-            if (cy != 0) {
-                Rect tempRect = new Rect();
-                calcOurVisibleRect(tempRect);
-                tempRect.offset(cx, cy);
-                requestRectangleOnScreen(tempRect);
-            }
-            // FIXME: We scroll horizontally no matter what because currently
-            // ScrollView and ListView will not scroll horizontally.
-            // FIXME: Why do we only scroll horizontally if there is no
-            // vertical scroll?
-//                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
-            return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
-        } else {
-            return pinScrollBy(cx, cy, animate, 0);
-        }
-    }
-
-    /**
-     * Called by CallbackProxy when the page starts loading.
-     * @param url The URL of the page which has started loading.
-     */
-    /* package */ void onPageStarted(String url) {
-        // every time we start a new page, we want to reset the
-        // WebView certificate:  if the new site is secure, we
-        // will reload it and get a new certificate set;
-        // if the new site is not secure, the certificate must be
-        // null, and that will be the case
-        setCertificate(null);
-
-        // reset the flag since we set to true in if need after
-        // loading is see onPageFinished(Url)
-        mAccessibilityScriptInjected = false;
-    }
-
-    /**
-     * Called by CallbackProxy when the page finishes loading.
-     * @param url The URL of the page which has finished loading.
-     */
-    /* package */ void onPageFinished(String url) {
-        if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
-            // If the user is now on a different page, or has scrolled the page
-            // past the point where the title bar is offscreen, ignore the
-            // scroll request.
-            if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
-                    && mScrollX == 0 && mScrollY == 0) {
-                pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
-                        SLIDE_TITLE_DURATION);
-            }
-            mPageThatNeedsToSlideTitleBarOffScreen = null;
-        }
-        mZoomManager.onPageFinished(url);
-        injectAccessibilityForUrl(url);
-    }
-
-    /**
-     * This method injects accessibility in the loaded document if accessibility
-     * is enabled. If JavaScript is enabled we try to inject a URL specific script.
-     * If no URL specific script is found or JavaScript is disabled we fallback to
-     * the default {@link AccessibilityInjector} implementation.
-     * </p>
-     * If the URL has the "axs" paramter set to 1 it has already done the
-     * script injection so we do nothing. If the parameter is set to 0
-     * the URL opts out accessibility script injection so we fall back to
-     * the default {@link AccessibilityInjector}.
-     * </p>
-     * Note: If the user has not opted-in the accessibility script injection no scripts
-     * are injected rather the default {@link AccessibilityInjector} implementation
-     * is used.
-     *
-     * @param url The URL loaded by this {@link WebView}.
-     */
-    private void injectAccessibilityForUrl(String url) {
-        if (mWebViewCore == null) {
-            return;
-        }
-        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-
-        if (!accessibilityManager.isEnabled()) {
-            // it is possible that accessibility was turned off between reloads
-            ensureAccessibilityScriptInjectorInstance(false);
-            return;
-        }
-
-        if (!getSettings().getJavaScriptEnabled()) {
-            // no JS so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-            return;
-        }
-
-        // check the URL "axs" parameter to choose appropriate action
-        int axsParameterValue = getAxsUrlParameterValue(url);
-        if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
-            boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
-                    .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
-            if (onDeviceScriptInjectionEnabled) {
-                ensureAccessibilityScriptInjectorInstance(false);
-                // neither script injected nor script injection opted out => we inject
-                loadUrl(getScreenReaderInjectingJs());
-                // TODO: Set this flag after successfull script injection. Maybe upon injection
-                // the chooser should update the meta tag and we check it to declare success
-                mAccessibilityScriptInjected = true;
-            } else {
-                // injection disabled so we fallback to the basic built-in support
-                ensureAccessibilityScriptInjectorInstance(true);
-            }
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
-            // injection opted out so we fallback to the basic buil-in support
-            ensureAccessibilityScriptInjectorInstance(true);
-        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
-            ensureAccessibilityScriptInjectorInstance(false);
-            // the URL provides accessibility but we still need to add our generic script
-            loadUrl(getScreenReaderInjectingJs());
-        } else {
-            Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
-        }
-    }
-
-    /**
-     * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
-     *
-     * @param present True to ensure an insance, false to ensure no instance.
-     */
-    private void ensureAccessibilityScriptInjectorInstance(boolean present) {
-        if (present) {
-            if (mAccessibilityInjector == null) {
-                mAccessibilityInjector = new AccessibilityInjector(this);
-            }
-        } else {
-            mAccessibilityInjector = null;
-        }
-    }
-
-    /**
-     * Gets JavaScript that injects a screen-reader.
-     *
-     * @return The JavaScript snippet.
-     */
-    private String getScreenReaderInjectingJs() {
-        String screenReaderUrl = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
-        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
-    }
-
-    /**
-     * Gets the "axs" URL parameter value.
-     *
-     * @param url A url to fetch the paramter from.
-     * @return The parameter value if such, -1 otherwise.
-     */
-    private int getAxsUrlParameterValue(String url) {
-        if (mMatchAxsUrlParameterPattern == null) {
-            mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
-        }
-        Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
-        if (matcher.find()) {
-            String keyValuePair = url.substring(matcher.start(), matcher.end());
-            return Integer.parseInt(keyValuePair.split("=")[1]);
-        }
-        return -1;
-    }
-
-    /**
-     * The URL of a page that sent a message to scroll the title bar off screen.
-     *
-     * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
-     * title bar off the screen.  Sometimes, the scroll position is set before
-     * the page finishes loading.  Rather than scrolling while the page is still
-     * loading, keep track of the URL and new scroll position so we can perform
-     * the scroll once the page finishes loading.
-     */
-    private String mPageThatNeedsToSlideTitleBarOffScreen;
-
-    /**
-     * The destination Y scroll position to be used when the page finishes
-     * loading.  See mPageThatNeedsToSlideTitleBarOffScreen.
-     */
-    private int mYDistanceToSlideTitleOffScreen;
-
-    // scale from content to view coordinates, and pin
-    // return true if pin caused the final x/y different than the request cx/cy,
-    // and a future scroll may reach the request cx/cy after our size has
-    // changed
-    // return false if the view scroll to the exact position as it is requested,
-    // where negative numbers are taken to mean 0
-    private boolean setContentScrollTo(int cx, int cy) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            // One known case where this is called is that WebCore tries to
-            // restore the scroll position. As history Picture already uses the
-            // saved scroll position, it is ok to skip this.
-            return false;
-        }
-        int vx;
-        int vy;
-        if ((cx | cy) == 0) {
-            // If the page is being scrolled to (0,0), do not add in the title
-            // bar's height, and simply scroll to (0,0). (The only other work
-            // in contentToView_ is to multiply, so this would not change 0.)
-            vx = 0;
-            vy = 0;
-        } else {
-            vx = contentToViewX(cx);
-            vy = contentToViewY(cy);
-        }
-//        Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
-//                      vx + " " + vy + "]");
-        // Some mobile sites attempt to scroll the title bar off the page by
-        // scrolling to (0,1).  If we are at the top left corner of the
-        // page, assume this is an attempt to scroll off the title bar, and
-        // animate the title bar off screen slowly enough that the user can see
-        // it.
-        if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
-                && mTitleBar != null) {
-            // FIXME: 100 should be defined somewhere as our max progress.
-            if (getProgress() < 100) {
-                // Wait to scroll the title bar off screen until the page has
-                // finished loading.  Keep track of the URL and the destination
-                // Y position
-                mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
-                mYDistanceToSlideTitleOffScreen = vy;
-            } else {
-                pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
-            }
-            // Since we are animating, we have not yet reached the desired
-            // scroll position.  Do not return true to request another attempt
-            return false;
-        }
-        pinScrollTo(vx, vy, false, 0);
-        // If the request was to scroll to a negative coordinate, treat it as if
-        // it was a request to scroll to 0
-        if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    // scale from content to view coordinates, and pin
-    private void spawnContentScrollTo(int cx, int cy) {
-        if (mDrawHistory) {
-            // disallow WebView to change the scroll position as History Picture
-            // is used in the view system.
-            return;
-        }
-        int vx = contentToViewX(cx);
-        int vy = contentToViewY(cy);
-        pinScrollTo(vx, vy, true, 0);
-    }
-
-    /**
-     * These are from webkit, and are in content coordinate system (unzoomed)
-     */
-    private void contentSizeChanged(boolean updateLayout) {
-        // suppress 0,0 since we usually see real dimensions soon after
-        // this avoids drawing the prev content in a funny place. If we find a
-        // way to consolidate these notifications, this check may become
-        // obsolete
-        if ((mContentWidth | mContentHeight) == 0) {
-            return;
-        }
-
-        if (mHeightCanMeasure) {
-            if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
-                    || updateLayout) {
-                requestLayout();
-            }
-        } else if (mWidthCanMeasure) {
-            if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
-                    || updateLayout) {
-                requestLayout();
-            }
-        } else {
-            // If we don't request a layout, try to send our view size to the
-            // native side to ensure that WebCore has the correct dimensions.
-            sendViewSizeZoom(false);
-        }
+        mProvider.documentHasImages(response);
     }
 
     /**
@@ -4427,17 +1315,7 @@
      */
     public void setWebViewClient(WebViewClient client) {
         checkThread();
-        mCallbackProxy.setWebViewClient(client);
-    }
-
-    /**
-     * Gets the WebViewClient
-     * @return the current WebViewClient instance.
-     *
-     * @hide This is an implementation detail.
-     */
-    public WebViewClient getWebViewClient() {
-        return mCallbackProxy.getWebViewClient();
+        mProvider.setWebViewClient(client);
     }
 
     /**
@@ -4448,7 +1326,7 @@
      */
     public void setDownloadListener(DownloadListener listener) {
         checkThread();
-        mCallbackProxy.setDownloadListener(listener);
+        mProvider.setDownloadListener(listener);
     }
 
     /**
@@ -4459,36 +1337,7 @@
      */
     public void setWebChromeClient(WebChromeClient client) {
         checkThread();
-        mCallbackProxy.setWebChromeClient(client);
-    }
-
-    /**
-     * Gets the chrome handler.
-     * @return the current WebChromeClient instance.
-     *
-     * @hide This is an implementation detail.
-     */
-    public WebChromeClient getWebChromeClient() {
-        return mCallbackProxy.getWebChromeClient();
-    }
-
-    /**
-     * Set the back/forward list client. This is an implementation of
-     * WebBackForwardListClient for handling new items and changes in the
-     * history index.
-     * @param client An implementation of WebBackForwardListClient.
-     * {@hide}
-     */
-    public void setWebBackForwardListClient(WebBackForwardListClient client) {
-        mCallbackProxy.setWebBackForwardListClient(client);
-    }
-
-    /**
-     * Gets the WebBackForwardListClient.
-     * {@hide}
-     */
-    public WebBackForwardListClient getWebBackForwardListClient() {
-        return mCallbackProxy.getWebBackForwardListClient();
+        mProvider.setWebChromeClient(client);
     }
 
     /**
@@ -4500,23 +1349,7 @@
     @Deprecated
     public void setPictureListener(PictureListener listener) {
         checkThread();
-        mPictureListener = listener;
-    }
-
-    /**
-     * {@hide}
-     */
-    /* FIXME: Debug only! Remove for SDK! */
-    public void externalRepresentation(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
-    }
-
-    /**
-     * {@hide}
-     */
-    /* FIXME: Debug only! Remove for SDK! */
-    public void documentAsText(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
+        mProvider.setPictureListener(listener);
     }
 
     /**
@@ -4547,13 +1380,7 @@
      */
     public void addJavascriptInterface(Object object, String name) {
         checkThread();
-        if (object == null) {
-            return;
-        }
-        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-        arg.mObject = object;
-        arg.mInterfaceName = name;
-        mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
+        mProvider.addJavascriptInterface(object, name);
     }
 
     /**
@@ -4562,11 +1389,7 @@
      */
     public void removeJavascriptInterface(String interfaceName) {
         checkThread();
-        if (mWebViewCore != null) {
-            WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-            arg.mInterfaceName = interfaceName;
-            mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
-        }
+        mProvider.removeJavascriptInterface(interfaceName);
     }
 
     /**
@@ -4577,1387 +1400,31 @@
      */
     public WebSettings getSettings() {
         checkThread();
-        return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
+        return mProvider.getSettings();
     }
 
-   /**
-    * Return the list of currently loaded plugins.
-    * @return The list of currently loaded plugins.
-    *
-    * @hide
-    * @deprecated This was used for Gears, which has been deprecated.
-    */
+    /**
+     * Return the list of currently loaded plugins.
+     * @return The list of currently loaded plugins.
+     *
+     * @hide
+     * @deprecated This was used for Gears, which has been deprecated.
+     */
     @Deprecated
     public static synchronized PluginList getPluginList() {
         checkThread();
         return new PluginList();
     }
 
-   /**
-    * @hide
-    * @deprecated This was used for Gears, which has been deprecated.
-    */
+    /**
+     * @hide
+     * @deprecated This was used for Gears, which has been deprecated.
+     */
     @Deprecated
     public void refreshPlugins(boolean reloadOpenPages) {
         checkThread();
     }
 
-    //-------------------------------------------------------------------------
-    // Override View methods
-    //-------------------------------------------------------------------------
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mNativeClass != 0) {
-                mPrivateHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        destroy();
-                    }
-                });
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (child == mTitleBar) {
-            // When drawing the title bar, move it horizontally to always show
-            // at the top of the WebView.
-            mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
-            int newTop = 0;
-            if (mTitleGravity == Gravity.NO_GRAVITY) {
-                newTop = Math.min(0, mScrollY);
-            } else if (mTitleGravity == Gravity.TOP) {
-                newTop = mScrollY;
-            }
-            mTitleBar.setBottom(newTop + mTitleBar.getHeight());
-            mTitleBar.setTop(newTop);
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    private void drawContent(Canvas canvas, boolean drawRings) {
-        drawCoreAndCursorRing(canvas, mBackgroundColor,
-                mDrawCursorRing && drawRings);
-    }
-
-    /**
-     * Draw the background when beyond bounds
-     * @param canvas Canvas to draw into
-     */
-    private void drawOverScrollBackground(Canvas canvas) {
-        if (mOverScrollBackground == null) {
-            mOverScrollBackground = new Paint();
-            Bitmap bm = BitmapFactory.decodeResource(
-                    mContext.getResources(),
-                    com.android.internal.R.drawable.status_bar_background);
-            mOverScrollBackground.setShader(new BitmapShader(bm,
-                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
-            mOverScrollBorder = new Paint();
-            mOverScrollBorder.setStyle(Paint.Style.STROKE);
-            mOverScrollBorder.setStrokeWidth(0);
-            mOverScrollBorder.setColor(0xffbbbbbb);
-        }
-
-        int top = 0;
-        int right = computeRealHorizontalScrollRange();
-        int bottom = top + computeRealVerticalScrollRange();
-        // first draw the background and anchor to the top of the view
-        canvas.save();
-        canvas.translate(mScrollX, mScrollY);
-        canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
-                - mScrollY, Region.Op.DIFFERENCE);
-        canvas.drawPaint(mOverScrollBackground);
-        canvas.restore();
-        // then draw the border
-        canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
-        // next clip the region for the content
-        canvas.clipRect(0, top, right, bottom);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (inFullScreenMode()) {
-            return; // no need to draw anything if we aren't visible.
-        }
-        // if mNativeClass is 0, the WebView is either destroyed or not
-        // initialized. In either case, just draw the background color and return
-        if (mNativeClass == 0) {
-            canvas.drawColor(mBackgroundColor);
-            return;
-        }
-
-        // if both mContentWidth and mContentHeight are 0, it means there is no
-        // valid Picture passed to WebView yet. This can happen when WebView
-        // just starts. Draw the background and return.
-        if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
-            canvas.drawColor(mBackgroundColor);
-            return;
-        }
-
-        if (canvas.isHardwareAccelerated()) {
-            mZoomManager.setHardwareAccelerated();
-        } else {
-            mWebViewCore.resumeWebKitDraw();
-        }
-
-        int saveCount = canvas.save();
-        if (mInOverScrollMode && !getSettings()
-                .getUseWebViewBackgroundForOverscrollBackground()) {
-            drawOverScrollBackground(canvas);
-        }
-        if (mTitleBar != null) {
-            canvas.translate(0, getTitleHeight());
-        }
-        boolean drawNativeRings = !sDisableNavcache;
-        drawContent(canvas, drawNativeRings);
-        canvas.restoreToCount(saveCount);
-
-        if (AUTO_REDRAW_HACK && mAutoRedraw) {
-            invalidate();
-        }
-        mWebViewCore.signalRepaintDone();
-
-        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
-            invalidate();
-        }
-
-        if (mFocusTransition != null) {
-            mFocusTransition.draw(canvas);
-        } else if (shouldDrawHighlightRect()) {
-            RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
-            Rect r = new Rect();
-            while (iter.next(r)) {
-                canvas.drawRect(r, mTouchHightlightPaint);
-            }
-        }
-        if (DEBUG_TOUCH_HIGHLIGHT) {
-            if (getSettings().getNavDump()) {
-                if ((mTouchHighlightX | mTouchHighlightY) != 0) {
-                    if (mTouchCrossHairColor == null) {
-                        mTouchCrossHairColor = new Paint();
-                        mTouchCrossHairColor.setColor(Color.RED);
-                    }
-                    canvas.drawLine(mTouchHighlightX - mNavSlop,
-                            mTouchHighlightY - mNavSlop, mTouchHighlightX
-                                    + mNavSlop + 1, mTouchHighlightY + mNavSlop
-                                    + 1, mTouchCrossHairColor);
-                    canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
-                            mTouchHighlightY - mNavSlop, mTouchHighlightX
-                                    - mNavSlop,
-                            mTouchHighlightY + mNavSlop + 1,
-                            mTouchCrossHairColor);
-                }
-            }
-        }
-    }
-
-    private void removeTouchHighlight() {
-        mWebViewCore.removeMessages(EventHub.HIT_TEST);
-        mPrivateHandler.removeMessages(HIT_TEST_RESULT);
-        setTouchHighlightRects(null);
-    }
-
-    @Override
-    public void setLayoutParams(ViewGroup.LayoutParams params) {
-        if (params.height == LayoutParams.WRAP_CONTENT) {
-            mWrapContent = true;
-        }
-        super.setLayoutParams(params);
-    }
-
-    @Override
-    public boolean performLongClick() {
-        // performLongClick() is the result of a delayed message. If we switch
-        // to windows overview, the WebView will be temporarily removed from the
-        // view system. In that case, do nothing.
-        if (getParent() == null) return false;
-
-        // A multi-finger gesture can look like a long press; make sure we don't take
-        // long press actions if we're scaling.
-        final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
-        if (detector != null && detector.isInProgress()) {
-            return false;
-        }
-
-        if (mNativeClass != 0 && nativeCursorIsTextInput()) {
-            // Send the click so that the textfield is in focus
-            centerKeyPressOnTextField();
-            rebuildWebTextView();
-        } else {
-            clearTextEntry();
-        }
-        if (inEditingMode()) {
-            // Since we just called rebuildWebTextView, the layout is not set
-            // properly.  Update it so it can correctly find the word to select.
-            mWebTextView.ensureLayout();
-            // Provide a touch down event to WebTextView, which will allow it
-            // to store the location to use in performLongClick.
-            AbsoluteLayout.LayoutParams params
-                    = (AbsoluteLayout.LayoutParams) mWebTextView.getLayoutParams();
-            MotionEvent fake = MotionEvent.obtain(mLastTouchTime,
-                    mLastTouchTime, MotionEvent.ACTION_DOWN,
-                    mLastTouchX - params.x + mScrollX,
-                    mLastTouchY - params.y + mScrollY, 0);
-            mWebTextView.dispatchTouchEvent(fake);
-            return mWebTextView.performLongClick();
-        }
-        if (mSelectingText) return false; // long click does nothing on selection
-        /* if long click brings up a context menu, the super function
-         * returns true and we're done. Otherwise, nothing happened when
-         * the user clicked. */
-        if (super.performLongClick()) {
-            return true;
-        }
-        /* In the case where the application hasn't already handled the long
-         * click action, look for a word under the  click. If one is found,
-         * animate the text selection into view.
-         * FIXME: no animation code yet */
-        final boolean isSelecting = selectText();
-        if (isSelecting) {
-            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        } else if (focusCandidateIsEditableText()) {
-            mSelectCallback = new SelectActionModeCallback();
-            mSelectCallback.setWebView(this);
-            mSelectCallback.setTextSelected(false);
-            startActionMode(mSelectCallback);
-        }
-        return isSelecting;
-    }
-
-    /**
-     * Select the word at the last click point.
-     *
-     * @hide This is an implementation detail.
-     */
-    public boolean selectText() {
-        int x = viewToContentX(mLastTouchX + mScrollX);
-        int y = viewToContentY(mLastTouchY + mScrollY);
-        return selectText(x, y);
-    }
-
-    /**
-     * Select the word at the indicated content coordinates.
-     */
-    boolean selectText(int x, int y) {
-        mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
-        return true;
-    }
-
-    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        mCachedOverlappingActionModeHeight = -1;
-        if (mSelectingText && mOrientation != newConfig.orientation) {
-            selectionDone();
-        }
-        mOrientation = newConfig.orientation;
-        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
-            mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
-        }
-    }
-
-    /**
-     * Keep track of the Callback so we can end its ActionMode or remove its
-     * titlebar.
-     */
-    private SelectActionModeCallback mSelectCallback;
-
-    // These values are possible options for didUpdateWebTextViewDimensions.
-    private static final int FULLY_ON_SCREEN = 0;
-    private static final int INTERSECTS_SCREEN = 1;
-    private static final int ANYWHERE = 2;
-
-    /**
-     * Check to see if the focused textfield/textarea is still on screen.  If it
-     * is, update the the dimensions and location of WebTextView.  Otherwise,
-     * remove the WebTextView.  Should be called when the zoom level changes.
-     * @param intersection How to determine whether the textfield/textarea is
-     *        still on screen.
-     * @return boolean True if the textfield/textarea is still on screen and the
-     *         dimensions/location of WebTextView have been updated.
-     */
-    private boolean didUpdateWebTextViewDimensions(int intersection) {
-        Rect contentBounds = nativeFocusCandidateNodeBounds();
-        Rect vBox = contentToViewRect(contentBounds);
-        Rect visibleRect = new Rect();
-        calcOurVisibleRect(visibleRect);
-        offsetByLayerScrollPosition(vBox);
-        // If the textfield is on screen, place the WebTextView in
-        // its new place, accounting for our new scroll/zoom values,
-        // and adjust its textsize.
-        boolean onScreen;
-        switch (intersection) {
-            case FULLY_ON_SCREEN:
-                onScreen = visibleRect.contains(vBox);
-                break;
-            case INTERSECTS_SCREEN:
-                onScreen = Rect.intersects(visibleRect, vBox);
-                break;
-            case ANYWHERE:
-                onScreen = true;
-                break;
-            default:
-                throw new AssertionError(
-                        "invalid parameter passed to didUpdateWebTextViewDimensions");
-        }
-        if (onScreen) {
-            mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
-                    vBox.height());
-            mWebTextView.updateTextSize();
-            updateWebTextViewPadding();
-            return true;
-        } else {
-            // The textfield is now off screen.  The user probably
-            // was not zooming to see the textfield better.  Remove
-            // the WebTextView.  If the user types a key, and the
-            // textfield is still in focus, we will reconstruct
-            // the WebTextView and scroll it back on screen.
-            mWebTextView.remove();
-            return false;
-        }
-    }
-
-    private void offsetByLayerScrollPosition(Rect box) {
-        if ((mCurrentScrollingLayerId != 0)
-                && (mCurrentScrollingLayerId == nativeFocusCandidateLayerId())) {
-            box.offsetTo(box.left - mScrollingLayerRect.left,
-                    box.top - mScrollingLayerRect.top);
-        }
-    }
-
-    void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator,
-            boolean isPictureAfterFirstLayout) {
-        if (mNativeClass == 0)
-            return;
-        boolean queueFull;
-        queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
-                                       showVisualIndicator, isPictureAfterFirstLayout);
-
-        if (layer == 0 || isPictureAfterFirstLayout) {
-            mWebViewCore.resumeWebKitDraw();
-        } else if (queueFull) {
-            // temporarily disable webkit draw throttling
-            // TODO: re-enable
-            // mWebViewCore.pauseWebKitDraw();
-        }
-
-        if (mHTML5VideoViewProxy != null) {
-            mHTML5VideoViewProxy.setBaseLayer(layer);
-        }
-    }
-
-    int getBaseLayer() {
-        if (mNativeClass == 0) {
-            return 0;
-        }
-        return nativeGetBaseLayer();
-    }
-
-    private void onZoomAnimationStart() {
-        // If it is in password mode, turn it off so it does not draw misplaced.
-        if (inEditingMode()) {
-            mWebTextView.setVisibility(INVISIBLE);
-        }
-    }
-
-    private void onZoomAnimationEnd() {
-        // adjust the edit text view if needed
-        if (inEditingMode()
-                && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)) {
-            // If it is a password field, start drawing the WebTextView once
-            // again.
-            mWebTextView.setVisibility(VISIBLE);
-        }
-    }
-
-    void onFixedLengthZoomAnimationStart() {
-        WebViewCore.pauseUpdatePicture(getWebViewCore());
-        onZoomAnimationStart();
-    }
-
-    void onFixedLengthZoomAnimationEnd() {
-        if (!mBlockWebkitViewMessages && !mSelectingText) {
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-        }
-        onZoomAnimationEnd();
-    }
-
-    private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
-                                         Paint.DITHER_FLAG |
-                                         Paint.SUBPIXEL_TEXT_FLAG;
-    private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
-                                           Paint.DITHER_FLAG;
-
-    private final DrawFilter mZoomFilter =
-            new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
-    // If we need to trade better quality for speed, set mScrollFilter to null
-    private final DrawFilter mScrollFilter =
-            new PaintFlagsDrawFilter(SCROLL_BITS, 0);
-
-    private void drawCoreAndCursorRing(Canvas canvas, int color,
-        boolean drawCursorRing) {
-        if (mDrawHistory) {
-            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
-            canvas.drawPicture(mHistoryPicture);
-            return;
-        }
-        if (mNativeClass == 0) return;
-
-        boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
-        boolean animateScroll = ((!mScroller.isFinished()
-                || mVelocityTracker != null)
-                && (mTouchMode != TOUCH_DRAG_MODE ||
-                mHeldMotionless != MOTIONLESS_TRUE))
-                || mDeferTouchMode == TOUCH_DRAG_MODE;
-        if (mTouchMode == TOUCH_DRAG_MODE) {
-            if (mHeldMotionless == MOTIONLESS_PENDING) {
-                mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                mHeldMotionless = MOTIONLESS_FALSE;
-            }
-            if (mHeldMotionless == MOTIONLESS_FALSE) {
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
-                mHeldMotionless = MOTIONLESS_PENDING;
-            }
-        }
-        int saveCount = canvas.save();
-        if (animateZoom) {
-            mZoomManager.animateZoom(canvas);
-        } else if (!canvas.isHardwareAccelerated()) {
-            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
-        }
-
-        boolean UIAnimationsRunning = false;
-        // Currently for each draw we compute the animation values;
-        // We may in the future decide to do that independently.
-        if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
-                && nativeEvaluateLayersAnimations(mNativeClass)) {
-            UIAnimationsRunning = true;
-            // If we have unfinished (or unstarted) animations,
-            // we ask for a repaint. We only need to do this in software
-            // rendering (with hardware rendering we already have a different
-            // method of requesting a repaint)
-            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
-            invalidate();
-        }
-
-        // decide which adornments to draw
-        int extras = DRAW_EXTRAS_NONE;
-        if (!mFindIsUp) {
-            if (mSelectingText) {
-                extras = DRAW_EXTRAS_SELECTION;
-            } else if (drawCursorRing) {
-                extras = DRAW_EXTRAS_CURSOR_RING;
-            }
-        }
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
-                    + " mSelectingText=" + mSelectingText
-                    + " nativePageShouldHandleShiftAndArrows()="
-                    + nativePageShouldHandleShiftAndArrows()
-                    + " animateZoom=" + animateZoom
-                    + " extras=" + extras);
-        }
-
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        if (canvas.isHardwareAccelerated()) {
-            Rect glRectViewport = mGLViewportEmpty ? null : mGLRectViewport;
-            Rect viewRectViewport = mGLViewportEmpty ? null : mViewRectViewport;
-
-            int functor = nativeGetDrawGLFunction(mNativeClass, glRectViewport,
-                    viewRectViewport, mVisibleContentRect, getScale(), extras);
-            ((HardwareCanvas) canvas).callDrawGLFunction(functor);
-            if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
-                mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
-                nativeUseHardwareAccelSkia(mHardwareAccelSkia);
-            }
-
-        } else {
-            DrawFilter df = null;
-            if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
-                df = mZoomFilter;
-            } else if (animateScroll) {
-                df = mScrollFilter;
-            }
-            canvas.setDrawFilter(df);
-            // XXX: Revisit splitting content.  Right now it causes a
-            // synchronization problem with layers.
-            int content = nativeDraw(canvas, mVisibleContentRect, color,
-                    extras, false);
-            canvas.setDrawFilter(null);
-            if (!mBlockWebkitViewMessages && content != 0) {
-                mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
-            }
-        }
-
-        canvas.restoreToCount(saveCount);
-        if (mSelectingText) {
-            drawTextSelectionHandles(canvas);
-        }
-
-        if (extras == DRAW_EXTRAS_CURSOR_RING) {
-            if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
-                mTouchMode = TOUCH_SHORTPRESS_MODE;
-            }
-        }
-        if (mFocusSizeChanged) {
-            mFocusSizeChanged = false;
-            // If we are zooming, this will get handled above, when the zoom
-            // finishes.  We also do not need to do this unless the WebTextView
-            // is showing. With hardware acceleration, the pageSwapCallback()
-            // updates the WebTextView position in sync with page swapping
-            if (!canvas.isHardwareAccelerated() && !animateZoom && inEditingMode()) {
-                didUpdateWebTextViewDimensions(ANYWHERE);
-            }
-        }
-    }
-
-    private void drawTextSelectionHandles(Canvas canvas) {
-        int[] handles = new int[4];
-        getSelectionHandles(handles);
-        int start_x = contentToViewDimension(handles[0]);
-        int start_y = contentToViewDimension(handles[1]);
-        int end_x = contentToViewDimension(handles[2]);
-        int end_y = contentToViewDimension(handles[3]);
-
-        if (mIsCaretSelection) {
-            if (mSelectHandleCenter == null) {
-                mSelectHandleCenter = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_middle);
-            }
-            // Caret handle is centered
-            start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2);
-            mSelectHandleCenter.setBounds(start_x, start_y,
-                    start_x + mSelectHandleCenter.getIntrinsicWidth(),
-                    start_y + mSelectHandleCenter.getIntrinsicHeight());
-            mSelectHandleCenter.draw(canvas);
-        } else {
-            if (mSelectHandleLeft == null) {
-                mSelectHandleLeft = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_left);
-            }
-            // Magic formula copied from TextView
-            start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
-            mSelectHandleLeft.setBounds(start_x, start_y,
-                    start_x + mSelectHandleLeft.getIntrinsicWidth(),
-                    start_y + mSelectHandleLeft.getIntrinsicHeight());
-            if (mSelectHandleRight == null) {
-                mSelectHandleRight = mContext.getResources().getDrawable(
-                        com.android.internal.R.drawable.text_select_handle_right);
-            }
-            end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
-            mSelectHandleRight.setBounds(end_x, end_y,
-                    end_x + mSelectHandleRight.getIntrinsicWidth(),
-                    end_y + mSelectHandleRight.getIntrinsicHeight());
-            mSelectHandleLeft.draw(canvas);
-            mSelectHandleRight.draw(canvas);
-        }
-    }
-
-    /**
-     * Takes an int[4] array as an output param with the values being
-     * startX, startY, endX, endY
-     */
-    private void getSelectionHandles(int[] handles) {
-        handles[0] = mSelectCursorBase.right;
-        handles[1] = mSelectCursorBase.bottom -
-                (mSelectCursorBase.height() / 4);
-        handles[2] = mSelectCursorExtent.left;
-        handles[3] = mSelectCursorExtent.bottom
-                - (mSelectCursorExtent.height() / 4);
-        if (!nativeIsBaseFirst(mNativeClass)) {
-            int swap = handles[0];
-            handles[0] = handles[2];
-            handles[2] = swap;
-            swap = handles[1];
-            handles[1] = handles[3];
-            handles[3] = swap;
-        }
-    }
-
-    // draw history
-    private boolean mDrawHistory = false;
-    private Picture mHistoryPicture = null;
-    private int mHistoryWidth = 0;
-    private int mHistoryHeight = 0;
-
-    // Only check the flag, can be called from WebCore thread
-    boolean drawHistory() {
-        return mDrawHistory;
-    }
-
-    int getHistoryPictureWidth() {
-        return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
-    }
-
-    // Should only be called in UI thread
-    void switchOutDrawHistory() {
-        if (null == mWebViewCore) return; // CallbackProxy may trigger this
-        if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
-            mDrawHistory = false;
-            mHistoryPicture = null;
-            invalidate();
-            int oldScrollX = mScrollX;
-            int oldScrollY = mScrollY;
-            mScrollX = pinLocX(mScrollX);
-            mScrollY = pinLocY(mScrollY);
-            if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
-                onScrollChanged(mScrollX, mScrollY, oldScrollX, oldScrollY);
-            } else {
-                sendOurVisibleRect();
-            }
-        }
-    }
-
-    WebViewCore.CursorData cursorData() {
-        WebViewCore.CursorData result = cursorDataNoPosition();
-        Point position = nativeCursorPosition();
-        result.mX = position.x;
-        result.mY = position.y;
-        return result;
-    }
-
-    WebViewCore.CursorData cursorDataNoPosition() {
-        WebViewCore.CursorData result = new WebViewCore.CursorData();
-        result.mMoveGeneration = nativeMoveGeneration();
-        result.mFrame = nativeCursorFramePointer();
-        return result;
-    }
-
-    /**
-     *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of
-     *  order, swap them.
-     *  @param  start   Beginning of selection to delete.
-     *  @param  end     End of selection to delete.
-     */
-    /* package */ void deleteSelection(int start, int end) {
-        mTextGeneration++;
-        WebViewCore.TextSelectionData data
-                = new WebViewCore.TextSelectionData(start, end, 0);
-        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
-                data);
-    }
-
-    /**
-     *  Set the selection to (start, end) in the focused textfield. If start and
-     *  end are out of order, swap them.
-     *  @param  start   Beginning of selection.
-     *  @param  end     End of selection.
-     */
-    /* package */ void setSelection(int start, int end) {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (mInputConnection == null) {
-            mInputConnection = new WebViewInputConnection();
-        }
-        mInputConnection.setupEditorInfo(outAttrs);
-        return mInputConnection;
-    }
-
-    /**
-     * Called in response to a message from webkit telling us that the soft
-     * keyboard should be launched.
-     */
-    private void displaySoftKeyboard(boolean isTextView) {
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        // bring it back to the default level scale so that user can enter text
-        boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
-        if (zoom) {
-            mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
-            mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
-        }
-        if (isTextView) {
-            rebuildWebTextView();
-            if (inEditingMode()) {
-                imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver());
-                if (zoom) {
-                    didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
-                }
-                return;
-            }
-        }
-        // Used by plugins and contentEditable.
-        // Also used if the navigation cache is out of date, and
-        // does not recognize that a textfield is in focus.  In that
-        // case, use WebView as the targeted view.
-        // see http://b/issue?id=2457459
-        imm.showSoftInput(this, 0);
-    }
-
-    // Called by WebKit to instruct the UI to hide the keyboard
-    private void hideSoftKeyboard() {
-        InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && (imm.isActive(this)
-                || (inEditingMode() && imm.isActive(mWebTextView)))) {
-            imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
-        }
-    }
-
-    /*
-     * This method checks the current focus and cursor and potentially rebuilds
-     * mWebTextView to have the appropriate properties, such as password,
-     * multiline, and what text it contains.  It also removes it if necessary.
-     */
-    /* package */ void rebuildWebTextView() {
-        if (!sEnableWebTextView) {
-            return; // always use WebKit's text entry
-        }
-        // If the WebView does not have focus, do nothing until it gains focus.
-        if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
-            return;
-        }
-        boolean alreadyThere = inEditingMode();
-        // inEditingMode can only return true if mWebTextView is non-null,
-        // so we can safely call remove() if (alreadyThere)
-        if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
-            if (alreadyThere) {
-                mWebTextView.remove();
-            }
-            return;
-        }
-        // At this point, we know we have found an input field, so go ahead
-        // and create the WebTextView if necessary.
-        if (mWebTextView == null) {
-            mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillData.getQueryId());
-            // Initialize our generation number.
-            mTextGeneration = 0;
-        }
-        mWebTextView.updateTextSize();
-        updateWebTextViewPosition();
-        String text = nativeFocusCandidateText();
-        int nodePointer = nativeFocusCandidatePointer();
-        // This needs to be called before setType, which may call
-        // requestFormData, and it needs to have the correct nodePointer.
-        mWebTextView.setNodePointer(nodePointer);
-        mWebTextView.setType(nativeFocusCandidateType());
-        // Gravity needs to be set after setType
-        mWebTextView.setGravityForRtl(nativeFocusCandidateIsRtlText());
-        if (null == text) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "rebuildWebTextView null == text");
-            }
-            text = "";
-        }
-        mWebTextView.setTextAndKeepSelection(text);
-        InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && imm.isActive(mWebTextView)) {
-            imm.restartInput(mWebTextView);
-            mWebTextView.clearComposingText();
-        }
-        if (isFocused()) {
-            mWebTextView.requestFocus();
-        }
-    }
-
-    private void updateWebTextViewPosition() {
-        Rect visibleRect = new Rect();
-        calcOurContentVisibleRect(visibleRect);
-        // Note that sendOurVisibleRect calls viewToContent, so the coordinates
-        // should be in content coordinates.
-        Rect bounds = nativeFocusCandidateNodeBounds();
-        Rect vBox = contentToViewRect(bounds);
-        offsetByLayerScrollPosition(vBox);
-        mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
-        if (!Rect.intersects(bounds, visibleRect)) {
-            revealSelection();
-        }
-        updateWebTextViewPadding();
-    }
-
-    /**
-     * Update the padding of mWebTextView based on the native textfield/textarea
-     */
-    void updateWebTextViewPadding() {
-        Rect paddingRect = nativeFocusCandidatePaddingRect();
-        if (paddingRect != null) {
-            // Use contentToViewDimension since these are the dimensions of
-            // the padding.
-            mWebTextView.setPadding(
-                    contentToViewDimension(paddingRect.left),
-                    contentToViewDimension(paddingRect.top),
-                    contentToViewDimension(paddingRect.right),
-                    contentToViewDimension(paddingRect.bottom));
-        }
-    }
-
-    /**
-     * Tell webkit to put the cursor on screen.
-     */
-    /* package */ void revealSelection() {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessage(EventHub.REVEAL_SELECTION);
-        }
-    }
-
-    /**
-     * Called by WebTextView to find saved form data associated with the
-     * textfield
-     * @param name Name of the textfield.
-     * @param nodePointer Pointer to the node of the textfield, so it can be
-     *          compared to the currently focused textfield when the data is
-     *          retrieved.
-     * @param autoFillable true if WebKit has determined this field is part of
-     *          a form that can be auto filled.
-     * @param autoComplete true if the attribute "autocomplete" is set to true
-     *          on the textfield.
-     */
-    /* package */ void requestFormData(String name, int nodePointer,
-            boolean autoFillable, boolean autoComplete) {
-        if (mWebViewCore.getSettings().getSaveFormData()) {
-            Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
-            update.arg1 = nodePointer;
-            RequestFormData updater = new RequestFormData(name, getUrl(),
-                    update, autoFillable, autoComplete);
-            Thread t = new Thread(updater);
-            t.start();
-        }
-    }
-
-    /**
-     * Pass a message to find out the <label> associated with the <input>
-     * identified by nodePointer
-     * @param framePointer Pointer to the frame containing the <input> node
-     * @param nodePointer Pointer to the node for which a <label> is desired.
-     */
-    /* package */ void requestLabel(int framePointer, int nodePointer) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
-                nodePointer);
-    }
-
-    /*
-     * This class requests an Adapter for the WebTextView which shows past
-     * entries stored in the database.  It is a Runnable so that it can be done
-     * in its own thread, without slowing down the UI.
-     */
-    private class RequestFormData implements Runnable {
-        private String mName;
-        private String mUrl;
-        private Message mUpdateMessage;
-        private boolean mAutoFillable;
-        private boolean mAutoComplete;
-        private WebSettings mWebSettings;
-
-        public RequestFormData(String name, String url, Message msg,
-                boolean autoFillable, boolean autoComplete) {
-            mName = name;
-            mUrl = WebTextView.urlForAutoCompleteData(url);
-            mUpdateMessage = msg;
-            mAutoFillable = autoFillable;
-            mAutoComplete = autoComplete;
-            mWebSettings = getSettings();
-        }
-
-        @Override
-        public void run() {
-            ArrayList<String> pastEntries = new ArrayList<String>();
-
-            if (mAutoFillable) {
-                // Note that code inside the adapter click handler in WebTextView depends
-                // on the AutoFill item being at the top of the drop down list. If you change
-                // the order, make sure to do it there too!
-                if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
-                    pastEntries.add(getResources().getText(
-                            com.android.internal.R.string.autofill_this_form).toString() +
-                            " " +
-                            mAutoFillData.getPreviewString());
-                    mWebTextView.setAutoFillProfileIsSet(true);
-                } else {
-                    // There is no autofill profile set up yet, so add an option that
-                    // will invite the user to set their profile up.
-                    pastEntries.add(getResources().getText(
-                            com.android.internal.R.string.setup_autofill).toString());
-                    mWebTextView.setAutoFillProfileIsSet(false);
-                }
-            }
-
-            if (mAutoComplete) {
-                pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
-            }
-
-            if (pastEntries.size() > 0) {
-                AutoCompleteAdapter adapter = new
-                        AutoCompleteAdapter(mContext, pastEntries);
-                mUpdateMessage.obj = adapter;
-                mUpdateMessage.sendToTarget();
-            }
-        }
-    }
-
-    /**
-     * Dump the display tree to "/sdcard/displayTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpDisplayTree() {
-        nativeDumpDisplayTree(getUrl());
-    }
-
-    /**
-     * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
-     * "/sdcard/domTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpDomTree(boolean toFile) {
-        mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
-    }
-
-    /**
-     * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
-     * to "/sdcard/renderTree.txt"
-     *
-     * @hide debug only
-     */
-    public void dumpRenderTree(boolean toFile) {
-        mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
-    }
-
-    /**
-     * Called by DRT on UI thread, need to proxy to WebCore thread.
-     *
-     * @hide debug only
-     */
-    public void useMockDeviceOrientation() {
-        mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * @hide debug only
-     */
-    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
-            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
-        mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
-                canProvideGamma, gamma);
-    }
-
-    // This is used to determine long press with the center key.  Does not
-    // affect long press with the trackball/touch.
-    private boolean mGotCenterDown = false;
-
-    @Override
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-        // send complex characters to webkit for use by JS and plugins
-        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-            // return true as DOM handles the key
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isEnterActionKey(int keyCode) {
-        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER
-                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
-                    + "keyCode=" + keyCode
-                    + ", " + event + ", unicode=" + event.getUnicodeChar());
-        }
-        if (mIsCaretSelection) {
-            selectionDone();
-        }
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-
-        // don't implement accelerator keys here; defer to host application
-        if (event.isCtrlPressed()) {
-            return false;
-        }
-
-        if (mNativeClass == 0) {
-            return false;
-        }
-
-        // do this hack up front, so it always works, regardless of touch-mode
-        if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
-            mAutoRedraw = !mAutoRedraw;
-            if (mAutoRedraw) {
-                invalidate();
-            }
-            return true;
-        }
-
-        // Bubble up the key event if
-        // 1. it is a system key; or
-        // 2. the host application wants to handle it;
-        if (event.isSystem()
-                || mCallbackProxy.uiOverrideKeyEvent(event)) {
-            return false;
-        }
-
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // if an accessibility injector is present (no JavaScript enabled or the site
-                    // opts out injecting our JavaScript screen reader) we let it decide whether
-                    // to act on and consume the event.
-                    return true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
-            if (event.hasNoModifiers()) {
-                pageUp(false);
-                return true;
-            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                pageUp(true);
-                return true;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
-            if (event.hasNoModifiers()) {
-                pageDown(false);
-                return true;
-            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                pageDown(true);
-                return true;
-            }
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
-            pageUp(true);
-            return true;
-        }
-
-        if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
-            pageDown(true);
-            return true;
-        }
-
-        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
-                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-            switchOutDrawHistory();
-            if (nativePageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
-                return true;
-            }
-            if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                switch (keyCode) {
-                    case KeyEvent.KEYCODE_DPAD_UP:
-                        pageUp(true);
-                        return true;
-                    case KeyEvent.KEYCODE_DPAD_DOWN:
-                        pageDown(true);
-                        return true;
-                    case KeyEvent.KEYCODE_DPAD_LEFT:
-                        nativeClearCursor(); // start next trackball movement from page edge
-                        return pinScrollTo(0, mScrollY, true, 0);
-                    case KeyEvent.KEYCODE_DPAD_RIGHT:
-                        nativeClearCursor(); // start next trackball movement from page edge
-                        return pinScrollTo(mContentWidth, mScrollY, true, 0);
-                }
-            }
-            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
-                playSoundEffect(keyCodeToSoundsEffect(keyCode));
-                return true;
-            }
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
-        }
-
-        if (isEnterActionKey(keyCode)) {
-            switchOutDrawHistory();
-            boolean wantsKeyEvents = nativeCursorNodePointer() == 0
-                || nativeCursorWantsKeyEvents();
-            if (event.getRepeatCount() == 0) {
-                if (mSelectingText) {
-                    return true; // discard press if copy in progress
-                }
-                mGotCenterDown = true;
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
-                if (!wantsKeyEvents) return true;
-            }
-            // Bubble up the key event as WebView doesn't handle it
-            if (!wantsKeyEvents) return false;
-        }
-
-        if (getSettings().getNavDump()) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_4:
-                    dumpDisplayTree();
-                    break;
-                case KeyEvent.KEYCODE_5:
-                case KeyEvent.KEYCODE_6:
-                    dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
-                    break;
-                case KeyEvent.KEYCODE_7:
-                case KeyEvent.KEYCODE_8:
-                    dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
-                    break;
-            }
-        }
-
-        if (nativeCursorIsTextInput()) {
-            // This message will put the node in focus, for the DOM's notion
-            // of focus.
-            mWebViewCore.sendMessage(EventHub.FAKE_CLICK, nativeCursorFramePointer(),
-                    nativeCursorNodePointer());
-            // This will bring up the WebTextView and put it in focus, for
-            // our view system's notion of focus
-            rebuildWebTextView();
-            // Now we need to pass the event to it
-            if (inEditingMode()) {
-                mWebTextView.setDefaultSelection();
-                return mWebTextView.dispatchKeyEvent(event);
-            }
-        } else if (nativeHasFocusNode()) {
-            // In this case, the cursor is not on a text input, but the focus
-            // might be.  Check it, and if so, hand over to the WebTextView.
-            rebuildWebTextView();
-            if (inEditingMode()) {
-                mWebTextView.setDefaultSelection();
-                return mWebTextView.dispatchKeyEvent(event);
-            }
-        }
-
-        // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeCursorWantsKeyEvents() || true) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-            // return true as DOM handles the key
-            return true;
-        }
-
-        // Bubble up the key event as WebView doesn't handle it
-        return false;
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
-                    + ", " + event + ", unicode=" + event.getUnicodeChar());
-        }
-        if (mBlockWebkitViewMessages) {
-            return false;
-        }
-
-        if (mNativeClass == 0) {
-            return false;
-        }
-
-        // special CALL handling when cursor node's href is "tel:XXX"
-        if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
-            String text = nativeCursorText();
-            if (!nativeCursorIsTextInput() && text != null
-                    && text.startsWith(SCHEME_TEL)) {
-                Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
-                getContext().startActivity(intent);
-                return true;
-            }
-        }
-
-        // Bubble up the key event if
-        // 1. it is a system key; or
-        // 2. the host application wants to handle it;
-        if (event.isSystem()
-                || mCallbackProxy.uiOverrideKeyEvent(event)) {
-            return false;
-        }
-
-        // accessibility support
-        if (accessibilityScriptInjected()) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                // if an accessibility script is injected we delegate to it the key handling.
-                // this script is a screen reader which is a fully fledged solution for blind
-                // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-                return true;
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityScriptInjected = false;
-            }
-        } else if (mAccessibilityInjector != null) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                if (mAccessibilityInjector.onKeyEvent(event)) {
-                    // if an accessibility injector is present (no JavaScript enabled or the site
-                    // opts out injecting our JavaScript screen reader) we let it decide whether to
-                    // act on and consume the event.
-                    return true;
-                }
-            } else {
-                // Clean up if accessibility was disabled after loading the current URL.
-                mAccessibilityInjector = null;
-            }
-        }
-
-        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
-                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-            if (nativePageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
-                return true;
-            }
-            // always handle the navigation keys in the UI thread
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
-        }
-
-        if (isEnterActionKey(keyCode)) {
-            // remove the long press message first
-            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
-            mGotCenterDown = false;
-
-            if (mSelectingText) {
-                copySelection();
-                selectionDone();
-                return true; // discard press if copy in progress
-            }
-
-            if (!sDisableNavcache) {
-                // perform the single click
-                Rect visibleRect = sendOurVisibleRect();
-                // Note that sendOurVisibleRect calls viewToContent, so the
-                // coordinates should be in content coordinates.
-                if (!nativeCursorIntersects(visibleRect)) {
-                    return false;
-                }
-                WebViewCore.CursorData data = cursorData();
-                mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-                playSoundEffect(SoundEffectConstants.CLICK);
-                if (nativeCursorIsTextInput()) {
-                    rebuildWebTextView();
-                    centerKeyPressOnTextField();
-                    if (inEditingMode()) {
-                        mWebTextView.setDefaultSelection();
-                    }
-                    return true;
-                }
-                clearTextEntry();
-                nativeShowCursorTimed();
-                if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
-                    return true;
-                }
-                if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
-                    mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
-                            nativeCursorNodePointer());
-                    return true;
-                }
-            }
-        }
-
-        // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeCursorWantsKeyEvents() || true) {
-            // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
-            // return true as DOM handles the key
-            return true;
-        }
-
-        // Bubble up the key event as WebView doesn't handle it
-        return false;
-    }
-
-    private boolean startSelectActionMode() {
-        mSelectCallback = new SelectActionModeCallback();
-        mSelectCallback.setTextSelected(!mIsCaretSelection);
-        mSelectCallback.setWebView(this);
-        if (startActionMode(mSelectCallback) == null) {
-            // There is no ActionMode, so do not allow the user to modify a
-            // selection.
-            selectionDone();
-            return false;
-        }
-        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        return true;
-    }
-
-    private void syncSelectionCursors() {
-        mSelectCursorBaseLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase);
-        mSelectCursorExtentLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent);
-    }
-
-    private boolean setupWebkitSelect() {
-        syncSelectionCursors();
-        ClipboardManager cm = (ClipboardManager)(mContext
-                .getSystemService(Context.CLIPBOARD_SERVICE));
-        if (!mIsCaretSelection || cm.hasPrimaryClip()) {
-            if (!startSelectActionMode()) {
-                selectionDone();
-                return false;
-            }
-        }
-        mSelectingText = true;
-        mTouchMode = TOUCH_DRAG_MODE;
-        return true;
-    }
-
-    private void updateWebkitSelection() {
-        int[] handles = null;
-        if (mIsCaretSelection) {
-            mSelectCursorExtent.set(mSelectCursorBase);
-        }
-        if (mSelectingText) {
-            handles = new int[4];
-            handles[0] = mSelectCursorBase.centerX();
-            handles[1] = mSelectCursorBase.centerY();
-            handles[2] = mSelectCursorExtent.centerX();
-            handles[3] = mSelectCursorExtent.centerY();
-        } else {
-            nativeSetTextSelection(mNativeClass, 0);
-        }
-        mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
-        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles);
-    }
-
-    private void resetCaretTimer() {
-        mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-        if (!mSelectionStarted) {
-            mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
-                    CARET_HANDLE_STAMINA_MS);
-        }
-    }
-
     /**
      * Use this method to put the WebView into text selection mode.
      * Do not rely on this functionality; it will be deprecated in the future.
@@ -5966,168 +1433,7 @@
     @Deprecated
     public void emulateShiftHeld() {
         checkThread();
-    }
-
-    /**
-     * Select all of the text in this WebView.
-     *
-     * @hide This is an implementation detail.
-     */
-    public void selectAll() {
-        mWebViewCore.sendMessage(EventHub.SELECT_ALL);
-    }
-
-    /**
-     * Called when the selection has been removed.
-     */
-    void selectionDone() {
-        if (mSelectingText) {
-            mSelectingText = false;
-            // finish is idempotent, so this is fine even if selectionDone was
-            // called by mSelectCallback.onDestroyActionMode
-            if (mSelectCallback != null) {
-                mSelectCallback.finish();
-                mSelectCallback = null;
-            }
-            if (!mIsCaretSelection) {
-                updateWebkitSelection();
-            }
-            mIsCaretSelection = false;
-            invalidate(); // redraw without selection
-            mAutoScrollX = 0;
-            mAutoScrollY = 0;
-            mSentAutoScrollMessage = false;
-        }
-    }
-
-    /**
-     * Copy the selection to the clipboard
-     *
-     * @hide This is an implementation detail.
-     */
-    public boolean copySelection() {
-        boolean copiedSomething = false;
-        String selection = getSelection();
-        if (selection != null && selection != "") {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "copySelection \"" + selection + "\"");
-            }
-            Toast.makeText(mContext
-                    , com.android.internal.R.string.text_copied
-                    , Toast.LENGTH_SHORT).show();
-            copiedSomething = true;
-            ClipboardManager cm = (ClipboardManager)getContext()
-                    .getSystemService(Context.CLIPBOARD_SERVICE);
-            cm.setText(selection);
-            int[] handles = new int[4];
-            getSelectionHandles(handles);
-            mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
-        }
-        invalidate(); // remove selection region and pointer
-        return copiedSomething;
-    }
-
-    /**
-     * Cut the selected text into the clipboard
-     *
-     * @hide This is an implementation detail
-     */
-    public void cutSelection() {
-        copySelection();
-        int[] handles = new int[4];
-        getSelectionHandles(handles);
-        mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
-    }
-
-    /**
-     * Paste text from the clipboard to the cursor position.
-     *
-     * @hide This is an implementation detail
-     */
-    public void pasteFromClipboard() {
-        ClipboardManager cm = (ClipboardManager)getContext()
-                .getSystemService(Context.CLIPBOARD_SERVICE);
-        ClipData clipData = cm.getPrimaryClip();
-        if (clipData != null) {
-            ClipData.Item clipItem = clipData.getItemAt(0);
-            CharSequence pasteText = clipItem.getText();
-            if (pasteText != null) {
-                int[] handles = new int[4];
-                getSelectionHandles(handles);
-                mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
-                mWebViewCore.sendMessage(EventHub.INSERT_TEXT,
-                        pasteText.toString());
-            }
-        }
-    }
-
-    /**
-     * @hide This is an implementation detail.
-     */
-    public SearchBox getSearchBox() {
-        if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
-            return null;
-        }
-        return mWebViewCore.getBrowserFrame().getSearchBox();
-    }
-
-    /**
-     * Returns the currently highlighted text as a string.
-     */
-    String getSelection() {
-        if (mNativeClass == 0) return "";
-        return nativeGetSelection();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (hasWindowFocus()) setActive(true);
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        if (mGlobalLayoutListener == null) {
-            mGlobalLayoutListener = new InnerGlobalLayoutListener();
-            treeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
-        }
-        if (mScrollChangedListener == null) {
-            mScrollChangedListener = new InnerScrollChangedListener();
-            treeObserver.addOnScrollChangedListener(mScrollChangedListener);
-        }
-
-        addAccessibilityApisToJavaScript();
-
-        mTouchEventQueue.reset();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        clearHelpers();
-        mZoomManager.dismissZoomPicker();
-        if (hasWindowFocus()) setActive(false);
-
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        if (mGlobalLayoutListener != null) {
-            treeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
-            mGlobalLayoutListener = null;
-        }
-        if (mScrollChangedListener != null) {
-            treeObserver.removeOnScrollChangedListener(mScrollChangedListener);
-            mScrollChangedListener = null;
-        }
-
-        removeAccessibilityApisFromJavaScript();
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        // The zoomManager may be null if the webview is created from XML that
-        // specifies the view's visibility param as not visible (see http://b/2794841)
-        if (visibility != View.VISIBLE && mZoomManager != null) {
-            mZoomManager.dismissZoomPicker();
-        }
-        updateDrawingState();
+        mProvider.emulateShiftHeld();
     }
 
     /**
@@ -6158,1569 +1464,15 @@
     public void onGlobalFocusChanged(View oldFocus, View newFocus) {
     }
 
-    void setActive(boolean active) {
-        if (active) {
-            if (hasFocus()) {
-                // If our window regained focus, and we have focus, then begin
-                // drawing the cursor ring
-                mDrawCursorRing = !inEditingMode();
-                setFocusControllerActive(true);
-            } else {
-                mDrawCursorRing = false;
-                if (!inEditingMode()) {
-                    // If our window gained focus, but we do not have it, do not
-                    // draw the cursor ring.
-                    setFocusControllerActive(false);
-                }
-                // We do not call recordButtons here because we assume
-                // that when we lost focus, or window focus, it got called with
-                // false for the first parameter
-            }
-        } else {
-            if (!mZoomManager.isZoomPickerVisible()) {
-                /*
-                 * The external zoom controls come in their own window, so our
-                 * window loses focus. Our policy is to not draw the cursor ring
-                 * if our window is not focused, but this is an exception since
-                 * the user can still navigate the web page with the zoom
-                 * controls showing.
-                 */
-                mDrawCursorRing = false;
-            }
-            mKeysPressed.clear();
-            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-            mTouchMode = TOUCH_DONE_MODE;
-            setFocusControllerActive(false);
-        }
-        invalidate();
-    }
-
-    // To avoid drawing the cursor ring, and remove the TextView when our window
-    // loses focus.
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        setActive(hasWindowFocus);
-        if (hasWindowFocus) {
-            JWebCoreJavaBridge.setActiveWebView(this);
-            if (mPictureUpdatePausedForFocusChange) {
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
-                mPictureUpdatePausedForFocusChange = false;
-            }
-        } else {
-            JWebCoreJavaBridge.removeActiveWebView(this);
-            final WebSettings settings = getSettings();
-            if (settings != null && settings.enableSmoothTransition() &&
-                    mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
-                WebViewCore.pauseUpdatePicture(mWebViewCore);
-                mPictureUpdatePausedForFocusChange = true;
-            }
-        }
-        super.onWindowFocusChanged(hasWindowFocus);
-    }
-
-    /*
-     * Pass a message to WebCore Thread, telling the WebCore::Page's
-     * FocusController to be  "inactive" so that it will
-     * not draw the blinking cursor.  It gets set to "active" to draw the cursor
-     * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
-     */
-    /* package */ void setFocusControllerActive(boolean active) {
-        if (mWebViewCore == null) return;
-        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
-        // Need to send this message after the document regains focus.
-        if (active && mListBoxMessage != null) {
-            mWebViewCore.sendMessage(mListBoxMessage);
-            mListBoxMessage = null;
-        }
-    }
-
-    @Override
-    protected void onFocusChanged(boolean focused, int direction,
-            Rect previouslyFocusedRect) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
-        }
-        if (focused) {
-            // When we regain focus, if we have window focus, resume drawing
-            // the cursor ring
-            if (hasWindowFocus()) {
-                mDrawCursorRing = !inEditingMode();
-                setFocusControllerActive(true);
-            //} else {
-                // The WebView has gained focus while we do not have
-                // windowfocus.  When our window lost focus, we should have
-                // called recordButtons(false...)
-            }
-        } else {
-            // When we lost focus, unless focus went to the TextView (which is
-            // true if we are in editing mode), stop drawing the cursor ring.
-            mDrawCursorRing = false;
-            if (!inEditingMode()) {
-                setFocusControllerActive(false);
-            }
-            mKeysPressed.clear();
-        }
-
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-    }
-
-    void setGLRectViewport() {
-        // Use the getGlobalVisibleRect() to get the intersection among the parents
-        // visible == false means we're clipped - send a null rect down to indicate that
-        // we should not draw
-        boolean visible = getGlobalVisibleRect(mGLRectViewport);
-        if (visible) {
-            // Then need to invert the Y axis, just for GL
-            View rootView = getRootView();
-            int rootViewHeight = rootView.getHeight();
-            mViewRectViewport.set(mGLRectViewport);
-            int savedWebViewBottom = mGLRectViewport.bottom;
-            mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeightImpl();
-            mGLRectViewport.top = rootViewHeight - savedWebViewBottom;
-            mGLViewportEmpty = false;
-        } else {
-            mGLViewportEmpty = true;
-        }
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
-                mGLViewportEmpty ? null : mViewRectViewport,
-                mVisibleContentRect, getScale());
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        boolean changed = super.setFrame(left, top, right, bottom);
-        if (!changed && mHeightCanMeasure) {
-            // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
-            // in WebViewCore after we get the first layout. We do call
-            // requestLayout() when we get contentSizeChanged(). But the View
-            // system won't call onSizeChanged if the dimension is not changed.
-            // In this case, we need to call sendViewSizeZoom() explicitly to
-            // notify the WebKit about the new dimensions.
-            sendViewSizeZoom(false);
-        }
-        setGLRectViewport();
-        return changed;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int ow, int oh) {
-        super.onSizeChanged(w, h, ow, oh);
-
-        // adjust the max viewport width depending on the view dimensions. This
-        // is to ensure the scaling is not going insane. So do not shrink it if
-        // the view size is temporarily smaller, e.g. when soft keyboard is up.
-        int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
-        if (newMaxViewportWidth > sMaxViewportWidth) {
-            sMaxViewportWidth = newMaxViewportWidth;
-        }
-
-        mZoomManager.onSizeChanged(w, h, ow, oh);
-
-        if (mLoadedPicture != null && mDelaySetPicture == null) {
-            // Size changes normally result in a new picture
-            // Re-set the loaded picture to simulate that
-            // However, do not update the base layer as that hasn't changed
-            setNewPicture(mLoadedPicture, false);
-        }
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        if (!mInOverScrollMode) {
-            sendOurVisibleRect();
-            // update WebKit if visible title bar height changed. The logic is same
-            // as getVisibleTitleHeightImpl.
-            int titleHeight = getTitleHeight();
-            if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
-                sendViewSizeZoom(false);
-            }
-        }
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        switch (event.getAction()) {
-            case KeyEvent.ACTION_DOWN:
-                mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
-                break;
-            case KeyEvent.ACTION_MULTIPLE:
-                // Always accept the action.
-                break;
-            case KeyEvent.ACTION_UP:
-                int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
-                if (location == -1) {
-                    // We did not receive the key down for this key, so do not
-                    // handle the key up.
-                    return false;
-                } else {
-                    // We did receive the key down.  Handle the key up, and
-                    // remove it from our pressed keys.
-                    mKeysPressed.remove(location);
-                }
-                break;
-            default:
-                // Accept the action.  This should not happen, unless a new
-                // action is added to KeyEvent.
-                break;
-        }
-        if (inEditingMode() && mWebTextView.isFocused()) {
-            // Ensure that the WebTextView gets the event, even if it does
-            // not currently have a bounds.
-            return mWebTextView.dispatchKeyEvent(event);
-        } else {
-            return super.dispatchKeyEvent(event);
-        }
-    }
-
-    /*
-     * Here is the snap align logic:
-     * 1. If it starts nearly horizontally or vertically, snap align;
-     * 2. If there is a dramitic direction change, let it go;
-     *
-     * Adjustable parameters. Angle is the radians on a unit circle, limited
-     * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
-     */
-    private static final float HSLOPE_TO_START_SNAP = .25f;
-    private static final float HSLOPE_TO_BREAK_SNAP = .4f;
-    private static final float VSLOPE_TO_START_SNAP = 1.25f;
-    private static final float VSLOPE_TO_BREAK_SNAP = .95f;
-    /*
-     *  These values are used to influence the average angle when entering
-     *  snap mode. If is is the first movement entering snap, we set the average
-     *  to the appropriate ideal. If the user is entering into snap after the
-     *  first movement, then we average the average angle with these values.
-     */
-    private static final float ANGLE_VERT = 2f;
-    private static final float ANGLE_HORIZ = 0f;
-    /*
-     *  The modified moving average weight.
-     *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
-     */
-    private static final float MMA_WEIGHT_N = 5;
-
-    private boolean hitFocusedPlugin(int contentX, int contentY) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
-            Rect r = nativeFocusNodeBounds();
-            Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
-                    + ", " + r.right + ", " + r.bottom + ")");
-        }
-        return nativeFocusIsPlugin()
-                && nativeFocusNodeBounds().contains(contentX, contentY);
-    }
-
-    private boolean shouldForwardTouchEvent() {
-        if (mFullScreenHolder != null) return true;
-        if (mBlockWebkitViewMessages) return false;
-        return mForwardTouchEvents
-                && !mSelectingText
-                && mPreventDefault != PREVENT_DEFAULT_IGNORE
-                && mPreventDefault != PREVENT_DEFAULT_NO;
-    }
-
-    private boolean inFullScreenMode() {
-        return mFullScreenHolder != null;
-    }
-
-    private void dismissFullScreenMode() {
-        if (inFullScreenMode()) {
-            mFullScreenHolder.hide();
-            mFullScreenHolder = null;
-            invalidate();
-        }
-    }
-
-    void onPinchToZoomAnimationStart() {
-        // cancel the single touch handling
-        cancelTouch();
-        onZoomAnimationStart();
-    }
-
-    void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
-        onZoomAnimationEnd();
-        // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
-        // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
-        // as it may trigger the unwanted fling.
-        mTouchMode = TOUCH_PINCH_DRAG;
-        mConfirmMove = true;
-        startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
-    }
-
-    // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
-    // layer is found.
-    private void startScrollingLayer(float x, float y) {
-        int contentX = viewToContentX((int) x + mScrollX);
-        int contentY = viewToContentY((int) y + mScrollY);
-        mCurrentScrollingLayerId = nativeScrollableLayer(contentX, contentY,
-                mScrollingLayerRect, mScrollingLayerBounds);
-        if (mCurrentScrollingLayerId != 0) {
-            mTouchMode = TOUCH_DRAG_LAYER_MODE;
-        }
-    }
-
-    // 1/(density * density) used to compute the distance between points.
-    // Computed in init().
-    private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
-
-    // The distance between two points reported in onTouchEvent scaled by the
-    // density of the screen.
-    private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        WebViewCore.CursorData data = cursorDataNoPosition();
-        data.mX = viewToContentX((int) event.getX() + mScrollX);
-        data.mY = viewToContentY((int) event.getY() + mScrollY);
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-        return true;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
-            return false;
-        }
-
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, ev + " at " + ev.getEventTime()
-                + " mTouchMode=" + mTouchMode
-                + " numPointers=" + ev.getPointerCount());
-        }
-
-        // If WebKit wasn't interested in this multitouch gesture, enqueue
-        // the event for handling directly rather than making the round trip
-        // to WebKit and back.
-        if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) {
-            passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence());
-        } else {
-            mTouchEventQueue.enqueueTouchEvent(ev);
-        }
-
-        // Since all events are handled asynchronously, we always want the gesture stream.
-        return true;
-    }
-
-    private float calculateDragAngle(int dx, int dy) {
-        dx = Math.abs(dx);
-        dy = Math.abs(dy);
-        return (float) Math.atan2(dy, dx);
-    }
-
-    /*
-     * Common code for single touch and multi-touch.
-     * (x, y) denotes current focus point, which is the touch point for single touch
-     * and the middle point for multi-touch.
-     */
-    private boolean handleTouchEventCommon(MotionEvent ev, int action, int x, int y) {
-        long eventTime = ev.getEventTime();
-
-        // Due to the touch screen edge effect, a touch closer to the edge
-        // always snapped to the edge. As getViewWidth() can be different from
-        // getWidth() due to the scrollbar, adjusting the point to match
-        // getViewWidth(). Same applied to the height.
-        x = Math.min(x, getViewWidth() - 1);
-        y = Math.min(y, getViewHeightWithTitle() - 1);
-
-        int deltaX = mLastTouchX - x;
-        int deltaY = mLastTouchY - y;
-        int contentX = viewToContentX(x + mScrollX);
-        int contentY = viewToContentY(y + mScrollY);
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                mPreventDefault = PREVENT_DEFAULT_NO;
-                mConfirmMove = false;
-                mInitialHitTestResult = null;
-                if (!mScroller.isFinished()) {
-                    // stop the current scroll animation, but if this is
-                    // the start of a fling, allow it to add to the current
-                    // fling's velocity
-                    mScroller.abortAnimation();
-                    mTouchMode = TOUCH_DRAG_START_MODE;
-                    mConfirmMove = true;
-                    nativeSetIsScrolling(false);
-                } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
-                    mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
-                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
-                    } else {
-                        // commit the short press action for the previous tap
-                        doShortPress();
-                        mTouchMode = TOUCH_INIT_MODE;
-                        mDeferTouchProcess = !mBlockWebkitViewMessages
-                                && (!inFullScreenMode() && mForwardTouchEvents)
-                                ? hitFocusedPlugin(contentX, contentY)
-                                : false;
-                    }
-                } else { // the normal case
-                    mTouchMode = TOUCH_INIT_MODE;
-                    mDeferTouchProcess = !mBlockWebkitViewMessages
-                            && (!inFullScreenMode() && mForwardTouchEvents)
-                            ? hitFocusedPlugin(contentX, contentY)
-                            : false;
-                    if (!mBlockWebkitViewMessages) {
-                        mWebViewCore.sendMessage(
-                                EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
-                    }
-                    if (sDisableNavcache) {
-                        TouchHighlightData data = new TouchHighlightData();
-                        data.mX = contentX;
-                        data.mY = contentY;
-                        data.mNativeLayerRect = new Rect();
-                        data.mNativeLayer = nativeScrollableLayer(
-                                contentX, contentY, data.mNativeLayerRect, null);
-                        data.mSlop = viewToContentDimension(mNavSlop);
-                        mTouchHighlightRegion.setEmpty();
-                        if (!mBlockWebkitViewMessages) {
-                            mTouchHighlightRequested = System.currentTimeMillis();
-                            mWebViewCore.sendMessageAtFrontOfQueue(
-                                    EventHub.HIT_TEST, data);
-                        }
-                        if (DEBUG_TOUCH_HIGHLIGHT) {
-                            if (getSettings().getNavDump()) {
-                                mTouchHighlightX = x + mScrollX;
-                                mTouchHighlightY = y + mScrollY;
-                                mPrivateHandler.postDelayed(new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        mTouchHighlightX = mTouchHighlightY = 0;
-                                        invalidate();
-                                    }
-                                }, TOUCH_HIGHLIGHT_ELAPSE_TIME);
-                            }
-                        }
-                    }
-                    if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
-                        EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
-                                (eventTime - mLastTouchUpTime), eventTime);
-                    }
-                    mSelectionStarted = false;
-                    if (mSelectingText) {
-                        int shiftedY = y - getTitleHeight() + mScrollY;
-                        int shiftedX = x + mScrollX;
-                        if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds()
-                                .contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorBase;
-                            mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-                        } else if (mSelectHandleLeft != null
-                                && mSelectHandleLeft.getBounds()
-                                    .contains(shiftedX, shiftedY)) {
-                                mSelectionStarted = true;
-                                mSelectDraggingCursor = mSelectCursorBase;
-                        } else if (mSelectHandleRight != null
-                                && mSelectHandleRight.getBounds()
-                                .contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorExtent;
-                        } else if (mIsCaretSelection) {
-                            selectionDone();
-                        }
-                        if (mSelectDraggingCursor != null) {
-                            mSelectDraggingOffset.set(
-                                    mSelectDraggingCursor.left - contentX,
-                                    mSelectDraggingCursor.top - contentY);
-                        }
-                        if (DebugFlags.WEB_VIEW) {
-                            Log.v(LOGTAG, "select=" + contentX + "," + contentY);
-                        }
-                    }
-                }
-                // Trigger the link
-                if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
-                        || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
-                    mPrivateHandler.sendEmptyMessageDelayed(
-                            SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
-                    mPrivateHandler.sendEmptyMessageDelayed(
-                            SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
-                    if (inFullScreenMode() || mDeferTouchProcess) {
-                        mPreventDefault = PREVENT_DEFAULT_YES;
-                    } else if (!mBlockWebkitViewMessages && mForwardTouchEvents) {
-                        mPreventDefault = PREVENT_DEFAULT_MAYBE_YES;
-                    } else {
-                        mPreventDefault = PREVENT_DEFAULT_NO;
-                    }
-                    // pass the touch events from UI thread to WebCore thread
-                    if (shouldForwardTouchEvent()) {
-                        TouchEventData ted = new TouchEventData();
-                        ted.mAction = action;
-                        ted.mIds = new int[1];
-                        ted.mIds[0] = ev.getPointerId(0);
-                        ted.mPoints = new Point[1];
-                        ted.mPoints[0] = new Point(contentX, contentY);
-                        ted.mPointsInView = new Point[1];
-                        ted.mPointsInView[0] = new Point(x, y);
-                        ted.mMetaState = ev.getMetaState();
-                        ted.mReprocess = mDeferTouchProcess;
-                        ted.mNativeLayer = nativeScrollableLayer(
-                                contentX, contentY, ted.mNativeLayerRect, null);
-                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                        mTouchEventQueue.preQueueTouchEventData(ted);
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                        if (mDeferTouchProcess) {
-                            // still needs to set them for compute deltaX/Y
-                            mLastTouchX = x;
-                            mLastTouchY = y;
-                            break;
-                        }
-                        if (!inFullScreenMode()) {
-                            mPrivateHandler.removeMessages(PREVENT_DEFAULT_TIMEOUT);
-                            mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                    .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
-                                            action, 0), TAP_TIMEOUT);
-                        }
-                    }
-                }
-                startTouch(x, y, eventTime);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                boolean firstMove = false;
-                if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
-                        >= mTouchSlopSquare) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                    mConfirmMove = true;
-                    firstMove = true;
-                    if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
-                        mTouchMode = TOUCH_INIT_MODE;
-                    }
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                }
-                if (mSelectingText && mSelectionStarted) {
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
-                    }
-                    ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    if (deltaX != 0 || deltaY != 0) {
-                        mSelectDraggingCursor.offsetTo(
-                                contentX + mSelectDraggingOffset.x,
-                                contentY + mSelectDraggingOffset.y);
-                        updateWebkitSelection();
-                        mLastTouchX = x;
-                        mLastTouchY = y;
-                        invalidate();
-                    }
-                    break;
-                }
-
-                // pass the touch events from UI thread to WebCore thread
-                if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
-                        || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
-                    TouchEventData ted = new TouchEventData();
-                    ted.mAction = action;
-                    ted.mIds = new int[1];
-                    ted.mIds[0] = ev.getPointerId(0);
-                    ted.mPoints = new Point[1];
-                    ted.mPoints[0] = new Point(contentX, contentY);
-                    ted.mPointsInView = new Point[1];
-                    ted.mPointsInView[0] = new Point(x, y);
-                    ted.mMetaState = ev.getMetaState();
-                    ted.mReprocess = mDeferTouchProcess;
-                    ted.mNativeLayer = mCurrentScrollingLayerId;
-                    ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                    mTouchEventQueue.preQueueTouchEventData(ted);
-                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                    mLastSentTouchTime = eventTime;
-                    if (mDeferTouchProcess) {
-                        break;
-                    }
-                    if (firstMove && !inFullScreenMode()) {
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
-                                        action, 0), TAP_TIMEOUT);
-                    }
-                }
-                if (mTouchMode == TOUCH_DONE_MODE
-                        || mPreventDefault == PREVENT_DEFAULT_YES) {
-                    // no dragging during scroll zoom animation, or when prevent
-                    // default is yes
-                    break;
-                }
-                if (mVelocityTracker == null) {
-                    Log.e(LOGTAG, "Got null mVelocityTracker when "
-                            + "mPreventDefault = " + mPreventDefault
-                            + " mDeferTouchProcess = " + mDeferTouchProcess
-                            + " mTouchMode = " + mTouchMode);
-                } else {
-                    mVelocityTracker.addMovement(ev);
-                }
-
-                if (mTouchMode != TOUCH_DRAG_MODE &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-
-                    if (!mConfirmMove) {
-                        break;
-                    }
-
-                    if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
-                            || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
-                        // track mLastTouchTime as we may need to do fling at
-                        // ACTION_UP
-                        mLastTouchTime = eventTime;
-                        break;
-                    }
-
-                    // Only lock dragging to one axis if we don't have a scale in progress.
-                    // Scaling implies free-roaming movement. Note this is only ever a question
-                    // if mZoomManager.supportsPanDuringZoom() is true.
-                    final ScaleGestureDetector detector =
-                      mZoomManager.getMultiTouchGestureDetector();
-                    mAverageAngle = calculateDragAngle(deltaX, deltaY);
-                    if (detector == null || !detector.isInProgress()) {
-                        // if it starts nearly horizontal or vertical, enforce it
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = ANGLE_HORIZ;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = ANGLE_VERT;
-                        }
-                    }
-
-                    mTouchMode = TOUCH_DRAG_MODE;
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-                    deltaX = 0;
-                    deltaY = 0;
-
-                    startScrollingLayer(x, y);
-                    startDrag();
-                }
-
-                // do pan
-                boolean done = false;
-                boolean keepScrollBarsVisible = false;
-                if (deltaX == 0 && deltaY == 0) {
-                    keepScrollBarsVisible = done = true;
-                } else {
-                    mAverageAngle +=
-                        (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
-                        / MMA_WEIGHT_N;
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if (mSnapScrollMode == SNAP_Y) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
-                                mSnapScrollMode = SNAP_NONE;
-                            }
-                        }
-                        if (mSnapScrollMode == SNAP_X) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
-                                mSnapScrollMode = SNAP_NONE;
-                            }
-                        }
-                    } else {
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
-                        }
-                    }
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
-                            deltaY = 0;
-                        } else {
-                            deltaX = 0;
-                        }
-                    }
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-
-                    if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
-                        mHeldMotionless = MOTIONLESS_FALSE;
-                        nativeSetIsScrolling(true);
-                    } else {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        nativeSetIsScrolling(false);
-                        keepScrollBarsVisible = true;
-                    }
-
-                    mLastTouchTime = eventTime;
-                }
-
-                doDrag(deltaX, deltaY);
-
-                // Turn off scrollbars when dragging a layer.
-                if (keepScrollBarsVisible &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    if (mHeldMotionless != MOTIONLESS_TRUE) {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        invalidate();
-                    }
-                    // keep the scrollbar on the screen even there is no scroll
-                    awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
-                            false);
-                    // Post a message so that we'll keep them alive while we're not scrolling.
-                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                            .obtainMessage(AWAKEN_SCROLL_BARS),
-                            ViewConfiguration.getScrollDefaultDelay());
-                    // return false to indicate that we can't pan out of the
-                    // view space
-                    return !done;
-                } else {
-                    mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                }
-                break;
-            }
-            case MotionEvent.ACTION_UP: {
-                if (!isFocused()) requestFocus();
-                // pass the touch events from UI thread to WebCore thread
-                if (shouldForwardTouchEvent()) {
-                    TouchEventData ted = new TouchEventData();
-                    ted.mIds = new int[1];
-                    ted.mIds[0] = ev.getPointerId(0);
-                    ted.mAction = action;
-                    ted.mPoints = new Point[1];
-                    ted.mPoints[0] = new Point(contentX, contentY);
-                    ted.mPointsInView = new Point[1];
-                    ted.mPointsInView[0] = new Point(x, y);
-                    ted.mMetaState = ev.getMetaState();
-                    ted.mReprocess = mDeferTouchProcess;
-                    ted.mNativeLayer = mCurrentScrollingLayerId;
-                    ted.mNativeLayerRect.set(mScrollingLayerRect);
-                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                    mTouchEventQueue.preQueueTouchEventData(ted);
-                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                }
-                mLastTouchUpTime = eventTime;
-                if (mSentAutoScrollMessage) {
-                    mAutoScrollX = mAutoScrollY = 0;
-                }
-                switch (mTouchMode) {
-                    case TOUCH_DOUBLE_TAP_MODE: // double tap
-                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                        if (inFullScreenMode() || mDeferTouchProcess) {
-                            TouchEventData ted = new TouchEventData();
-                            ted.mIds = new int[1];
-                            ted.mIds[0] = ev.getPointerId(0);
-                            ted.mAction = WebViewCore.ACTION_DOUBLETAP;
-                            ted.mPoints = new Point[1];
-                            ted.mPoints[0] = new Point(contentX, contentY);
-                            ted.mPointsInView = new Point[1];
-                            ted.mPointsInView[0] = new Point(x, y);
-                            ted.mMetaState = ev.getMetaState();
-                            ted.mReprocess = mDeferTouchProcess;
-                            ted.mNativeLayer = nativeScrollableLayer(
-                                    contentX, contentY,
-                                    ted.mNativeLayerRect, null);
-                            ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                            mTouchEventQueue.preQueueTouchEventData(ted);
-                            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                        } else if (mPreventDefault != PREVENT_DEFAULT_YES){
-                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
-                            mTouchMode = TOUCH_DONE_MODE;
-                        }
-                        break;
-                    case TOUCH_INIT_MODE: // tap
-                    case TOUCH_SHORTPRESS_START_MODE:
-                    case TOUCH_SHORTPRESS_MODE:
-                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                        if (mConfirmMove) {
-                            Log.w(LOGTAG, "Miss a drag as we are waiting for" +
-                                    " WebCore's response for touch down.");
-                            if (mPreventDefault != PREVENT_DEFAULT_YES
-                                    && (computeMaxScrollX() > 0
-                                            || computeMaxScrollY() > 0)) {
-                                // If the user has performed a very quick touch
-                                // sequence it is possible that we may get here
-                                // before WebCore has had a chance to process the events.
-                                // In this case, any call to preventDefault in the
-                                // JS touch handler will not have been executed yet.
-                                // Hence we will see both the UI (now) and WebCore
-                                // (when context switches) handling the event,
-                                // regardless of whether the web developer actually
-                                // doeses preventDefault in their touch handler. This
-                                // is the nature of our asynchronous touch model.
-
-                                // we will not rewrite drag code here, but we
-                                // will try fling if it applies.
-                                WebViewCore.reducePriority();
-                                // to get better performance, pause updating the
-                                // picture
-                                WebViewCore.pauseUpdatePicture(mWebViewCore);
-                                // fall through to TOUCH_DRAG_MODE
-                            } else {
-                                // WebKit may consume the touch event and modify
-                                // DOM. drawContentPicture() will be called with
-                                // animateSroll as true for better performance.
-                                // Force redraw in high-quality.
-                                invalidate();
-                                break;
-                            }
-                        } else {
-                            if (mSelectingText) {
-                                // tapping on selection or controls does nothing
-                                if (!mSelectionStarted) {
-                                    selectionDone();
-                                }
-                                break;
-                            }
-                            // only trigger double tap if the WebView is
-                            // scalable
-                            if (mTouchMode == TOUCH_INIT_MODE
-                                    && (canZoomIn() || canZoomOut())) {
-                                mPrivateHandler.sendEmptyMessageDelayed(
-                                        RELEASE_SINGLE_TAP, ViewConfiguration
-                                                .getDoubleTapTimeout());
-                            } else {
-                                doShortPress();
-                            }
-                            break;
-                        }
-                    case TOUCH_DRAG_MODE:
-                    case TOUCH_DRAG_LAYER_MODE:
-                        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-                        // if the user waits a while w/o moving before the
-                        // up, we don't want to do a fling
-                        if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
-                            if (mVelocityTracker == null) {
-                                Log.e(LOGTAG, "Got null mVelocityTracker when "
-                                        + "mPreventDefault = "
-                                        + mPreventDefault
-                                        + " mDeferTouchProcess = "
-                                        + mDeferTouchProcess);
-                            } else {
-                                mVelocityTracker.addMovement(ev);
-                            }
-                            // set to MOTIONLESS_IGNORE so that it won't keep
-                            // removing and sending message in
-                            // drawCoreAndCursorRing()
-                            mHeldMotionless = MOTIONLESS_IGNORE;
-                            doFling();
-                            break;
-                        } else {
-                            if (mScroller.springBack(mScrollX, mScrollY, 0,
-                                    computeMaxScrollX(), 0,
-                                    computeMaxScrollY())) {
-                                invalidate();
-                            }
-                        }
-                        // redraw in high-quality, as we're done dragging
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        invalidate();
-                        // fall through
-                    case TOUCH_DRAG_START_MODE:
-                        // TOUCH_DRAG_START_MODE should not happen for the real
-                        // device as we almost certain will get a MOVE. But this
-                        // is possible on emulator.
-                        mLastVelocity = 0;
-                        WebViewCore.resumePriority();
-                        if (!mSelectingText) {
-                            WebViewCore.resumeUpdatePicture(mWebViewCore);
-                        }
-                        break;
-                }
-                stopTouch();
-                break;
-            }
-            case MotionEvent.ACTION_CANCEL: {
-                if (mTouchMode == TOUCH_DRAG_MODE) {
-                    mScroller.springBack(mScrollX, mScrollY, 0,
-                            computeMaxScrollX(), 0, computeMaxScrollY());
-                    invalidate();
-                }
-                cancelWebCoreTouchEvent(contentX, contentY, false);
-                cancelTouch();
-                break;
-            }
-        }
-        return true;
-    }
-
-    private void passMultiTouchToWebKit(MotionEvent ev, long sequence) {
-        TouchEventData ted = new TouchEventData();
-        ted.mAction = ev.getActionMasked();
-        final int count = ev.getPointerCount();
-        ted.mIds = new int[count];
-        ted.mPoints = new Point[count];
-        ted.mPointsInView = new Point[count];
-        for (int c = 0; c < count; c++) {
-            ted.mIds[c] = ev.getPointerId(c);
-            int x = viewToContentX((int) ev.getX(c) + mScrollX);
-            int y = viewToContentY((int) ev.getY(c) + mScrollY);
-            ted.mPoints[c] = new Point(x, y);
-            ted.mPointsInView[c] = new Point((int) ev.getX(c), (int) ev.getY(c));
-        }
-        if (ted.mAction == MotionEvent.ACTION_POINTER_DOWN
-            || ted.mAction == MotionEvent.ACTION_POINTER_UP) {
-            ted.mActionIndex = ev.getActionIndex();
-        }
-        ted.mMetaState = ev.getMetaState();
-        ted.mReprocess = true;
-        ted.mMotionEvent = MotionEvent.obtain(ev);
-        ted.mSequence = sequence;
-        mTouchEventQueue.preQueueTouchEventData(ted);
-        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-        cancelLongPress();
-        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-    }
-
-    void handleMultiTouchInWebView(MotionEvent ev) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "multi-touch: " + ev + " at " + ev.getEventTime()
-                + " mTouchMode=" + mTouchMode
-                + " numPointers=" + ev.getPointerCount()
-                + " scrolloffset=(" + mScrollX + "," + mScrollY + ")");
-        }
-
-        final ScaleGestureDetector detector =
-            mZoomManager.getMultiTouchGestureDetector();
-
-        // A few apps use WebView but don't instantiate gesture detector.
-        // We don't need to support multi touch for them.
-        if (detector == null) return;
-
-        float x = ev.getX();
-        float y = ev.getY();
-
-        if (mPreventDefault != PREVENT_DEFAULT_YES) {
-            detector.onTouchEvent(ev);
-
-            if (detector.isInProgress()) {
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "detector is in progress");
-                }
-                mLastTouchTime = ev.getEventTime();
-                x = detector.getFocusX();
-                y = detector.getFocusY();
-
-                cancelLongPress();
-                mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                if (!mZoomManager.supportsPanDuringZoom()) {
-                    return;
-                }
-                mTouchMode = TOUCH_DRAG_MODE;
-                if (mVelocityTracker == null) {
-                    mVelocityTracker = VelocityTracker.obtain();
-                }
-            }
-        }
-
-        int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_POINTER_DOWN) {
-            cancelTouch();
-            action = MotionEvent.ACTION_DOWN;
-        } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
-            // set mLastTouchX/Y to the remaining points for multi-touch.
-            mLastTouchX = Math.round(x);
-            mLastTouchY = Math.round(y);
-        } else if (action == MotionEvent.ACTION_MOVE) {
-            // negative x or y indicate it is on the edge, skip it.
-            if (x < 0 || y < 0) {
-                return;
-            }
-        }
-
-        handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
-    }
-
-    private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
-        if (shouldForwardTouchEvent()) {
-            if (removeEvents) {
-                mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
-            }
-            TouchEventData ted = new TouchEventData();
-            ted.mIds = new int[1];
-            ted.mIds[0] = 0;
-            ted.mPoints = new Point[1];
-            ted.mPoints[0] = new Point(x, y);
-            ted.mPointsInView = new Point[1];
-            int viewX = contentToViewX(x) - mScrollX;
-            int viewY = contentToViewY(y) - mScrollY;
-            ted.mPointsInView[0] = new Point(viewX, viewY);
-            ted.mAction = MotionEvent.ACTION_CANCEL;
-            ted.mNativeLayer = nativeScrollableLayer(
-                    x, y, ted.mNativeLayerRect, null);
-            ted.mSequence = mTouchEventQueue.nextTouchSequence();
-            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-            mPreventDefault = PREVENT_DEFAULT_IGNORE;
-
-            if (removeEvents) {
-                // Mark this after sending the message above; we should
-                // be willing to ignore the cancel event that we just sent.
-                mTouchEventQueue.ignoreCurrentlyMissingEvents();
-            }
-        }
-    }
-
-    private void startTouch(float x, float y, long eventTime) {
-        // Remember where the motion event started
-        mStartTouchX = mLastTouchX = Math.round(x);
-        mStartTouchY = mLastTouchY = Math.round(y);
-        mLastTouchTime = eventTime;
-        mVelocityTracker = VelocityTracker.obtain();
-        mSnapScrollMode = SNAP_NONE;
-        mPrivateHandler.sendEmptyMessageDelayed(UPDATE_SELECTION,
-                ViewConfiguration.getTapTimeout());
-    }
-
-    private void startDrag() {
-        WebViewCore.reducePriority();
-        // to get better performance, pause updating the picture
-        WebViewCore.pauseUpdatePicture(mWebViewCore);
-        nativeSetIsScrolling(true);
-
-        if (!mDragFromTextInput) {
-            nativeHideCursor();
-        }
-
-        if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
-                || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
-            mZoomManager.invokeZoomPicker();
-        }
-    }
-
-    private void doDrag(int deltaX, int deltaY) {
-        if ((deltaX | deltaY) != 0) {
-            int oldX = mScrollX;
-            int oldY = mScrollY;
-            int rangeX = computeMaxScrollX();
-            int rangeY = computeMaxScrollY();
-            // Check for the original scrolling layer in case we change
-            // directions.  mTouchMode might be TOUCH_DRAG_MODE if we have
-            // reached the edge of a layer but mScrollingLayer will be non-zero
-            // if we initiated the drag on a layer.
-            if (mCurrentScrollingLayerId != 0) {
-                final int contentX = viewToContentDimension(deltaX);
-                final int contentY = viewToContentDimension(deltaY);
-
-                // Check the scrolling bounds to see if we will actually do any
-                // scrolling.  The rectangle is in document coordinates.
-                final int maxX = mScrollingLayerRect.right;
-                final int maxY = mScrollingLayerRect.bottom;
-                final int resultX = Math.max(0,
-                        Math.min(mScrollingLayerRect.left + contentX, maxX));
-                final int resultY = Math.max(0,
-                        Math.min(mScrollingLayerRect.top + contentY, maxY));
-
-                if (resultX != mScrollingLayerRect.left ||
-                        resultY != mScrollingLayerRect.top) {
-                    // In case we switched to dragging the page.
-                    mTouchMode = TOUCH_DRAG_LAYER_MODE;
-                    deltaX = contentX;
-                    deltaY = contentY;
-                    oldX = mScrollingLayerRect.left;
-                    oldY = mScrollingLayerRect.top;
-                    rangeX = maxX;
-                    rangeY = maxY;
-                } else {
-                    // Scroll the main page if we are not going to scroll the
-                    // layer.  This does not reset mScrollingLayer in case the
-                    // user changes directions and the layer can scroll the
-                    // other way.
-                    mTouchMode = TOUCH_DRAG_MODE;
-                }
-            }
-
-            if (mOverScrollGlow != null) {
-                mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
-            }
-
-            overScrollBy(deltaX, deltaY, oldX, oldY,
-                    rangeX, rangeY,
-                    mOverscrollDistance, mOverscrollDistance, true);
-            if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
-                invalidate();
-            }
-        }
-        mZoomManager.keepZoomPickerVisible();
-    }
-
-    private void stopTouch() {
-        if (mScroller.isFinished() && !mSelectingText
-                && (mTouchMode == TOUCH_DRAG_MODE || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
-            WebViewCore.resumePriority();
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-            nativeSetIsScrolling(false);
-        }
-
-        // we also use mVelocityTracker == null to tell us that we are
-        // not "moving around", so we can take the slower/prettier
-        // mode in the drawing code
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-
-        // Release any pulled glows
-        if (mOverScrollGlow != null) {
-            mOverScrollGlow.releaseAll();
-        }
-
-        if (mSelectingText) {
-            mSelectionStarted = false;
-            if (mIsCaretSelection) {
-                resetCaretTimer();
-            }
-            syncSelectionCursors();
-            invalidate();
-        }
-    }
-
-    private void cancelTouch() {
-        // we also use mVelocityTracker == null to tell us that we are
-        // not "moving around", so we can take the slower/prettier
-        // mode in the drawing code
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-
-        if ((mTouchMode == TOUCH_DRAG_MODE
-                || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
-            WebViewCore.resumePriority();
-            WebViewCore.resumeUpdatePicture(mWebViewCore);
-            nativeSetIsScrolling(false);
-        }
-        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
-        if (sDisableNavcache) {
-            removeTouchHighlight();
-        }
-        mHeldMotionless = MOTIONLESS_TRUE;
-        mTouchMode = TOUCH_DONE_MODE;
-        nativeHideCursor();
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    final float vscroll;
-                    final float hscroll;
-                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
-                        vscroll = 0;
-                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                    } else {
-                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
-                    }
-                    if (hscroll != 0 || vscroll != 0) {
-                        final int vdelta = (int) (vscroll * getVerticalScrollFactor());
-                        final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
-                        if (pinScrollBy(hdelta, vdelta, false, 0)) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return super.onGenericMotionEvent(event);
-    }
-
-    private long mTrackballFirstTime = 0;
-    private long mTrackballLastTime = 0;
-    private float mTrackballRemainsX = 0.0f;
-    private float mTrackballRemainsY = 0.0f;
-    private int mTrackballXMove = 0;
-    private int mTrackballYMove = 0;
-    private boolean mSelectingText = false;
-    private boolean mSelectionStarted = false;
-    private static final int TRACKBALL_KEY_TIMEOUT = 1000;
-    private static final int TRACKBALL_TIMEOUT = 200;
-    private static final int TRACKBALL_WAIT = 100;
-    private static final int TRACKBALL_SCALE = 400;
-    private static final int TRACKBALL_SCROLL_COUNT = 5;
-    private static final int TRACKBALL_MOVE_COUNT = 10;
-    private static final int TRACKBALL_MULTIPLIER = 3;
-    private static final int SELECT_CURSOR_OFFSET = 16;
-    private static final int SELECT_SCROLL = 5;
-    private int mSelectX = 0;
-    private int mSelectY = 0;
-    private boolean mFocusSizeChanged = false;
-    private boolean mTrackballDown = false;
-    private long mTrackballUpTime = 0;
-    private long mLastCursorTime = 0;
-    private Rect mLastCursorBounds;
-
-    // Set by default; BrowserActivity clears to interpret trackball data
-    // directly for movement. Currently, the framework only passes
-    // arrow key events, not trackball events, from one child to the next
-    private boolean mMapTrackballToArrowKeys = true;
-
-    private DrawData mDelaySetPicture;
-    private DrawData mLoadedPicture;
-
     public void setMapTrackballToArrowKeys(boolean setMap) {
         checkThread();
-        mMapTrackballToArrowKeys = setMap;
+        mProvider.setMapTrackballToArrowKeys(setMap);
     }
 
-    void resetTrackballTime() {
-        mTrackballLastTime = 0;
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        long time = ev.getEventTime();
-        if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
-            if (ev.getY() > 0) pageDown(true);
-            if (ev.getY() < 0) pageUp(true);
-            return true;
-        }
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (mSelectingText) {
-                return true; // discard press if copy in progress
-            }
-            mTrackballDown = true;
-            if (mNativeClass == 0) {
-                return false;
-            }
-            if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
-                    && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
-                nativeSelectBestAt(mLastCursorBounds);
-            }
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
-                        + " time=" + time
-                        + " mLastCursorTime=" + mLastCursorTime);
-            }
-            if (isInTouchMode()) requestFocusFromTouch();
-            return false; // let common code in onKeyDown at it
-        }
-        if (ev.getAction() == MotionEvent.ACTION_UP) {
-            // LONG_PRESS_CENTER is set in common onKeyDown
-            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
-            mTrackballDown = false;
-            mTrackballUpTime = time;
-            if (mSelectingText) {
-                copySelection();
-                selectionDone();
-                return true; // discard press if copy in progress
-            }
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
-                        + " time=" + time
-                );
-            }
-            return false; // let common code in onKeyUp at it
-        }
-        if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
-                AccessibilityManager.getInstance(mContext).isEnabled()) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
-            return false;
-        }
-        if (mTrackballDown) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
-            return true; // discard move if trackball is down
-        }
-        if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
-            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
-            return true;
-        }
-        // TODO: alternatively we can do panning as touch does
-        switchOutDrawHistory();
-        if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "onTrackballEvent time="
-                        + time + " last=" + mTrackballLastTime);
-            }
-            mTrackballFirstTime = time;
-            mTrackballXMove = mTrackballYMove = 0;
-        }
-        mTrackballLastTime = time;
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
-        }
-        mTrackballRemainsX += ev.getX();
-        mTrackballRemainsY += ev.getY();
-        doTrackball(time, ev.getMetaState());
-        return true;
-    }
-
-    private int scaleTrackballX(float xRate, int width) {
-        int xMove = (int) (xRate / TRACKBALL_SCALE * width);
-        int nextXMove = xMove;
-        if (xMove > 0) {
-            if (xMove > mTrackballXMove) {
-                xMove -= mTrackballXMove;
-            }
-        } else if (xMove < mTrackballXMove) {
-            xMove -= mTrackballXMove;
-        }
-        mTrackballXMove = nextXMove;
-        return xMove;
-    }
-
-    private int scaleTrackballY(float yRate, int height) {
-        int yMove = (int) (yRate / TRACKBALL_SCALE * height);
-        int nextYMove = yMove;
-        if (yMove > 0) {
-            if (yMove > mTrackballYMove) {
-                yMove -= mTrackballYMove;
-            }
-        } else if (yMove < mTrackballYMove) {
-            yMove -= mTrackballYMove;
-        }
-        mTrackballYMove = nextYMove;
-        return yMove;
-    }
-
-    private int keyCodeToSoundsEffect(int keyCode) {
-        switch(keyCode) {
-            case KeyEvent.KEYCODE_DPAD_UP:
-                return SoundEffectConstants.NAVIGATION_UP;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                return SoundEffectConstants.NAVIGATION_RIGHT;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                return SoundEffectConstants.NAVIGATION_DOWN;
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                return SoundEffectConstants.NAVIGATION_LEFT;
-        }
-        throw new IllegalArgumentException("keyCode must be one of " +
-                "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
-                "KEYCODE_DPAD_LEFT}.");
-    }
-
-    private void doTrackball(long time, int metaState) {
-        int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
-        if (elapsed == 0) {
-            elapsed = TRACKBALL_TIMEOUT;
-        }
-        float xRate = mTrackballRemainsX * 1000 / elapsed;
-        float yRate = mTrackballRemainsY * 1000 / elapsed;
-        int viewWidth = getViewWidth();
-        int viewHeight = getViewHeight();
-        float ax = Math.abs(xRate);
-        float ay = Math.abs(yRate);
-        float maxA = Math.max(ax, ay);
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
-                    + " xRate=" + xRate
-                    + " yRate=" + yRate
-                    + " mTrackballRemainsX=" + mTrackballRemainsX
-                    + " mTrackballRemainsY=" + mTrackballRemainsY);
-        }
-        int width = mContentWidth - viewWidth;
-        int height = mContentHeight - viewHeight;
-        if (width < 0) width = 0;
-        if (height < 0) height = 0;
-        ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
-        ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
-        maxA = Math.max(ax, ay);
-        int count = Math.max(0, (int) maxA);
-        int oldScrollX = mScrollX;
-        int oldScrollY = mScrollY;
-        if (count > 0) {
-            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
-                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
-                    mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
-                    KeyEvent.KEYCODE_DPAD_RIGHT;
-            count = Math.min(count, TRACKBALL_MOVE_COUNT);
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
-                        + " count=" + count
-                        + " mTrackballRemainsX=" + mTrackballRemainsX
-                        + " mTrackballRemainsY=" + mTrackballRemainsY);
-            }
-            if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
-                for (int i = 0; i < count; i++) {
-                    letPageHandleNavKey(selectKeyCode, time, true, metaState);
-                }
-                letPageHandleNavKey(selectKeyCode, time, false, metaState);
-            } else if (navHandledKey(selectKeyCode, count, false, time)) {
-                playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
-            }
-            mTrackballRemainsX = mTrackballRemainsY = 0;
-        }
-        if (count >= TRACKBALL_SCROLL_COUNT) {
-            int xMove = scaleTrackballX(xRate, width);
-            int yMove = scaleTrackballY(yRate, height);
-            if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doTrackball pinScrollBy"
-                        + " count=" + count
-                        + " xMove=" + xMove + " yMove=" + yMove
-                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
-                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
-                        );
-            }
-            if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
-                xMove = 0;
-            }
-            if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
-                yMove = 0;
-            }
-            if (xMove != 0 || yMove != 0) {
-                pinScrollBy(xMove, yMove, true, 0);
-            }
-        }
-    }
-
-    /**
-     * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
-     * @return Maximum horizontal scroll position within real content
-     */
-    int computeMaxScrollX() {
-        return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
-    }
-
-    /**
-     * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
-     * @return Maximum vertical scroll position within real content
-     */
-    int computeMaxScrollY() {
-        return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
-                - getViewHeightWithTitle(), 0);
-    }
-
-    boolean updateScrollCoordinates(int x, int y) {
-        int oldX = mScrollX;
-        int oldY = mScrollY;
-        mScrollX = x;
-        mScrollY = y;
-        if (oldX != mScrollX || oldY != mScrollY) {
-            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-            return true;
-        } else {
-            return false;
-        }
-    }
 
     public void flingScroll(int vx, int vy) {
         checkThread();
-        mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
-                computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
-        invalidate();
-    }
-
-    private void doFling() {
-        if (mVelocityTracker == null) {
-            return;
-        }
-        int maxX = computeMaxScrollX();
-        int maxY = computeMaxScrollY();
-
-        mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
-        int vx = (int) mVelocityTracker.getXVelocity();
-        int vy = (int) mVelocityTracker.getYVelocity();
-
-        int scrollX = mScrollX;
-        int scrollY = mScrollY;
-        int overscrollDistance = mOverscrollDistance;
-        int overflingDistance = mOverflingDistance;
-
-        // Use the layer's scroll data if applicable.
-        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-            scrollX = mScrollingLayerRect.left;
-            scrollY = mScrollingLayerRect.top;
-            maxX = mScrollingLayerRect.right;
-            maxY = mScrollingLayerRect.bottom;
-            // No overscrolling for layers.
-            overscrollDistance = overflingDistance = 0;
-        }
-
-        if (mSnapScrollMode != SNAP_NONE) {
-            if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
-                vy = 0;
-            } else {
-                vx = 0;
-            }
-        }
-        if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
-            WebViewCore.resumePriority();
-            if (!mSelectingText) {
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
-            }
-            if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
-                invalidate();
-            }
-            return;
-        }
-        float currentVelocity = mScroller.getCurrVelocity();
-        float velocity = (float) Math.hypot(vx, vy);
-        if (mLastVelocity > 0 && currentVelocity > 0 && velocity
-                > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
-            float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
-                    - Math.atan2(vy, vx)));
-            final float circle = (float) (Math.PI) * 2.0f;
-            if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
-                vx += currentVelocity * mLastVelX / mLastVelocity;
-                vy += currentVelocity * mLastVelY / mLastVelocity;
-                velocity = (float) Math.hypot(vx, vy);
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
-                }
-            } else if (DebugFlags.WEB_VIEW) {
-                Log.v(LOGTAG, "doFling missed " + deltaR / circle);
-            }
-        } else if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "doFling start last=" + mLastVelocity
-                    + " current=" + currentVelocity
-                    + " vx=" + vx + " vy=" + vy
-                    + " maxX=" + maxX + " maxY=" + maxY
-                    + " scrollX=" + scrollX + " scrollY=" + scrollY
-                    + " layer=" + mCurrentScrollingLayerId);
-        }
-
-        // Allow sloppy flings without overscrolling at the edges.
-        if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
-            vx = 0;
-        }
-        if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
-            vy = 0;
-        }
-
-        if (overscrollDistance < overflingDistance) {
-            if ((vx > 0 && scrollX == -overscrollDistance) ||
-                    (vx < 0 && scrollX == maxX + overscrollDistance)) {
-                vx = 0;
-            }
-            if ((vy > 0 && scrollY == -overscrollDistance) ||
-                    (vy < 0 && scrollY == maxY + overscrollDistance)) {
-                vy = 0;
-            }
-        }
-
-        mLastVelX = vx;
-        mLastVelY = vy;
-        mLastVelocity = velocity;
-
-        // no horizontal overscroll if the content just fits
-        mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
-                maxX == 0 ? 0 : overflingDistance, overflingDistance);
-        // Duration is calculated based on velocity. With range boundaries and overscroll
-        // we may not know how long the final animation will take. (Hence the deprecation
-        // warning on the call below.) It's not a big deal for scroll bars but if webcore
-        // resumes during this effect we will take a performance hit. See computeScroll;
-        // we resume webcore there when the animation is finished.
-        final int time = mScroller.getDuration();
-
-        // Suppress scrollbars for layer scrolling.
-        if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-            awakenScrollBars(time);
-        }
-
-        invalidate();
+        mProvider.flingScroll(vx, vy);
     }
 
     /**
@@ -7739,27 +1491,7 @@
     @Deprecated
     public View getZoomControls() {
         checkThread();
-        if (!getSettings().supportZoom()) {
-            Log.w(LOGTAG, "This WebView doesn't support zoom.");
-            return null;
-        }
-        return mZoomManager.getExternalZoomPicker();
-    }
-
-    void dismissZoomControl() {
-        mZoomManager.dismissZoomPicker();
-    }
-
-    float getDefaultZoomScale() {
-        return mZoomManager.getDefaultScale();
-    }
-
-    /**
-     * Return the overview scale of the WebView
-     * @return The overview scale.
-     */
-    float getZoomOverviewScale() {
-        return mZoomManager.getZoomOverviewScale();
+        return mProvider.getZoomControls();
     }
 
     /**
@@ -7767,7 +1499,7 @@
      */
     public boolean canZoomIn() {
         checkThread();
-        return mZoomManager.canZoomIn();
+        return mProvider.canZoomIn();
     }
 
     /**
@@ -7775,7 +1507,7 @@
      */
     public boolean canZoomOut() {
         checkThread();
-        return mZoomManager.canZoomOut();
+        return mProvider.canZoomOut();
     }
 
     /**
@@ -7784,7 +1516,7 @@
      */
     public boolean zoomIn() {
         checkThread();
-        return mZoomManager.zoomIn();
+        return mProvider.zoomIn();
     }
 
     /**
@@ -7793,2262 +1525,7 @@
      */
     public boolean zoomOut() {
         checkThread();
-        return mZoomManager.zoomOut();
-    }
-
-    /**
-     * This selects the best clickable target at mLastTouchX and mLastTouchY
-     * and calls showCursorTimed on the native side
-     */
-    private void updateSelection() {
-        if (mNativeClass == 0 || sDisableNavcache) {
-            return;
-        }
-        mPrivateHandler.removeMessages(UPDATE_SELECTION);
-        // mLastTouchX and mLastTouchY are the point in the current viewport
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        int slop = viewToContentDimension(mNavSlop);
-        Rect rect = new Rect(contentX - slop, contentY - slop,
-                contentX + slop, contentY + slop);
-        nativeSelectBestAt(rect);
-        mInitialHitTestResult = hitTestResult(null);
-    }
-
-    /**
-     * Scroll the focused text field to match the WebTextView
-     * @param xPercent New x position of the WebTextView from 0 to 1.
-     */
-    /*package*/ void scrollFocusedTextInputX(float xPercent) {
-        if (!inEditingMode() || mWebViewCore == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
-                new Float(xPercent));
-    }
-
-    /**
-     * Scroll the focused textarea vertically to match the WebTextView
-     * @param y New y position of the WebTextView in view coordinates
-     */
-    /* package */ void scrollFocusedTextInputY(int y) {
-        if (!inEditingMode() || mWebViewCore == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, viewToContentDimension(y));
-    }
-
-    /**
-     * Set our starting point and time for a drag from the WebTextView.
-     */
-    /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
-        if (!inEditingMode()) {
-            return;
-        }
-        mLastTouchX = Math.round(x + mWebTextView.getLeft() - mScrollX);
-        mLastTouchY = Math.round(y + mWebTextView.getTop() - mScrollY);
-        mLastTouchTime = eventTime;
-        if (!mScroller.isFinished()) {
-            abortAnimation();
-        }
-        mSnapScrollMode = SNAP_NONE;
-        mVelocityTracker = VelocityTracker.obtain();
-        mTouchMode = TOUCH_DRAG_START_MODE;
-    }
-
-    /**
-     * Given a motion event from the WebTextView, set its location to our
-     * coordinates, and handle the event.
-     */
-    /*package*/ boolean textFieldDrag(MotionEvent event) {
-        if (!inEditingMode()) {
-            return false;
-        }
-        mDragFromTextInput = true;
-        event.offsetLocation((mWebTextView.getLeft() - mScrollX),
-                (mWebTextView.getTop() - mScrollY));
-        boolean result = onTouchEvent(event);
-        mDragFromTextInput = false;
-        return result;
-    }
-
-    /**
-     * Due a touch up from a WebTextView.  This will be handled by webkit to
-     * change the selection.
-     * @param event MotionEvent in the WebTextView's coordinates.
-     */
-    /*package*/ void touchUpOnTextField(MotionEvent event) {
-        if (!inEditingMode()) {
-            return;
-        }
-        int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
-        int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
-        int slop = viewToContentDimension(mNavSlop);
-        nativeMotionUp(x, y, slop);
-    }
-
-    /**
-     * Called when pressing the center key or trackball on a textfield.
-     */
-    /*package*/ void centerKeyPressOnTextField() {
-        mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
-                    nativeCursorNodePointer());
-    }
-
-    private void doShortPress() {
-        if (mNativeClass == 0) {
-            return;
-        }
-        if (mPreventDefault == PREVENT_DEFAULT_YES) {
-            return;
-        }
-        mTouchMode = TOUCH_DONE_MODE;
-        updateSelection();
-        switchOutDrawHistory();
-        // mLastTouchX and mLastTouchY are the point in the current viewport
-        int contentX = viewToContentX(mLastTouchX + mScrollX);
-        int contentY = viewToContentY(mLastTouchY + mScrollY);
-        int slop = viewToContentDimension(mNavSlop);
-        if (sDisableNavcache && !mTouchHighlightRegion.isEmpty()) {
-            // set mTouchHighlightRequested to 0 to cause an immediate
-            // drawing of the touch rings
-            mTouchHighlightRequested = 0;
-            invalidate(mTouchHighlightRegion.getBounds());
-            mPrivateHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    removeTouchHighlight();
-                }
-            }, ViewConfiguration.getPressedStateDuration());
-        }
-        if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-            overrideLoading(mFocusedNode.mIntentUrl);
-        } else if (sDisableNavcache) {
-            WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
-            // use "0" as generation id to inform WebKit to use the same x/y as
-            // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
-            touchUpData.mMoveGeneration = 0;
-            mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
-        } else if (nativePointInNavCache(contentX, contentY, slop)) {
-            WebViewCore.MotionUpData motionUpData = new WebViewCore
-                    .MotionUpData();
-            motionUpData.mFrame = nativeCacheHitFramePointer();
-            motionUpData.mNode = nativeCacheHitNodePointer();
-            motionUpData.mBounds = nativeCacheHitNodeBounds();
-            motionUpData.mX = contentX;
-            motionUpData.mY = contentY;
-            mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
-                    motionUpData);
-        } else {
-            doMotionUp(contentX, contentY);
-        }
-    }
-
-    private void doMotionUp(int contentX, int contentY) {
-        int slop = viewToContentDimension(mNavSlop);
-        if (nativeMotionUp(contentX, contentY, slop) && mLogEvent) {
-            EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
-        }
-        if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-        }
-    }
-
-    void sendPluginDrawMsg() {
-        mWebViewCore.sendMessage(EventHub.PLUGIN_SURFACE_READY);
-    }
-
-    /**
-     * Returns plugin bounds if x/y in content coordinates corresponds to a
-     * plugin. Otherwise a NULL rectangle is returned.
-     */
-    Rect getPluginBounds(int x, int y) {
-        int slop = viewToContentDimension(mNavSlop);
-        if (nativePointInNavCache(x, y, slop) && nativeCacheHitIsPlugin()) {
-            return nativeCacheHitNodeBounds();
-        } else {
-            return null;
-        }
-    }
-
-    /*
-     * Return true if the rect (e.g. plugin) is fully visible and maximized
-     * inside the WebView.
-     */
-    boolean isRectFitOnScreen(Rect rect) {
-        final int rectWidth = rect.width();
-        final int rectHeight = rect.height();
-        final int viewWidth = getViewWidth();
-        final int viewHeight = getViewHeightWithTitle();
-        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
-        scale = mZoomManager.computeScaleWithLimits(scale);
-        return !mZoomManager.willScaleTriggerZoom(scale)
-                && contentToViewX(rect.left) >= mScrollX
-                && contentToViewX(rect.right) <= mScrollX + viewWidth
-                && contentToViewY(rect.top) >= mScrollY
-                && contentToViewY(rect.bottom) <= mScrollY + viewHeight;
-    }
-
-    /*
-     * Maximize and center the rectangle, specified in the document coordinate
-     * space, 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. The rect is in document
-     * coordinates
-     */
-    void centerFitRect(Rect rect) {
-        final int rectWidth = rect.width();
-        final int rectHeight = rect.height();
-        final int viewWidth = getViewWidth();
-        final int viewHeight = getViewHeightWithTitle();
-        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
-                / rectHeight);
-        scale = mZoomManager.computeScaleWithLimits(scale);
-        if (!mZoomManager.willScaleTriggerZoom(scale)) {
-            pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
-                    contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
-                    true, 0);
-        } else {
-            float actualScale = mZoomManager.getScale();
-            float oldScreenX = rect.left * actualScale - mScrollX;
-            float rectViewX = rect.left * scale;
-            float rectViewWidth = rectWidth * scale;
-            float newMaxWidth = mContentWidth * scale;
-            float newScreenX = (viewWidth - rectViewWidth) / 2;
-            // pin the newX to the WebView
-            if (newScreenX > rectViewX) {
-                newScreenX = rectViewX;
-            } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
-                newScreenX = viewWidth - (newMaxWidth - rectViewX);
-            }
-            float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
-                    / (scale - actualScale);
-            float oldScreenY = rect.top * actualScale + getTitleHeight()
-                    - mScrollY;
-            float rectViewY = rect.top * scale + getTitleHeight();
-            float rectViewHeight = rectHeight * scale;
-            float newMaxHeight = mContentHeight * scale + getTitleHeight();
-            float newScreenY = (viewHeight - rectViewHeight) / 2;
-            // pin the newY to the WebView
-            if (newScreenY > rectViewY) {
-                newScreenY = rectViewY;
-            } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
-                newScreenY = viewHeight - (newMaxHeight - rectViewY);
-            }
-            float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
-                    / (scale - actualScale);
-            mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
-            mZoomManager.startZoomAnimation(scale, false);
-        }
-    }
-
-    // Called by JNI to handle a touch on a node representing an email address,
-    // address, or phone number
-    private void overrideLoading(String url) {
-        mCallbackProxy.uiOverrideUrlLoading(url);
-    }
-
-    @Override
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        // FIXME: If a subwindow is showing find, and the user touches the
-        // background window, it can steal focus.
-        if (mFindIsUp) return false;
-        boolean result = false;
-        if (inEditingMode()) {
-            result = mWebTextView.requestFocus(direction,
-                    previouslyFocusedRect);
-        } else {
-            result = super.requestFocus(direction, previouslyFocusedRect);
-            if (mWebViewCore.getSettings().getNeedInitialFocus() && !isInTouchMode()) {
-                // For cases such as GMail, where we gain focus from a direction,
-                // we want to move to the first available link.
-                // FIXME: If there are no visible links, we may not want to
-                int fakeKeyDirection = 0;
-                switch(direction) {
-                    case View.FOCUS_UP:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
-                        break;
-                    case View.FOCUS_DOWN:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
-                        break;
-                    case View.FOCUS_LEFT:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
-                        break;
-                    case View.FOCUS_RIGHT:
-                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
-                        break;
-                    default:
-                        return result;
-                }
-                if (mNativeClass != 0 && !nativeHasCursorNode()) {
-                    navHandledKey(fakeKeyDirection, 1, true, 0);
-                }
-            }
-        }
-        return result;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-
-        int measuredHeight = heightSize;
-        int measuredWidth = widthSize;
-
-        // Grab the content size from WebViewCore.
-        int contentHeight = contentToViewDimension(mContentHeight);
-        int contentWidth = contentToViewDimension(mContentWidth);
-
-//        Log.d(LOGTAG, "------- measure " + heightMode);
-
-        if (heightMode != MeasureSpec.EXACTLY) {
-            mHeightCanMeasure = true;
-            measuredHeight = contentHeight;
-            if (heightMode == MeasureSpec.AT_MOST) {
-                // If we are larger than the AT_MOST height, then our height can
-                // no longer be measured and we should scroll internally.
-                if (measuredHeight > heightSize) {
-                    measuredHeight = heightSize;
-                    mHeightCanMeasure = false;
-                    measuredHeight |= MEASURED_STATE_TOO_SMALL;
-                }
-            }
-        } else {
-            mHeightCanMeasure = false;
-        }
-        if (mNativeClass != 0) {
-            nativeSetHeightCanMeasure(mHeightCanMeasure);
-        }
-        // For the width, always use the given size unless unspecified.
-        if (widthMode == MeasureSpec.UNSPECIFIED) {
-            mWidthCanMeasure = true;
-            measuredWidth = contentWidth;
-        } else {
-            if (measuredWidth < contentWidth) {
-                measuredWidth |= MEASURED_STATE_TOO_SMALL;
-            }
-            mWidthCanMeasure = false;
-        }
-
-        synchronized (this) {
-            setMeasuredDimension(measuredWidth, measuredHeight);
-        }
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child,
-                                                 Rect rect,
-                                                 boolean immediate) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        // don't scroll while in zoom animation. When it is done, we will adjust
-        // the necessary components (e.g., WebTextView if it is in editing mode)
-        if (mZoomManager.isFixedLengthAnimationInProgress()) {
-            return false;
-        }
-
-        rect.offset(child.getLeft() - child.getScrollX(),
-                child.getTop() - child.getScrollY());
-
-        Rect content = new Rect(viewToContentX(mScrollX),
-                viewToContentY(mScrollY),
-                viewToContentX(mScrollX + getWidth()
-                - getVerticalScrollbarWidth()),
-                viewToContentY(mScrollY + getViewHeightWithTitle()));
-        content = nativeSubtractLayers(content);
-        int screenTop = contentToViewY(content.top);
-        int screenBottom = contentToViewY(content.bottom);
-        int height = screenBottom - screenTop;
-        int scrollYDelta = 0;
-
-        if (rect.bottom > screenBottom) {
-            int oneThirdOfScreenHeight = height / 3;
-            if (rect.height() > 2 * oneThirdOfScreenHeight) {
-                // If the rectangle is too tall to fit in the bottom two thirds
-                // of the screen, place it at the top.
-                scrollYDelta = rect.top - screenTop;
-            } else {
-                // If the rectangle will still fit on screen, we want its
-                // top to be in the top third of the screen.
-                scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
-            }
-        } else if (rect.top < screenTop) {
-            scrollYDelta = rect.top - screenTop;
-        }
-
-        int screenLeft = contentToViewX(content.left);
-        int screenRight = contentToViewX(content.right);
-        int width = screenRight - screenLeft;
-        int scrollXDelta = 0;
-
-        if (rect.right > screenRight && rect.left > screenLeft) {
-            if (rect.width() > width) {
-                scrollXDelta += (rect.left - screenLeft);
-            } else {
-                scrollXDelta += (rect.right - screenRight);
-            }
-        } else if (rect.left < screenLeft) {
-            scrollXDelta -= (screenLeft - rect.left);
-        }
-
-        if ((scrollYDelta | scrollXDelta) != 0) {
-            return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
-        }
-
-        return false;
-    }
-
-    /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
-            String replace, int newStart, int newEnd) {
-        WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
-        arg.mReplace = replace;
-        arg.mNewStart = newStart;
-        arg.mNewEnd = newEnd;
-        mTextGeneration++;
-        arg.mTextGeneration = mTextGeneration;
-        mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
-    }
-
-    /* package */ void passToJavaScript(String currentText, KeyEvent event) {
-        // check if mWebViewCore has been destroyed
-        if (mWebViewCore == null) {
-            return;
-        }
-        WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
-        arg.mEvent = event;
-        arg.mCurrentText = currentText;
-        // Increase our text generation number, and pass it to webcore thread
-        mTextGeneration++;
-        mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
-        // WebKit's document state is not saved until about to leave the page.
-        // To make sure the host application, like Browser, has the up to date
-        // document state when it goes to background, we force to save the
-        // document state.
-        mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
-        mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
-                cursorData(), 1000);
-    }
-
-    /**
-     * @hide
-     */
-    public synchronized WebViewCore getWebViewCore() {
-        return mWebViewCore;
-    }
-
-    /**
-     * Used only by TouchEventQueue to store pending touch events.
-     */
-    private static class QueuedTouch {
-        long mSequence;
-        MotionEvent mEvent; // Optional
-        TouchEventData mTed; // Optional
-
-        QueuedTouch mNext;
-
-        public QueuedTouch set(TouchEventData ted) {
-            mSequence = ted.mSequence;
-            mTed = ted;
-            mEvent = null;
-            mNext = null;
-            return this;
-        }
-
-        public QueuedTouch set(MotionEvent ev, long sequence) {
-            mEvent = MotionEvent.obtain(ev);
-            mSequence = sequence;
-            mTed = null;
-            mNext = null;
-            return this;
-        }
-
-        public QueuedTouch add(QueuedTouch other) {
-            if (other.mSequence < mSequence) {
-                other.mNext = this;
-                return other;
-            }
-
-            QueuedTouch insertAt = this;
-            while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) {
-                insertAt = insertAt.mNext;
-            }
-            other.mNext = insertAt.mNext;
-            insertAt.mNext = other;
-            return this;
-        }
-    }
-
-    /**
-     * WebView handles touch events asynchronously since some events must be passed to WebKit
-     * for potentially slower processing. TouchEventQueue serializes touch events regardless
-     * of which path they take to ensure that no events are ever processed out of order
-     * by WebView.
-     */
-    private class TouchEventQueue {
-        private long mNextTouchSequence = Long.MIN_VALUE + 1;
-        private long mLastHandledTouchSequence = Long.MIN_VALUE;
-        private long mIgnoreUntilSequence = Long.MIN_VALUE + 1;
-
-        // Events waiting to be processed.
-        private QueuedTouch mTouchEventQueue;
-
-        // Known events that are waiting on a response before being enqueued.
-        private QueuedTouch mPreQueue;
-
-        // Pool of QueuedTouch objects saved for later use.
-        private QueuedTouch mQueuedTouchRecycleBin;
-        private int mQueuedTouchRecycleCount;
-
-        private long mLastEventTime = Long.MAX_VALUE;
-        private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;
-
-        // milliseconds until we abandon hope of getting all of a previous gesture
-        private static final int QUEUED_GESTURE_TIMEOUT = 1000;
-
-        private QueuedTouch obtainQueuedTouch() {
-            if (mQueuedTouchRecycleBin != null) {
-                QueuedTouch result = mQueuedTouchRecycleBin;
-                mQueuedTouchRecycleBin = result.mNext;
-                mQueuedTouchRecycleCount--;
-                return result;
-            }
-            return new QueuedTouch();
-        }
-
-        /**
-         * Allow events with any currently missing sequence numbers to be skipped in processing.
-         */
-        public void ignoreCurrentlyMissingEvents() {
-            mIgnoreUntilSequence = mNextTouchSequence;
-
-            // Run any events we have available and complete, pre-queued or otherwise.
-            runQueuedAndPreQueuedEvents();
-        }
-
-        private void runQueuedAndPreQueuedEvents() {
-            QueuedTouch qd = mPreQueue;
-            boolean fromPreQueue = true;
-            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
-                handleQueuedTouch(qd);
-                QueuedTouch recycleMe = qd;
-                if (fromPreQueue) {
-                    mPreQueue = qd.mNext;
-                } else {
-                    mTouchEventQueue = qd.mNext;
-                }
-                recycleQueuedTouch(recycleMe);
-                mLastHandledTouchSequence++;
-
-                long nextPre = mPreQueue != null ? mPreQueue.mSequence : Long.MAX_VALUE;
-                long nextQueued = mTouchEventQueue != null ?
-                        mTouchEventQueue.mSequence : Long.MAX_VALUE;
-                fromPreQueue = nextPre < nextQueued;
-                qd = fromPreQueue ? mPreQueue : mTouchEventQueue;
-            }
-        }
-
-        /**
-         * Add a TouchEventData to the pre-queue.
-         *
-         * An event in the pre-queue is an event that we know about that
-         * has been sent to webkit, but that we haven't received back and
-         * enqueued into the normal touch queue yet. If webkit ever times
-         * out and we need to ignore currently missing events, we'll run
-         * events from the pre-queue to patch the holes.
-         *
-         * @param ted TouchEventData to pre-queue
-         */
-        public void preQueueTouchEventData(TouchEventData ted) {
-            QueuedTouch newTouch = obtainQueuedTouch().set(ted);
-            if (mPreQueue == null) {
-                mPreQueue = newTouch;
-            } else {
-                QueuedTouch insertionPoint = mPreQueue;
-                while (insertionPoint.mNext != null &&
-                        insertionPoint.mNext.mSequence < newTouch.mSequence) {
-                    insertionPoint = insertionPoint.mNext;
-                }
-                newTouch.mNext = insertionPoint.mNext;
-                insertionPoint.mNext = newTouch;
-            }
-        }
-
-        private void recycleQueuedTouch(QueuedTouch qd) {
-            if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
-                qd.mNext = mQueuedTouchRecycleBin;
-                mQueuedTouchRecycleBin = qd;
-                mQueuedTouchRecycleCount++;
-            }
-        }
-
-        /**
-         * Reset the touch event queue. This will dump any pending events
-         * and reset the sequence numbering.
-         */
-        public void reset() {
-            mNextTouchSequence = Long.MIN_VALUE + 1;
-            mLastHandledTouchSequence = Long.MIN_VALUE;
-            mIgnoreUntilSequence = Long.MIN_VALUE + 1;
-            while (mTouchEventQueue != null) {
-                QueuedTouch recycleMe = mTouchEventQueue;
-                mTouchEventQueue = mTouchEventQueue.mNext;
-                recycleQueuedTouch(recycleMe);
-            }
-            while (mPreQueue != null) {
-                QueuedTouch recycleMe = mPreQueue;
-                mPreQueue = mPreQueue.mNext;
-                recycleQueuedTouch(recycleMe);
-            }
-        }
-
-        /**
-         * Return the next valid sequence number for tagging incoming touch events.
-         * @return The next touch event sequence number
-         */
-        public long nextTouchSequence() {
-            return mNextTouchSequence++;
-        }
-
-        /**
-         * Enqueue a touch event in the form of TouchEventData.
-         * The sequence number will be read from the mSequence field of the argument.
-         *
-         * If the touch event's sequence number is the next in line to be processed, it will
-         * be handled before this method returns. Any subsequent events that have already
-         * been queued will also be processed in their proper order.
-         *
-         * @param ted Touch data to be processed in order.
-         * @return true if the event was processed before returning, false if it was just enqueued.
-         */
-        public boolean enqueueTouchEvent(TouchEventData ted) {
-            // Remove from the pre-queue if present
-            QueuedTouch preQueue = mPreQueue;
-            if (preQueue != null) {
-                // On exiting this block, preQueue is set to the pre-queued QueuedTouch object
-                // if it was present in the pre-queue, and removed from the pre-queue itself.
-                if (preQueue.mSequence == ted.mSequence) {
-                    mPreQueue = preQueue.mNext;
-                } else {
-                    QueuedTouch prev = preQueue;
-                    preQueue = null;
-                    while (prev.mNext != null) {
-                        if (prev.mNext.mSequence == ted.mSequence) {
-                            preQueue = prev.mNext;
-                            prev.mNext = preQueue.mNext;
-                            break;
-                        } else {
-                            prev = prev.mNext;
-                        }
-                    }
-                }
-            }
-
-            if (ted.mSequence < mLastHandledTouchSequence) {
-                // Stale event and we already moved on; drop it. (Should not be common.)
-                Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) +
-                        " received from webcore; ignoring");
-                return false;
-            }
-
-            if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) {
-                return false;
-            }
-
-            // dropStaleGestures above might have fast-forwarded us to
-            // an event we have already.
-            runNextQueuedEvents();
-
-            if (mLastHandledTouchSequence + 1 == ted.mSequence) {
-                if (preQueue != null) {
-                    recycleQueuedTouch(preQueue);
-                    preQueue = null;
-                }
-                handleQueuedTouchEventData(ted);
-
-                mLastHandledTouchSequence++;
-
-                // Do we have any more? Run them if so.
-                runNextQueuedEvents();
-            } else {
-                // Reuse the pre-queued object if we had it.
-                QueuedTouch qd = preQueue != null ? preQueue : obtainQueuedTouch().set(ted);
-                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
-            }
-            return true;
-        }
-
-        /**
-         * Enqueue a touch event in the form of a MotionEvent from the framework.
-         *
-         * If the touch event's sequence number is the next in line to be processed, it will
-         * be handled before this method returns. Any subsequent events that have already
-         * been queued will also be processed in their proper order.
-         *
-         * @param ev MotionEvent to be processed in order
-         */
-        public void enqueueTouchEvent(MotionEvent ev) {
-            final long sequence = nextTouchSequence();
-
-            if (dropStaleGestures(ev, sequence)) {
-                return;
-            }
-
-            // dropStaleGestures above might have fast-forwarded us to
-            // an event we have already.
-            runNextQueuedEvents();
-
-            if (mLastHandledTouchSequence + 1 == sequence) {
-                handleQueuedMotionEvent(ev);
-
-                mLastHandledTouchSequence++;
-
-                // Do we have any more? Run them if so.
-                runNextQueuedEvents();
-            } else {
-                QueuedTouch qd = obtainQueuedTouch().set(ev, sequence);
-                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
-            }
-        }
-
-        private void runNextQueuedEvents() {
-            QueuedTouch qd = mTouchEventQueue;
-            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
-                handleQueuedTouch(qd);
-                QueuedTouch recycleMe = qd;
-                qd = qd.mNext;
-                recycleQueuedTouch(recycleMe);
-                mLastHandledTouchSequence++;
-            }
-            mTouchEventQueue = qd;
-        }
-
-        private boolean dropStaleGestures(MotionEvent ev, long sequence) {
-            if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) {
-                // This is to make sure that we don't attempt to process a tap
-                // or long press when webkit takes too long to get back to us.
-                // The movement will be properly confirmed when we process the
-                // enqueued event later.
-                final int dx = Math.round(ev.getX()) - mLastTouchX;
-                final int dy = Math.round(ev.getY()) - mLastTouchY;
-                if (dx * dx + dy * dy > mTouchSlopSquare) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                }
-            }
-
-            if (mTouchEventQueue == null) {
-                return sequence <= mLastHandledTouchSequence;
-            }
-
-            // If we have a new down event and it's been a while since the last event
-            // we saw, catch up as best we can and keep going.
-            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) {
-                long eventTime = ev.getEventTime();
-                long lastHandledEventTime = mLastEventTime;
-                if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) {
-                    Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
-                            "Catching up.");
-                    runQueuedAndPreQueuedEvents();
-
-                    // Drop leftovers that we truly don't have.
-                    QueuedTouch qd = mTouchEventQueue;
-                    while (qd != null && qd.mSequence < sequence) {
-                        QueuedTouch recycleMe = qd;
-                        qd = qd.mNext;
-                        recycleQueuedTouch(recycleMe);
-                    }
-                    mTouchEventQueue = qd;
-                    mLastHandledTouchSequence = sequence - 1;
-                }
-            }
-
-            if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) {
-                QueuedTouch qd = mTouchEventQueue;
-                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
-                    QueuedTouch recycleMe = qd;
-                    qd = qd.mNext;
-                    recycleQueuedTouch(recycleMe);
-                }
-                mTouchEventQueue = qd;
-                mLastHandledTouchSequence = mIgnoreUntilSequence - 1;
-            }
-
-            if (mPreQueue != null) {
-                // Drop stale prequeued events
-                QueuedTouch qd = mPreQueue;
-                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
-                    QueuedTouch recycleMe = qd;
-                    qd = qd.mNext;
-                    recycleQueuedTouch(recycleMe);
-                }
-                mPreQueue = qd;
-            }
-
-            return sequence <= mLastHandledTouchSequence;
-        }
-
-        private void handleQueuedTouch(QueuedTouch qt) {
-            if (qt.mTed != null) {
-                handleQueuedTouchEventData(qt.mTed);
-            } else {
-                handleQueuedMotionEvent(qt.mEvent);
-                qt.mEvent.recycle();
-            }
-        }
-
-        private void handleQueuedMotionEvent(MotionEvent ev) {
-            mLastEventTime = ev.getEventTime();
-            int action = ev.getActionMasked();
-            if (ev.getPointerCount() > 1) {  // Multi-touch
-                handleMultiTouchInWebView(ev);
-            } else {
-                final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
-                if (detector != null && mPreventDefault != PREVENT_DEFAULT_YES) {
-                    // ScaleGestureDetector needs a consistent event stream to operate properly.
-                    // It won't take any action with fewer than two pointers, but it needs to
-                    // update internal bookkeeping state.
-                    detector.onTouchEvent(ev);
-                }
-
-                handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
-            }
-        }
-
-        private void handleQueuedTouchEventData(TouchEventData ted) {
-            if (ted.mMotionEvent != null) {
-                mLastEventTime = ted.mMotionEvent.getEventTime();
-            }
-            if (!ted.mReprocess) {
-                if (ted.mAction == MotionEvent.ACTION_DOWN
-                        && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
-                    // if prevent default is called from WebCore, UI
-                    // will not handle the rest of the touch events any
-                    // more.
-                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
-                            : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
-                } else if (ted.mAction == MotionEvent.ACTION_MOVE
-                        && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
-                    // the return for the first ACTION_MOVE will decide
-                    // whether UI will handle touch or not. Currently no
-                    // support for alternating prevent default
-                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
-                            : PREVENT_DEFAULT_NO;
-                }
-                if (mPreventDefault == PREVENT_DEFAULT_YES) {
-                    mTouchHighlightRegion.setEmpty();
-                }
-            } else {
-                if (ted.mPoints.length > 1) {  // multi-touch
-                    if (!ted.mNativeResult && mPreventDefault != PREVENT_DEFAULT_YES) {
-                        mPreventDefault = PREVENT_DEFAULT_NO;
-                        handleMultiTouchInWebView(ted.mMotionEvent);
-                    } else {
-                        mPreventDefault = PREVENT_DEFAULT_YES;
-                    }
-                    return;
-                }
-
-                // prevent default is not called in WebCore, so the
-                // message needs to be reprocessed in UI
-                if (!ted.mNativeResult) {
-                    // Following is for single touch.
-                    switch (ted.mAction) {
-                        case MotionEvent.ACTION_DOWN:
-                            mLastDeferTouchX = ted.mPointsInView[0].x;
-                            mLastDeferTouchY = ted.mPointsInView[0].y;
-                            mDeferTouchMode = TOUCH_INIT_MODE;
-                            break;
-                        case MotionEvent.ACTION_MOVE: {
-                            // no snapping in defer process
-                            int x = ted.mPointsInView[0].x;
-                            int y = ted.mPointsInView[0].y;
-
-                            if (mDeferTouchMode != TOUCH_DRAG_MODE) {
-                                mDeferTouchMode = TOUCH_DRAG_MODE;
-                                mLastDeferTouchX = x;
-                                mLastDeferTouchY = y;
-                                startScrollingLayer(x, y);
-                                startDrag();
-                            }
-                            int deltaX = pinLocX((int) (mScrollX
-                                    + mLastDeferTouchX - x))
-                                    - mScrollX;
-                            int deltaY = pinLocY((int) (mScrollY
-                                    + mLastDeferTouchY - y))
-                                    - mScrollY;
-                            doDrag(deltaX, deltaY);
-                            if (deltaX != 0) mLastDeferTouchX = x;
-                            if (deltaY != 0) mLastDeferTouchY = y;
-                            break;
-                        }
-                        case MotionEvent.ACTION_UP:
-                        case MotionEvent.ACTION_CANCEL:
-                            if (mDeferTouchMode == TOUCH_DRAG_MODE) {
-                                // no fling in defer process
-                                mScroller.springBack(mScrollX, mScrollY, 0,
-                                        computeMaxScrollX(), 0,
-                                        computeMaxScrollY());
-                                invalidate();
-                                WebViewCore.resumePriority();
-                                WebViewCore.resumeUpdatePicture(mWebViewCore);
-                            }
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                        case WebViewCore.ACTION_DOUBLETAP:
-                            // doDoubleTap() needs mLastTouchX/Y as anchor
-                            mLastDeferTouchX = ted.mPointsInView[0].x;
-                            mLastDeferTouchY = ted.mPointsInView[0].y;
-                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                        case WebViewCore.ACTION_LONGPRESS:
-                            HitTestResult hitTest = getHitTestResult();
-                            if (hitTest != null && hitTest.mType
-                                    != HitTestResult.UNKNOWN_TYPE) {
-                                performLongClick();
-                            }
-                            mDeferTouchMode = TOUCH_DONE_MODE;
-                            break;
-                    }
-                }
-            }
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods can be called from a separate thread, like WebViewCore
-    // If it needs to call the View system, it has to send message.
-    //-------------------------------------------------------------------------
-
-    /**
-     * General handler to receive message coming from webkit thread
-     */
-    class PrivateHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            // exclude INVAL_RECT_MSG_ID since it is frequently output
-            if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
-                if (msg.what >= FIRST_PRIVATE_MSG_ID
-                        && msg.what <= LAST_PRIVATE_MSG_ID) {
-                    Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
-                            - FIRST_PRIVATE_MSG_ID]);
-                } else if (msg.what >= FIRST_PACKAGE_MSG_ID
-                        && msg.what <= LAST_PACKAGE_MSG_ID) {
-                    Log.v(LOGTAG, HandlerPackageDebugString[msg.what
-                            - FIRST_PACKAGE_MSG_ID]);
-                } else {
-                    Log.v(LOGTAG, Integer.toString(msg.what));
-                }
-            }
-            if (mWebViewCore == null) {
-                // after WebView's destroy() is called, skip handling messages.
-                return;
-            }
-            if (mBlockWebkitViewMessages
-                    && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
-                // Blocking messages from webkit
-                return;
-            }
-            switch (msg.what) {
-                case REMEMBER_PASSWORD: {
-                    mDatabase.setUsernamePassword(
-                            msg.getData().getString("host"),
-                            msg.getData().getString("username"),
-                            msg.getData().getString("password"));
-                    ((Message) msg.obj).sendToTarget();
-                    break;
-                }
-                case NEVER_REMEMBER_PASSWORD: {
-                    mDatabase.setUsernamePassword(
-                            msg.getData().getString("host"), null, null);
-                    ((Message) msg.obj).sendToTarget();
-                    break;
-                }
-                case PREVENT_DEFAULT_TIMEOUT: {
-                    // if timeout happens, cancel it so that it won't block UI
-                    // to continue handling touch events
-                    if ((msg.arg1 == MotionEvent.ACTION_DOWN
-                            && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
-                            || (msg.arg1 == MotionEvent.ACTION_MOVE
-                            && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
-                        cancelWebCoreTouchEvent(
-                                viewToContentX(mLastTouchX + mScrollX),
-                                viewToContentY(mLastTouchY + mScrollY),
-                                true);
-                    }
-                    break;
-                }
-                case SCROLL_SELECT_TEXT: {
-                    if (mAutoScrollX == 0 && mAutoScrollY == 0) {
-                        mSentAutoScrollMessage = false;
-                        break;
-                    }
-                    if (mCurrentScrollingLayerId == 0) {
-                        pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
-                    } else {
-                        scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
-                                mScrollingLayerRect.top + mAutoScrollY);
-                    }
-                    sendEmptyMessageDelayed(
-                            SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
-                    break;
-                }
-                case UPDATE_SELECTION: {
-                    if (mTouchMode == TOUCH_INIT_MODE
-                            || mTouchMode == TOUCH_SHORTPRESS_MODE
-                            || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
-                        updateSelection();
-                    }
-                    break;
-                }
-                case SWITCH_TO_SHORTPRESS: {
-                    if (mTouchMode == TOUCH_INIT_MODE) {
-                        if (!sDisableNavcache
-                                && mPreventDefault != PREVENT_DEFAULT_YES) {
-                            mTouchMode = TOUCH_SHORTPRESS_START_MODE;
-                            updateSelection();
-                        } else {
-                            // set to TOUCH_SHORTPRESS_MODE so that it won't
-                            // trigger double tap any more
-                            mTouchMode = TOUCH_SHORTPRESS_MODE;
-                        }
-                    } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
-                        mTouchMode = TOUCH_DONE_MODE;
-                    }
-                    break;
-                }
-                case SWITCH_TO_LONGPRESS: {
-                    if (sDisableNavcache) {
-                        removeTouchHighlight();
-                    }
-                    if (inFullScreenMode() || mDeferTouchProcess) {
-                        TouchEventData ted = new TouchEventData();
-                        ted.mAction = WebViewCore.ACTION_LONGPRESS;
-                        ted.mIds = new int[1];
-                        ted.mIds[0] = 0;
-                        ted.mPoints = new Point[1];
-                        ted.mPoints[0] = new Point(viewToContentX(mLastTouchX + mScrollX),
-                                                   viewToContentY(mLastTouchY + mScrollY));
-                        ted.mPointsInView = new Point[1];
-                        ted.mPointsInView[0] = new Point(mLastTouchX, mLastTouchY);
-                        // metaState for long press is tricky. Should it be the
-                        // state when the press started or when the press was
-                        // released? Or some intermediary key state? For
-                        // simplicity for now, we don't set it.
-                        ted.mMetaState = 0;
-                        ted.mReprocess = mDeferTouchProcess;
-                        ted.mNativeLayer = nativeScrollableLayer(
-                                ted.mPoints[0].x, ted.mPoints[0].y,
-                                ted.mNativeLayerRect, null);
-                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
-                        mTouchEventQueue.preQueueTouchEventData(ted);
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                    } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
-                        mTouchMode = TOUCH_DONE_MODE;
-                        performLongClick();
-                    }
-                    break;
-                }
-                case RELEASE_SINGLE_TAP: {
-                    doShortPress();
-                    break;
-                }
-                case SCROLL_TO_MSG_ID: {
-                    // arg1 = animate, arg2 = onlyIfImeIsShowing
-                    // obj = Point(x, y)
-                    if (msg.arg2 == 1) {
-                        // This scroll is intended to bring the textfield into
-                        // view, but is only necessary if the IME is showing
-                        InputMethodManager imm = InputMethodManager.peekInstance();
-                        if (imm == null || !imm.isAcceptingText()
-                                || (!imm.isActive(WebView.this) && (!inEditingMode()
-                                || !imm.isActive(mWebTextView)))) {
-                            break;
-                        }
-                    }
-                    final Point p = (Point) msg.obj;
-                    if (msg.arg1 == 1) {
-                        spawnContentScrollTo(p.x, p.y);
-                    } else {
-                        setContentScrollTo(p.x, p.y);
-                    }
-                    break;
-                }
-                case UPDATE_ZOOM_RANGE: {
-                    WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
-                    // mScrollX contains the new minPrefWidth
-                    mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
-                    break;
-                }
-                case UPDATE_ZOOM_DENSITY: {
-                    final float density = (Float) msg.obj;
-                    mZoomManager.updateDefaultZoomDensity(density);
-                    break;
-                }
-                case REPLACE_BASE_CONTENT: {
-                    nativeReplaceBaseContent(msg.arg1);
-                    break;
-                }
-                case NEW_PICTURE_MSG_ID: {
-                    // called for new content
-                    final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
-                    setNewPicture(draw, true);
-                    break;
-                }
-                case WEBCORE_INITIALIZED_MSG_ID:
-                    // nativeCreate sets mNativeClass to a non-zero value
-                    String drawableDir = BrowserFrame.getRawResFilename(
-                            BrowserFrame.DRAWABLEDIR, mContext);
-                    WindowManager windowManager =
-                            (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-                    Display display = windowManager.getDefaultDisplay();
-                    nativeCreate(msg.arg1, drawableDir,
-                            ActivityManager.isHighEndGfx(display));
-                    if (mDelaySetPicture != null) {
-                        setNewPicture(mDelaySetPicture, true);
-                        mDelaySetPicture = null;
-                    }
-                    if (mIsPaused) {
-                        nativeSetPauseDrawing(mNativeClass, true);
-                    }
-                    break;
-                case UPDATE_TEXTFIELD_TEXT_MSG_ID:
-                    // Make sure that the textfield is currently focused
-                    // and representing the same node as the pointer.
-                    if (msg.arg2 == mTextGeneration) {
-                        String text = (String) msg.obj;
-                        if (null == text) {
-                            text = "";
-                        }
-                        if (inEditingMode() &&
-                                mWebTextView.isSameTextField(msg.arg1)) {
-                            mWebTextView.setTextAndKeepSelection(text);
-                        } else if (mInputConnection != null &&
-                                mFieldPointer == msg.arg1) {
-                            mInputConnection.setTextAndKeepSelection(text);
-                        }
-                    }
-                    break;
-                case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
-                    displaySoftKeyboard(true);
-                    // fall through to UPDATE_TEXT_SELECTION_MSG_ID
-                case UPDATE_TEXT_SELECTION_MSG_ID:
-                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
-                            (WebViewCore.TextSelectionData) msg.obj);
-                    break;
-                case FORM_DID_BLUR:
-                    if (inEditingMode()
-                            && mWebTextView.isSameTextField(msg.arg1)) {
-                        hideSoftKeyboard();
-                    }
-                    break;
-                case RETURN_LABEL:
-                    if (inEditingMode()
-                            && mWebTextView.isSameTextField(msg.arg1)) {
-                        mWebTextView.setHint((String) msg.obj);
-                        InputMethodManager imm
-                                = InputMethodManager.peekInstance();
-                        // The hint is propagated to the IME in
-                        // onCreateInputConnection.  If the IME is already
-                        // active, restart it so that its hint text is updated.
-                        if (imm != null && imm.isActive(mWebTextView)) {
-                            imm.restartInput(mWebTextView);
-                        }
-                    }
-                    break;
-                case UNHANDLED_NAV_KEY:
-                    navHandledKey(msg.arg1, 1, false, 0);
-                    break;
-                case UPDATE_TEXT_ENTRY_MSG_ID:
-                    // this is sent after finishing resize in WebViewCore. Make
-                    // sure the text edit box is still on the  screen.
-                    if (inEditingMode() && nativeCursorIsTextInput()) {
-                        updateWebTextViewPosition();
-                    }
-                    break;
-                case CLEAR_TEXT_ENTRY:
-                    clearTextEntry();
-                    break;
-                case INVAL_RECT_MSG_ID: {
-                    Rect r = (Rect)msg.obj;
-                    if (r == null) {
-                        invalidate();
-                    } else {
-                        // we need to scale r from content into view coords,
-                        // which viewInvalidate() does for us
-                        viewInvalidate(r.left, r.top, r.right, r.bottom);
-                    }
-                    break;
-                }
-                case REQUEST_FORM_DATA:
-                    AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
-                    if (mWebTextView.isSameTextField(msg.arg1)) {
-                        mWebTextView.setAdapterCustom(adapter);
-                    }
-                    break;
-
-                case LONG_PRESS_CENTER:
-                    // as this is shared by keydown and trackballdown, reset all
-                    // the states
-                    mGotCenterDown = false;
-                    mTrackballDown = false;
-                    performLongClick();
-                    break;
-
-                case WEBCORE_NEED_TOUCH_EVENTS:
-                    mForwardTouchEvents = (msg.arg1 != 0);
-                    break;
-
-                case PREVENT_TOUCH_ID:
-                    if (inFullScreenMode()) {
-                        break;
-                    }
-                    TouchEventData ted = (TouchEventData) msg.obj;
-
-                    if (mTouchEventQueue.enqueueTouchEvent(ted)) {
-                        // WebCore is responding to us; remove pending timeout.
-                        // It will be re-posted when needed.
-                        removeMessages(PREVENT_DEFAULT_TIMEOUT);
-                    }
-                    break;
-
-                case REQUEST_KEYBOARD:
-                    if (msg.arg1 == 0) {
-                        hideSoftKeyboard();
-                    } else {
-                        displaySoftKeyboard(false);
-                    }
-                    break;
-
-                case DRAG_HELD_MOTIONLESS:
-                    mHeldMotionless = MOTIONLESS_TRUE;
-                    invalidate();
-                    // fall through to keep scrollbars awake
-
-                case AWAKEN_SCROLL_BARS:
-                    if (mTouchMode == TOUCH_DRAG_MODE
-                            && mHeldMotionless == MOTIONLESS_TRUE) {
-                        awakenScrollBars(ViewConfiguration
-                                .getScrollDefaultDelay(), false);
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(AWAKEN_SCROLL_BARS),
-                                ViewConfiguration.getScrollDefaultDelay());
-                    }
-                    break;
-
-                case DO_MOTION_UP:
-                    doMotionUp(msg.arg1, msg.arg2);
-                    break;
-
-                case SCREEN_ON:
-                    setKeepScreenOn(msg.arg1 == 1);
-                    break;
-
-                case ENTER_FULLSCREEN_VIDEO:
-                    int layerId = msg.arg1;
-
-                    String url = (String) msg.obj;
-                    if (mHTML5VideoViewProxy != null) {
-                        mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url);
-                    }
-                    break;
-
-                case EXIT_FULLSCREEN_VIDEO:
-                    if (mHTML5VideoViewProxy != null) {
-                        mHTML5VideoViewProxy.exitFullScreenVideo();
-                    }
-                    break;
-
-                case SHOW_FULLSCREEN: {
-                    View view = (View) msg.obj;
-                    int orientation = msg.arg1;
-                    int npp = msg.arg2;
-
-                    if (inFullScreenMode()) {
-                        Log.w(LOGTAG, "Should not have another full screen.");
-                        dismissFullScreenMode();
-                    }
-                    mFullScreenHolder = new PluginFullScreenHolder(WebView.this, orientation, npp);
-                    mFullScreenHolder.setContentView(view);
-                    mFullScreenHolder.show();
-                    invalidate();
-
-                    break;
-                }
-                case HIDE_FULLSCREEN:
-                    dismissFullScreenMode();
-                    break;
-
-                case DOM_FOCUS_CHANGED:
-                    if (inEditingMode()) {
-                        nativeClearCursor();
-                        rebuildWebTextView();
-                    }
-                    break;
-
-                case SHOW_RECT_MSG_ID: {
-                    WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
-                    int x = mScrollX;
-                    int left = contentToViewX(data.mLeft);
-                    int width = contentToViewDimension(data.mWidth);
-                    int maxWidth = contentToViewDimension(data.mContentWidth);
-                    int viewWidth = getViewWidth();
-                    if (width < viewWidth) {
-                        // center align
-                        x += left + width / 2 - mScrollX - viewWidth / 2;
-                    } else {
-                        x += (int) (left + data.mXPercentInDoc * width
-                                - mScrollX - data.mXPercentInView * viewWidth);
-                    }
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
-                              width + ",maxWidth=" + maxWidth +
-                              ",viewWidth=" + viewWidth + ",x="
-                              + x + ",xPercentInDoc=" + data.mXPercentInDoc +
-                              ",xPercentInView=" + data.mXPercentInView+ ")");
-                    }
-                    // use the passing content width to cap x as the current
-                    // mContentWidth may not be updated yet
-                    x = Math.max(0,
-                            (Math.min(maxWidth, x + viewWidth)) - viewWidth);
-                    int top = contentToViewY(data.mTop);
-                    int height = contentToViewDimension(data.mHeight);
-                    int maxHeight = contentToViewDimension(data.mContentHeight);
-                    int viewHeight = getViewHeight();
-                    int y = (int) (top + data.mYPercentInDoc * height -
-                                   data.mYPercentInView * viewHeight);
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
-                              height + ",maxHeight=" + maxHeight +
-                              ",viewHeight=" + viewHeight + ",y="
-                              + y + ",yPercentInDoc=" + data.mYPercentInDoc +
-                              ",yPercentInView=" + data.mYPercentInView+ ")");
-                    }
-                    // use the passing content height to cap y as the current
-                    // mContentHeight may not be updated yet
-                    y = Math.max(0,
-                            (Math.min(maxHeight, y + viewHeight) - viewHeight));
-                    // We need to take into account the visible title height
-                    // when scrolling since y is an absolute view position.
-                    y = Math.max(0, y - getVisibleTitleHeightImpl());
-                    scrollTo(x, y);
-                    }
-                    break;
-
-                case CENTER_FIT_RECT:
-                    centerFitRect((Rect)msg.obj);
-                    break;
-
-                case SET_SCROLLBAR_MODES:
-                    mHorizontalScrollBarMode = msg.arg1;
-                    mVerticalScrollBarMode = msg.arg2;
-                    break;
-
-                case SELECTION_STRING_CHANGED:
-                    if (mAccessibilityInjector != null) {
-                        String selectionString = (String) msg.obj;
-                        mAccessibilityInjector.onSelectionStringChange(selectionString);
-                    }
-                    break;
-
-                case HIT_TEST_RESULT:
-                    WebKitHitTest hit = (WebKitHitTest) msg.obj;
-                    mFocusedNode = hit;
-                    setTouchHighlightRects(hit);
-                    setHitTestResult(hit);
-                    break;
-
-                case SAVE_WEBARCHIVE_FINISHED:
-                    SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
-                    if (saveMessage.mCallback != null) {
-                        saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
-                    }
-                    break;
-
-                case SET_AUTOFILLABLE:
-                    mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
-                    if (mWebTextView != null) {
-                        mWebTextView.setAutoFillable(mAutoFillData.getQueryId());
-                        rebuildWebTextView();
-                    }
-                    break;
-
-                case AUTOFILL_COMPLETE:
-                    if (mWebTextView != null) {
-                        // Clear the WebTextView adapter when AutoFill finishes
-                        // so that the drop down gets cleared.
-                        mWebTextView.setAdapterCustom(null);
-                    }
-                    break;
-
-                case SELECT_AT:
-                    nativeSelectAt(msg.arg1, msg.arg2);
-                    break;
-
-                case COPY_TO_CLIPBOARD:
-                    copyToClipboard((String) msg.obj);
-                    break;
-
-                case INIT_EDIT_FIELD:
-                    if (mInputConnection != null) {
-                        TextFieldInitData initData = (TextFieldInitData) msg.obj;
-                        mTextGeneration = 0;
-                        mFieldPointer = initData.mFieldPointer;
-                        mInputConnection.initEditorInfo(initData);
-                        mInputConnection.setTextAndKeepSelection(initData.mText);
-                    }
-                    break;
-
-                case REPLACE_TEXT:{
-                    String text = (String)msg.obj;
-                    int start = msg.arg1;
-                    int end = msg.arg2;
-                    int cursorPosition = start + text.length();
-                    replaceTextfieldText(start, end, text,
-                            cursorPosition, cursorPosition);
-                    break;
-                }
-
-                case UPDATE_MATCH_COUNT: {
-                    if (mFindCallback != null) {
-                        mFindCallback.updateMatchCount(msg.arg1, msg.arg2,
-                            (String) msg.obj);
-                    }
-                    break;
-                }
-                case CLEAR_CARET_HANDLE:
-                    selectionDone();
-                    break;
-
-                case KEY_PRESS:
-                    mWebViewCore.sendMessage(EventHub.KEY_PRESS, msg.arg1);
-                    break;
-
-                default:
-                    super.handleMessage(msg);
-                    break;
-            }
-        }
-    }
-
-    private void setHitTestTypeFromUrl(String url) {
-        String substr = null;
-        if (url.startsWith(SCHEME_GEO)) {
-            mInitialHitTestResult.mType = HitTestResult.GEO_TYPE;
-            substr = url.substring(SCHEME_GEO.length());
-        } else if (url.startsWith(SCHEME_TEL)) {
-            mInitialHitTestResult.mType = HitTestResult.PHONE_TYPE;
-            substr = url.substring(SCHEME_TEL.length());
-        } else if (url.startsWith(SCHEME_MAILTO)) {
-            mInitialHitTestResult.mType = HitTestResult.EMAIL_TYPE;
-            substr = url.substring(SCHEME_MAILTO.length());
-        } else {
-            mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
-            mInitialHitTestResult.mExtra = url;
-            return;
-        }
-        try {
-            mInitialHitTestResult.mExtra = URLDecoder.decode(substr, "UTF-8");
-        } catch (Throwable e) {
-            Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
-            mInitialHitTestResult.mType = HitTestResult.UNKNOWN_TYPE;
-        }
-    }
-
-    private void setHitTestResult(WebKitHitTest hit) {
-        if (hit == null) {
-            mInitialHitTestResult = null;
-            return;
-        }
-        mInitialHitTestResult = new HitTestResult();
-        if (hit.mLinkUrl != null) {
-            setHitTestTypeFromUrl(hit.mLinkUrl);
-            if (hit.mImageUrl != null
-                    && mInitialHitTestResult.mType == HitTestResult.SRC_ANCHOR_TYPE) {
-                mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
-                mInitialHitTestResult.mExtra = hit.mImageUrl;
-            }
-        } else if (hit.mImageUrl != null) {
-            mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
-            mInitialHitTestResult.mExtra = hit.mImageUrl;
-        } else if (hit.mEditable) {
-            mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
-        } else if (hit.mIntentUrl != null) {
-            setHitTestTypeFromUrl(hit.mIntentUrl);
-        }
-    }
-
-    private boolean shouldDrawHighlightRect() {
-        if (mFocusedNode == null || mInitialHitTestResult == null) {
-            return false;
-        }
-        if (mTouchHighlightRegion.isEmpty()) {
-            return false;
-        }
-        if (mFocusedNode.mHasFocus && !isInTouchMode()) {
-            return !mFocusedNode.mEditable;
-        }
-        if (mInitialHitTestResult.mType == HitTestResult.UNKNOWN_TYPE) {
-            return false;
-        }
-        long delay = System.currentTimeMillis() - mTouchHighlightRequested;
-        if (delay < ViewConfiguration.getTapTimeout()) {
-            Rect r = mTouchHighlightRegion.getBounds();
-            postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
-            return false;
-        }
-        return true;
-    }
-
-
-    private FocusTransitionDrawable mFocusTransition = null;
-    static class FocusTransitionDrawable extends Drawable {
-        Region mPreviousRegion;
-        Region mNewRegion;
-        float mProgress = 0;
-        WebView mWebView;
-        Paint mPaint;
-        int mMaxAlpha;
-        Point mTranslate;
-
-        public FocusTransitionDrawable(WebView view) {
-            mWebView = view;
-            mPaint = new Paint(mWebView.mTouchHightlightPaint);
-            mMaxAlpha = mPaint.getAlpha();
-        }
-
-        @Override
-        public void setColorFilter(ColorFilter cf) {
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-        }
-
-        @Override
-        public int getOpacity() {
-            return 0;
-        }
-
-        public void setProgress(float p) {
-            mProgress = p;
-            if (mWebView.mFocusTransition == this) {
-                if (mProgress == 1f)
-                    mWebView.mFocusTransition = null;
-                mWebView.invalidate();
-            }
-        }
-
-        public float getProgress() {
-            return mProgress;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            if (mTranslate == null) {
-                Rect bounds = mPreviousRegion.getBounds();
-                Point from = new Point(bounds.centerX(), bounds.centerY());
-                mNewRegion.getBounds(bounds);
-                Point to = new Point(bounds.centerX(), bounds.centerY());
-                mTranslate = new Point(from.x - to.x, from.y - to.y);
-            }
-            int alpha = (int) (mProgress * mMaxAlpha);
-            RegionIterator iter = new RegionIterator(mPreviousRegion);
-            Rect r = new Rect();
-            mPaint.setAlpha(mMaxAlpha - alpha);
-            float tx = mTranslate.x * mProgress;
-            float ty = mTranslate.y * mProgress;
-            int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            canvas.translate(-tx, -ty);
-            while (iter.next(r)) {
-                canvas.drawRect(r, mPaint);
-            }
-            canvas.restoreToCount(save);
-            iter = new RegionIterator(mNewRegion);
-            r = new Rect();
-            mPaint.setAlpha(alpha);
-            save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            tx = mTranslate.x - tx;
-            ty = mTranslate.y - ty;
-            canvas.translate(tx, ty);
-            while (iter.next(r)) {
-                canvas.drawRect(r, mPaint);
-            }
-            canvas.restoreToCount(save);
-        }
-    };
-
-    private boolean shouldAnimateTo(WebKitHitTest hit) {
-        // TODO: Don't be annoying or throw out the animation entirely
-        return false;
-    }
-
-    private void setTouchHighlightRects(WebKitHitTest hit) {
-        FocusTransitionDrawable transition = null;
-        if (shouldAnimateTo(hit)) {
-            transition = new FocusTransitionDrawable(this);
-        }
-        Rect[] rects = hit != null ? hit.mTouchRects : null;
-        if (!mTouchHighlightRegion.isEmpty()) {
-            invalidate(mTouchHighlightRegion.getBounds());
-            if (transition != null) {
-                transition.mPreviousRegion = new Region(mTouchHighlightRegion);
-            }
-            mTouchHighlightRegion.setEmpty();
-        }
-        if (rects != null) {
-            mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
-            for (Rect rect : rects) {
-                Rect viewRect = contentToViewRect(rect);
-                // some sites, like stories in nytimes.com, set
-                // mouse event handler in the top div. It is not
-                // user friendly to highlight the div if it covers
-                // more than half of the screen.
-                if (viewRect.width() < getWidth() >> 1
-                        || viewRect.height() < getHeight() >> 1) {
-                    mTouchHighlightRegion.union(viewRect);
-                } else {
-                    Log.w(LOGTAG, "Skip the huge selection rect:"
-                            + viewRect);
-                }
-            }
-            invalidate(mTouchHighlightRegion.getBounds());
-            if (transition != null && transition.mPreviousRegion != null) {
-                transition.mNewRegion = new Region(mTouchHighlightRegion);
-                mFocusTransition = transition;
-                ObjectAnimator animator = ObjectAnimator.ofFloat(
-                        mFocusTransition, "progress", 1f);
-                animator.start();
-            }
-        }
-    }
-
-    /** @hide Called by JNI when pages are swapped (only occurs with hardware
-     * acceleration) */
-    protected void pageSwapCallback(boolean notifyAnimationStarted) {
-        mWebViewCore.resumeWebKitDraw();
-        if (inEditingMode()) {
-            didUpdateWebTextViewDimensions(ANYWHERE);
-        }
-        if (notifyAnimationStarted) {
-            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
-        }
-    }
-
-    void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
-        if (mNativeClass == 0) {
-            if (mDelaySetPicture != null) {
-                throw new IllegalStateException("Tried to setNewPicture with"
-                        + " a delay picture already set! (memory leak)");
-            }
-            // Not initialized yet, delay set
-            mDelaySetPicture = draw;
-            return;
-        }
-        WebViewCore.ViewState viewState = draw.mViewState;
-        boolean isPictureAfterFirstLayout = viewState != null;
-
-        if (updateBaseLayer) {
-            setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
-                    getSettings().getShowVisualIndicator(),
-                    isPictureAfterFirstLayout);
-        }
-        final Point viewSize = draw.mViewSize;
-        // We update the layout (i.e. request a layout from the
-        // view system) if the last view size that we sent to
-        // WebCore matches the view size of the picture we just
-        // received in the fixed dimension.
-        final boolean updateLayout = viewSize.x == mLastWidthSent
-                && viewSize.y == mLastHeightSent;
-        // Don't send scroll event for picture coming from webkit,
-        // since the new picture may cause a scroll event to override
-        // the saved history scroll position.
-        mSendScrollEvent = false;
-        recordNewContentSize(draw.mContentSize.x,
-                draw.mContentSize.y, updateLayout);
-        if (isPictureAfterFirstLayout) {
-            // Reset the last sent data here since dealing with new page.
-            mLastWidthSent = 0;
-            mZoomManager.onFirstLayout(draw);
-            int scrollX = viewState.mShouldStartScrolledRight
-                    ? getContentWidth() : viewState.mScrollX;
-            int scrollY = viewState.mScrollY;
-            setContentScrollTo(scrollX, scrollY);
-            if (!mDrawHistory) {
-                // As we are on a new page, remove the WebTextView. This
-                // is necessary for page loads driven by webkit, and in
-                // particular when the user was on a password field, so
-                // the WebTextView was visible.
-                clearTextEntry();
-            }
-        }
-        mSendScrollEvent = true;
-
-        if (DebugFlags.WEB_VIEW) {
-            Rect b = draw.mInvalRegion.getBounds();
-            Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
-                    b.left+","+b.top+","+b.right+","+b.bottom+"}");
-        }
-        invalidateContentRect(draw.mInvalRegion.getBounds());
-
-        if (mPictureListener != null) {
-            mPictureListener.onNewPicture(WebView.this, capturePicture());
-        }
-
-        // update the zoom information based on the new picture
-        mZoomManager.onNewPicture(draw);
-
-        if (draw.mFocusSizeChanged && inEditingMode()) {
-            mFocusSizeChanged = true;
-        }
-        if (isPictureAfterFirstLayout) {
-            mViewManager.postReadyToDrawAll();
-        }
-    }
-
-    /**
-     * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
-     * and UPDATE_TEXT_SELECTION_MSG_ID.  Update the selection of WebTextView.
-     */
-    private void updateTextSelectionFromMessage(int nodePointer,
-            int textGeneration, WebViewCore.TextSelectionData data) {
-        if (textGeneration == mTextGeneration) {
-            if (inEditingMode()
-                    && mWebTextView.isSameTextField(nodePointer)) {
-                mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
-            } else if (mInputConnection != null && mFieldPointer == nodePointer) {
-                mInputConnection.setSelection(data.mStart, data.mEnd);
-            }
-        }
-        nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
-
-        if (data.mSelectTextPtr != 0 &&
-                (data.mStart != data.mEnd ||
-                (mFieldPointer == nodePointer && mFieldPointer != 0))) {
-            mIsCaretSelection = (data.mStart == data.mEnd);
-            if (!mSelectingText) {
-                setupWebkitSelect();
-            } else if (!mSelectionStarted) {
-                syncSelectionCursors();
-            }
-            if (mIsCaretSelection) {
-                resetCaretTimer();
-            }
-        } else {
-            selectionDone();
-        }
-        invalidate();
-    }
-
-    // Class used to use a dropdown for a <select> element
-    private class InvokeListBox implements Runnable {
-        // Whether the listbox allows multiple selection.
-        private boolean     mMultiple;
-        // Passed in to a list with multiple selection to tell
-        // which items are selected.
-        private int[]       mSelectedArray;
-        // Passed in to a list with single selection to tell
-        // where the initial selection is.
-        private int         mSelection;
-
-        private Container[] mContainers;
-
-        // Need these to provide stable ids to my ArrayAdapter,
-        // which normally does not have stable ids. (Bug 1250098)
-        private class Container extends Object {
-            /**
-             * Possible values for mEnabled.  Keep in sync with OptionStatus in
-             * WebViewCore.cpp
-             */
-            final static int OPTGROUP = -1;
-            final static int OPTION_DISABLED = 0;
-            final static int OPTION_ENABLED = 1;
-
-            String  mString;
-            int     mEnabled;
-            int     mId;
-
-            @Override
-            public String toString() {
-                return mString;
-            }
-        }
-
-        /**
-         *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
-         *  and allow filtering.
-         */
-        private class MyArrayListAdapter extends ArrayAdapter<Container> {
-            public MyArrayListAdapter() {
-                super(mContext,
-                        mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
-                        com.android.internal.R.layout.webview_select_singlechoice,
-                        mContainers);
-            }
-
-            @Override
-            public View getView(int position, View convertView,
-                    ViewGroup parent) {
-                // Always pass in null so that we will get a new CheckedTextView
-                // Otherwise, an item which was previously used as an <optgroup>
-                // element (i.e. has no check), could get used as an <option>
-                // element, which needs a checkbox/radio, but it would not have
-                // one.
-                convertView = super.getView(position, null, parent);
-                Container c = item(position);
-                if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
-                    // ListView does not draw dividers between disabled and
-                    // enabled elements.  Use a LinearLayout to provide dividers
-                    LinearLayout layout = new LinearLayout(mContext);
-                    layout.setOrientation(LinearLayout.VERTICAL);
-                    if (position > 0) {
-                        View dividerTop = new View(mContext);
-                        dividerTop.setBackgroundResource(
-                                android.R.drawable.divider_horizontal_bright);
-                        layout.addView(dividerTop);
-                    }
-
-                    if (Container.OPTGROUP == c.mEnabled) {
-                        // Currently select_dialog_multichoice uses CheckedTextViews.
-                        // If that changes, the class cast will no longer be valid.
-                        if (mMultiple) {
-                            Assert.assertTrue(convertView instanceof CheckedTextView);
-                            ((CheckedTextView) convertView).setCheckMarkDrawable(null);
-                        }
-                    } else {
-                        // c.mEnabled == Container.OPTION_DISABLED
-                        // Draw the disabled element in a disabled state.
-                        convertView.setEnabled(false);
-                    }
-
-                    layout.addView(convertView);
-                    if (position < getCount() - 1) {
-                        View dividerBottom = new View(mContext);
-                        dividerBottom.setBackgroundResource(
-                                android.R.drawable.divider_horizontal_bright);
-                        layout.addView(dividerBottom);
-                    }
-                    return layout;
-                }
-                return convertView;
-            }
-
-            @Override
-            public boolean hasStableIds() {
-                // AdapterView's onChanged method uses this to determine whether
-                // to restore the old state.  Return false so that the old (out
-                // of date) state does not replace the new, valid state.
-                return false;
-            }
-
-            private Container item(int position) {
-                if (position < 0 || position >= getCount()) {
-                    return null;
-                }
-                return getItem(position);
-            }
-
-            @Override
-            public long getItemId(int position) {
-                Container item = item(position);
-                if (item == null) {
-                    return -1;
-                }
-                return item.mId;
-            }
-
-            @Override
-            public boolean areAllItemsEnabled() {
-                return false;
-            }
-
-            @Override
-            public boolean isEnabled(int position) {
-                Container item = item(position);
-                if (item == null) {
-                    return false;
-                }
-                return Container.OPTION_ENABLED == item.mEnabled;
-            }
-        }
-
-        private InvokeListBox(String[] array, int[] enabled, int[] selected) {
-            mMultiple = true;
-            mSelectedArray = selected;
-
-            int length = array.length;
-            mContainers = new Container[length];
-            for (int i = 0; i < length; i++) {
-                mContainers[i] = new Container();
-                mContainers[i].mString = array[i];
-                mContainers[i].mEnabled = enabled[i];
-                mContainers[i].mId = i;
-            }
-        }
-
-        private InvokeListBox(String[] array, int[] enabled, int selection) {
-            mSelection = selection;
-            mMultiple = false;
-
-            int length = array.length;
-            mContainers = new Container[length];
-            for (int i = 0; i < length; i++) {
-                mContainers[i] = new Container();
-                mContainers[i].mString = array[i];
-                mContainers[i].mEnabled = enabled[i];
-                mContainers[i].mId = i;
-            }
-        }
-
-        /*
-         * Whenever the data set changes due to filtering, this class ensures
-         * that the checked item remains checked.
-         */
-        private class SingleDataSetObserver extends DataSetObserver {
-            private long        mCheckedId;
-            private ListView    mListView;
-            private Adapter     mAdapter;
-
-            /*
-             * Create a new observer.
-             * @param id The ID of the item to keep checked.
-             * @param l ListView for getting and clearing the checked states
-             * @param a Adapter for getting the IDs
-             */
-            public SingleDataSetObserver(long id, ListView l, Adapter a) {
-                mCheckedId = id;
-                mListView = l;
-                mAdapter = a;
-            }
-
-            @Override
-            public void onChanged() {
-                // The filter may have changed which item is checked.  Find the
-                // item that the ListView thinks is checked.
-                int position = mListView.getCheckedItemPosition();
-                long id = mAdapter.getItemId(position);
-                if (mCheckedId != id) {
-                    // Clear the ListView's idea of the checked item, since
-                    // it is incorrect
-                    mListView.clearChoices();
-                    // Search for mCheckedId.  If it is in the filtered list,
-                    // mark it as checked
-                    int count = mAdapter.getCount();
-                    for (int i = 0; i < count; i++) {
-                        if (mAdapter.getItemId(i) == mCheckedId) {
-                            mListView.setItemChecked(i, true);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            final ListView listView = (ListView) LayoutInflater.from(mContext)
-                    .inflate(com.android.internal.R.layout.select_dialog, null);
-            final MyArrayListAdapter adapter = new MyArrayListAdapter();
-            AlertDialog.Builder b = new AlertDialog.Builder(mContext)
-                    .setView(listView).setCancelable(true)
-                    .setInverseBackgroundForced(true);
-
-            if (mMultiple) {
-                b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mWebViewCore.sendMessage(
-                                EventHub.LISTBOX_CHOICES,
-                                adapter.getCount(), 0,
-                                listView.getCheckedItemPositions());
-                    }});
-                b.setNegativeButton(android.R.string.cancel,
-                        new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mWebViewCore.sendMessage(
-                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
-                }});
-            }
-            mListBoxDialog = b.create();
-            listView.setAdapter(adapter);
-            listView.setFocusableInTouchMode(true);
-            // There is a bug (1250103) where the checks in a ListView with
-            // multiple items selected are associated with the positions, not
-            // the ids, so the items do not properly retain their checks when
-            // filtered.  Do not allow filtering on multiple lists until
-            // that bug is fixed.
-
-            listView.setTextFilterEnabled(!mMultiple);
-            if (mMultiple) {
-                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-                int length = mSelectedArray.length;
-                for (int i = 0; i < length; i++) {
-                    listView.setItemChecked(mSelectedArray[i], true);
-                }
-            } else {
-                listView.setOnItemClickListener(new OnItemClickListener() {
-                    @Override
-                    public void onItemClick(AdapterView<?> parent, View v,
-                            int position, long id) {
-                        // Rather than sending the message right away, send it
-                        // after the page regains focus.
-                        mListBoxMessage = Message.obtain(null,
-                                EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
-                        mListBoxDialog.dismiss();
-                        mListBoxDialog = null;
-                    }
-                });
-                if (mSelection != -1) {
-                    listView.setSelection(mSelection);
-                    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-                    listView.setItemChecked(mSelection, true);
-                    DataSetObserver observer = new SingleDataSetObserver(
-                            adapter.getItemId(mSelection), listView, adapter);
-                    adapter.registerDataSetObserver(observer);
-                }
-            }
-            mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    mWebViewCore.sendMessage(
-                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
-                    mListBoxDialog = null;
-                }
-            });
-            mListBoxDialog.show();
-        }
-    }
-
-    private Message mListBoxMessage;
-
-    /*
-     * Request a dropdown menu for a listbox with multiple selection.
-     *
-     * @param array Labels for the listbox.
-     * @param enabledArray  State for each element in the list.  See static
-     *      integers in Container class.
-     * @param selectedArray Which positions are initally selected.
-     */
-    void requestListBox(String[] array, int[] enabledArray, int[]
-            selectedArray) {
-        mPrivateHandler.post(
-                new InvokeListBox(array, enabledArray, selectedArray));
-    }
-
-    /*
-     * Request a dropdown menu for a listbox with single selection or a single
-     * <select> element.
-     *
-     * @param array Labels for the listbox.
-     * @param enabledArray  State for each element in the list.  See static
-     *      integers in Container class.
-     * @param selection Which position is initally selected.
-     */
-    void requestListBox(String[] array, int[] enabledArray, int selection) {
-        mPrivateHandler.post(
-                new InvokeListBox(array, enabledArray, selection));
-    }
-
-    // called by JNI
-    private void sendMoveFocus(int frame, int node) {
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
-                new WebViewCore.CursorData(frame, node, 0, 0));
-    }
-
-    // called by JNI
-    private void sendMoveMouse(int frame, int node, int x, int y) {
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
-                new WebViewCore.CursorData(frame, node, x, y));
-    }
-
-    /*
-     * Send a mouse move event to the webcore thread.
-     *
-     * @param removeFocus Pass true to remove the WebTextView, if present.
-     * @param stopPaintingCaret Stop drawing the blinking caret if true.
-     * called by JNI
-     */
-    @SuppressWarnings("unused")
-    private void sendMoveMouseIfLatest(boolean removeFocus, boolean stopPaintingCaret) {
-        if (removeFocus) {
-            clearTextEntry();
-        }
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
-                stopPaintingCaret ? 1 : 0, 0,
-                cursorData());
-    }
-
-    /**
-     * Called by JNI to send a message to the webcore thread that the user
-     * touched the webpage.
-     * @param touchGeneration Generation number of the touch, to ignore touches
-     *      after a new one has been generated.
-     * @param frame Pointer to the frame holding the node that was touched.
-     * @param node Pointer to the node touched.
-     * @param x x-position of the touch.
-     * @param y y-position of the touch.
-     */
-    private void sendMotionUp(int touchGeneration,
-            int frame, int node, int x, int y) {
-        WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
-        touchUpData.mMoveGeneration = touchGeneration;
-        touchUpData.mFrame = frame;
-        touchUpData.mNode = node;
-        touchUpData.mX = x;
-        touchUpData.mY = y;
-        touchUpData.mNativeLayer = nativeScrollableLayer(
-                x, y, touchUpData.mNativeLayerRect, null);
-        mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
-    }
-
-
-    private int getScaledMaxXScroll() {
-        int width;
-        if (mHeightCanMeasure == false) {
-            width = getViewWidth() / 4;
-        } else {
-            Rect visRect = new Rect();
-            calcOurVisibleRect(visRect);
-            width = visRect.width() / 2;
-        }
-        // FIXME the divisor should be retrieved from somewhere
-        return viewToContentX(width);
-    }
-
-    private int getScaledMaxYScroll() {
-        int height;
-        if (mHeightCanMeasure == false) {
-            height = getViewHeight() / 4;
-        } else {
-            Rect visRect = new Rect();
-            calcOurVisibleRect(visRect);
-            height = visRect.height() / 2;
-        }
-        // FIXME the divisor should be retrieved from somewhere
-        // the closest thing today is hard-coded into ScrollView.java
-        // (from ScrollView.java, line 363)   int maxJump = height/2;
-        return Math.round(height * mZoomManager.getInvScale());
-    }
-
-    /**
-     * Called by JNI to invalidate view
-     */
-    private void viewInvalidate() {
-        invalidate();
-    }
-
-    /**
-     * Pass the key directly to the page.  This assumes that
-     * nativePageShouldHandleShiftAndArrows() returned true.
-     */
-    private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
-        int keyEventAction;
-        int eventHubAction;
-        if (down) {
-            keyEventAction = KeyEvent.ACTION_DOWN;
-            eventHubAction = EventHub.KEY_DOWN;
-            playSoundEffect(keyCodeToSoundsEffect(keyCode));
-        } else {
-            keyEventAction = KeyEvent.ACTION_UP;
-            eventHubAction = EventHub.KEY_UP;
-        }
-
-        KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
-                1, (metaState & KeyEvent.META_SHIFT_ON)
-                | (metaState & KeyEvent.META_ALT_ON)
-                | (metaState & KeyEvent.META_SYM_ON)
-                , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
-        mWebViewCore.sendMessage(eventHubAction, event);
-    }
-
-    // return true if the key was handled
-    private boolean navHandledKey(int keyCode, int count, boolean noScroll,
-            long time) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        mInitialHitTestResult = null;
-        mLastCursorTime = time;
-        mLastCursorBounds = nativeGetCursorRingBounds();
-        boolean keyHandled
-                = nativeMoveCursor(keyCode, count, noScroll) == false;
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
-                    + " mLastCursorTime=" + mLastCursorTime
-                    + " handled=" + keyHandled);
-        }
-        if (keyHandled == false) {
-            return keyHandled;
-        }
-        Rect contentCursorRingBounds = nativeGetCursorRingBounds();
-        if (contentCursorRingBounds.isEmpty()) return keyHandled;
-        Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
-        // set last touch so that context menu related functions will work
-        mLastTouchX = (viewCursorRingBounds.left + viewCursorRingBounds.right) / 2;
-        mLastTouchY = (viewCursorRingBounds.top + viewCursorRingBounds.bottom) / 2;
-        if (mHeightCanMeasure == false) {
-            return keyHandled;
-        }
-        Rect visRect = new Rect();
-        calcOurVisibleRect(visRect);
-        Rect outset = new Rect(visRect);
-        int maxXScroll = visRect.width() / 2;
-        int maxYScroll = visRect.height() / 2;
-        outset.inset(-maxXScroll, -maxYScroll);
-        if (Rect.intersects(outset, viewCursorRingBounds) == false) {
-            return keyHandled;
-        }
-        // FIXME: Necessary because ScrollView/ListView do not scroll left/right
-        int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
-                maxXScroll);
-        if (maxH > 0) {
-            pinScrollBy(maxH, 0, true, 0);
-        } else {
-            maxH = Math.max(viewCursorRingBounds.left - visRect.left,
-                    -maxXScroll);
-            if (maxH < 0) {
-                pinScrollBy(maxH, 0, true, 0);
-            }
-        }
-        if (mLastCursorBounds.isEmpty()) return keyHandled;
-        if (mLastCursorBounds.equals(contentCursorRingBounds)) {
-            return keyHandled;
-        }
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
-                    + contentCursorRingBounds);
-        }
-        requestRectangleOnScreen(viewCursorRingBounds);
-        return keyHandled;
-    }
-
-    /**
-     * @return Whether accessibility script has been injected.
-     */
-    private boolean accessibilityScriptInjected() {
-        // TODO: Maybe the injected script should announce its presence in
-        // the page meta-tag so the nativePageShouldHandleShiftAndArrows
-        // will check that as one of the conditions it looks for
-        return mAccessibilityScriptInjected;
-    }
-
-    /**
-     * Set the background color. It's white by default. Pass
-     * zero to make the view transparent.
-     * @param color   the ARGB color described by Color.java
-     */
-    @Override
-    public void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
+        return mProvider.zoomOut();
     }
 
     /**
@@ -10057,70 +1534,165 @@
     @Deprecated
     public void debugDump() {
         checkThread();
-        nativeDebugDump();
-        mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
+        mProvider.debugDump();
     }
 
+    //-------------------------------------------------------------------------
+    // Interface for WebView providers
+    //-------------------------------------------------------------------------
+
     /**
-     * Draw the HTML page into the specified canvas. This call ignores any
-     * view-specific zoom, scroll offset, or other changes. It does not draw
-     * any view-specific chrome, such as progress or URL bars.
+     * Used by providers to obtain the underlying implementation, e.g. when the appliction
+     * responds to WebViewClient.onCreateWindow() request.
      *
-     * @hide only needs to be accessible to Browser and testing
+     * @hide WebViewProvider is not public API.
      */
-    public void drawPage(Canvas canvas) {
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        nativeDraw(canvas, mVisibleContentRect, 0, 0, false);
+    public WebViewProvider getWebViewProvider() {
+        return mProvider;
     }
 
     /**
-     * Enable the communication b/t the webView and VideoViewProxy
-     *
-     * @hide only used by the Browser
+     * Callback interface, allows the provider implementation to access non-public methods
+     * and fields, and make super-class calls in this WebView instance.
+     * @hide Only for use by WebViewProvider implementations
      */
-    public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
-        mHTML5VideoViewProxy = proxy;
+    public class PrivateAccess {
+        // ---- Access to super-class methods ----
+        public int super_getScrollBarStyle() {
+            return WebView.super.getScrollBarStyle();
+        }
+
+        public void super_scrollTo(int scrollX, int scrollY) {
+            WebView.super.scrollTo(scrollX, scrollY);
+        }
+
+        public void super_computeScroll() {
+            WebView.super.computeScroll();
+        }
+
+        public boolean super_performLongClick() {
+            return WebView.super.performLongClick();
+        }
+
+        public boolean super_setFrame(int left, int top, int right, int bottom) {
+            return WebView.super.setFrame(left, top, right, bottom);
+        }
+
+        public boolean super_dispatchKeyEvent(KeyEvent event) {
+            return WebView.super.dispatchKeyEvent(event);
+        }
+
+        public boolean super_onGenericMotionEvent(MotionEvent event) {
+            return WebView.super.onGenericMotionEvent(event);
+        }
+
+        public boolean super_requestFocus(int direction, Rect previouslyFocusedRect) {
+            return WebView.super.requestFocus(direction, previouslyFocusedRect);
+        }
+
+        public void super_setLayoutParams(ViewGroup.LayoutParams params) {
+            WebView.super.setLayoutParams(params);
+        }
+
+        // ---- Access to non-public methods ----
+        public void overScrollBy(int deltaX, int deltaY,
+                int scrollX, int scrollY,
+                int scrollRangeX, int scrollRangeY,
+                int maxOverScrollX, int maxOverScrollY,
+                boolean isTouchEvent) {
+            WebView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+                    maxOverScrollX, maxOverScrollY, isTouchEvent);
+        }
+
+        public void awakenScrollBars(int duration) {
+            WebView.this.awakenScrollBars(duration);
+        }
+
+        public void awakenScrollBars(int duration, boolean invalidate) {
+            WebView.this.awakenScrollBars(duration, invalidate);
+        }
+
+        public float getVerticalScrollFactor() {
+            return WebView.this.getVerticalScrollFactor();
+        }
+
+        public float getHorizontalScrollFactor() {
+            return WebView.this.getHorizontalScrollFactor();
+        }
+
+        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+            WebView.this.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        public void onScrollChanged(int l, int t, int oldl, int oldt) {
+            WebView.this.onScrollChanged(l, t, oldl, oldt);
+        }
+
+        public int getHorizontalScrollbarHeight() {
+            return WebView.this.getHorizontalScrollbarHeight();
+        }
+
+        // ---- Access to (non-public) fields ----
+        /** Raw setter for the scroll X value, without invoking onScrollChanged handlers etc. */
+        public void setScrollXRaw(int scrollX) {
+            WebView.this.mScrollX = scrollX;
+        }
+
+        /** Raw setter for the scroll Y value, without invoking onScrollChanged handlers etc. */
+        public void setScrollYRaw(int scrollY) {
+            WebView.this.mScrollY = scrollY;
+        }
+
     }
 
-    /**
-     * Set the time to wait between passing touches to WebCore. See also the
-     * TOUCH_SENT_INTERVAL member for further discussion.
-     *
-     * @hide This is only used by the DRT test application.
-     */
-    public void setTouchInterval(int interval) {
-        mCurrentTouchInterval = interval;
+    //-------------------------------------------------------------------------
+    // Private internal stuff
+    //-------------------------------------------------------------------------
+
+    // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
+    // same provider.
+    private static WebViewFactoryProvider sProviderFactory;
+
+    private WebViewProvider mProvider;
+
+    private void ensureProviderCreated() {
+        checkThread();
+        if (mProvider == null) {
+            if (DEBUG) Log.v(LOGTAG, "instantiating webview provider instance");
+            // As this can get called during the base class constructor chain, pass the minimum
+            // number of dependencies here; the rest are deferred to init().
+            mProvider = getFactory().createWebView(this, new PrivateAccess());
+        }
     }
 
-    /**
-     * Copy text into the clipboard. This is called indirectly from
-     * WebViewCore.
-     * @param text The text to put into the clipboard.
-     */
-    private void copyToClipboard(String text) {
-        ClipboardManager cm = (ClipboardManager)getContext()
-                .getSystemService(Context.CLIPBOARD_SERVICE);
-        ClipData clip = ClipData.newPlainText(getTitle(), text);
-        cm.setPrimaryClip(clip);
+    private static synchronized WebViewFactoryProvider getFactory() {
+        // For now the main purpose of this function (and the factory abstration) is to keep
+        // us honest and minimize usage of WebViewClassic internals when binding the proxy.
+        checkThread();
+        if (sProviderFactory != null) return sProviderFactory;
+
+        sProviderFactory = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY);
+        if (sProviderFactory == null) {
+            if (DEBUG) Log.v (LOGTAG, "Falling back to explicit linkage");
+            sProviderFactory = new WebViewClassic.Factory();
+        }
+        return sProviderFactory;
     }
 
-    /**
-     *  Update our cache with updatedText.
-     *  @param updatedText  The new text to put in our cache.
-     *  @hide
-     */
-    protected void updateCachedTextfield(String updatedText) {
-        // Also place our generation number so that when we look at the cache
-        // we recognize that it is up to date.
-        nativeUpdateCachedTextfield(updatedText, mTextGeneration);
-    }
-
-    /*package*/ void autoFillForm(int autoFillQueryId) {
-        mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
-    }
-
-    /* package */ ViewManager getViewManager() {
-        return mViewManager;
+    private static WebViewFactoryProvider getFactoryByName(String providerName) {
+        try {
+            if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName);
+            Class<?> c = Class.forName(providerName);
+            if (DEBUG) Log.v(LOGTAG, "instantiating factory");
+            return (WebViewFactoryProvider) c.newInstance();
+        } catch (ClassNotFoundException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        } catch (IllegalAccessException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        } catch (InstantiationException e) {
+            Log.e(LOGTAG, "error loading " + providerName, e);
+        }
+        return null;
     }
 
     private static void checkThread() {
@@ -10135,226 +1707,252 @@
         }
     }
 
-    /** @hide send content invalidate */
-    protected void contentInvalidateAll() {
-        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
-            mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
-        }
+    //-------------------------------------------------------------------------
+    // Override View methods
+    //-------------------------------------------------------------------------
+
+    // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures
+    // there's a corresponding override (or better, caller) for each of them in here.
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mProvider.getViewDelegate().onAttachedToWindow();
     }
 
-    /** @hide discard all textures from tiles */
-    protected void discardAllTextures() {
-        nativeDiscardAllTextures();
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.getViewDelegate().onDetachedFromWindow();
+        super.onDetachedFromWindow();
     }
 
-    /**
-     * Begin collecting per-tile profiling data
-     *
-     * @hide only used by profiling tests
-     */
-    public void tileProfilingStart() {
-        nativeTileProfilingStart();
-    }
-    /**
-     * Return per-tile profiling data
-     *
-     * @hide only used by profiling tests
-     */
-    public float tileProfilingStop() {
-        return nativeTileProfilingStop();
+    @Override
+    public void setLayoutParams(ViewGroup.LayoutParams params) {
+        mProvider.getViewDelegate().setLayoutParams(params);
     }
 
-    /** @hide only used by profiling tests */
-    public void tileProfilingClear() {
-        nativeTileProfilingClear();
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingNumFrames() {
-        return nativeTileProfilingNumFrames();
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingNumTilesInFrame(int frame) {
-        return nativeTileProfilingNumTilesInFrame(frame);
-    }
-    /** @hide only used by profiling tests */
-    public int tileProfilingGetInt(int frame, int tile, String key) {
-        return nativeTileProfilingGetInt(frame, tile, key);
-    }
-    /** @hide only used by profiling tests */
-    public float tileProfilingGetFloat(int frame, int tile, String key) {
-        return nativeTileProfilingGetFloat(frame, tile, key);
+    @Override
+    public void setOverScrollMode(int mode) {
+        super.setOverScrollMode(mode);
+        // This method may called in the constructor chain, before the WebView provider is
+        // created. (Fortunately, this is the only method we override that can get called by
+        // any of the base class constructors).
+        ensureProviderCreated();
+        mProvider.getViewDelegate().setOverScrollMode(mode);
     }
 
-    /**
-     * Checks the focused content for an editable text field. This can be
-     * text input or ContentEditable.
-     * @return true if the focused item is an editable text field.
-     */
-    boolean focusCandidateIsEditableText() {
-        boolean isEditable = false;
-        // TODO: reverse sDisableNavcache so that its name is positive
-        boolean isNavcacheEnabled = !sDisableNavcache;
-        if (isNavcacheEnabled) {
-            isEditable = nativeFocusCandidateIsEditableText(mNativeClass);
-        } else if (mFocusedNode != null) {
-            isEditable = mFocusedNode.mEditable;
-        }
-        return isEditable;
+    @Override
+    public void setScrollBarStyle(int style) {
+        mProvider.getViewDelegate().setScrollBarStyle(style);
+        super.setScrollBarStyle(style);
     }
 
-    private native int nativeCacheHitFramePointer();
-    private native boolean  nativeCacheHitIsPlugin();
-    private native Rect nativeCacheHitNodeBounds();
-    private native int nativeCacheHitNodePointer();
-    /* package */ native void nativeClearCursor();
-    private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
-    private native int      nativeCursorFramePointer();
-    private native Rect     nativeCursorNodeBounds();
-    private native int nativeCursorNodePointer();
-    private native boolean  nativeCursorIntersects(Rect visibleRect);
-    private native boolean  nativeCursorIsAnchor();
-    private native boolean  nativeCursorIsTextInput();
-    private native Point    nativeCursorPosition();
-    private native String   nativeCursorText();
-    /**
-     * Returns true if the native cursor node says it wants to handle key events
-     * (ala plugins). This can only be called if mNativeClass is non-zero!
-     */
-    private native boolean  nativeCursorWantsKeyEvents();
-    private native void     nativeDebugDump();
-    private native void     nativeDestroy();
+    @Override
+    protected int computeHorizontalScrollRange() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollRange();
+    }
 
-    /**
-     * Draw the picture set with a background color and extra. If
-     * "splitIfNeeded" is true and the return value is not 0, the return value
-     * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
-     * native allocation can be freed.
-     */
-    private native int nativeDraw(Canvas canvas, RectF visibleRect,
-            int color, int extra, boolean splitIfNeeded);
-    private native void     nativeDumpDisplayTree(String urlOrNull);
-    private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
-    private native int      nativeGetDrawGLFunction(int nativeInstance, Rect rect,
-            Rect viewRect, RectF visibleRect, float scale, int extras);
-    private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
-            RectF visibleRect, float scale);
-    private native void     nativeExtendSelection(int x, int y);
-    /* package */ native int      nativeFocusCandidateFramePointer();
-    /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
-    /* package */ native boolean  nativeFocusCandidateIsPassword();
-    private native boolean  nativeFocusCandidateIsRtlText();
-    private native boolean  nativeFocusCandidateIsTextInput();
-    private native boolean nativeFocusCandidateIsEditableText(int nativeClass);
-    /* package */ native int      nativeFocusCandidateMaxLength();
-    /* package */ native boolean  nativeFocusCandidateIsAutoComplete();
-    /* package */ native boolean  nativeFocusCandidateIsSpellcheck();
-    /* package */ native String   nativeFocusCandidateName();
-    private native Rect     nativeFocusCandidateNodeBounds();
-    /**
-     * @return A Rect with left, top, right, bottom set to the corresponding
-     * padding values in the focus candidate, if it is a textfield/textarea with
-     * a style.  Otherwise return null.  This is not actually a rectangle; Rect
-     * is being used to pass four integers.
-     */
-    private native Rect     nativeFocusCandidatePaddingRect();
-    /* package */ native int      nativeFocusCandidatePointer();
-    private native String   nativeFocusCandidateText();
-    /* package */ native float    nativeFocusCandidateTextSize();
-    /* package */ native int nativeFocusCandidateLineHeight();
-    /**
-     * Returns an integer corresponding to WebView.cpp::type.
-     * See WebTextView.setType()
-     */
-    private native int      nativeFocusCandidateType();
-    private native int      nativeFocusCandidateLayerId();
-    private native boolean  nativeFocusIsPlugin();
-    private native Rect     nativeFocusNodeBounds();
-    /* package */ native int nativeFocusNodePointer();
-    private native Rect     nativeGetCursorRingBounds();
-    private native String   nativeGetSelection();
-    private native boolean  nativeHasCursorNode();
-    private native boolean  nativeHasFocusNode();
-    private native void     nativeHideCursor();
-    private native boolean  nativeHitSelection(int x, int y);
-    private native String   nativeImageURI(int x, int y);
-    private native Rect     nativeLayerBounds(int layer);
-    /* package */ native boolean nativeMoveCursorToNextTextInput();
-    // return true if the page has been scrolled
-    private native boolean  nativeMotionUp(int x, int y, int slop);
-    // returns false if it handled the key
-    private native boolean  nativeMoveCursor(int keyCode, int count,
-            boolean noScroll);
-    private native int      nativeMoveGeneration();
-    /**
-     * @return true if the page should get the shift and arrow keys, rather
-     * than select text/navigation.
-     *
-     * If the focus is a plugin, or if the focus and cursor match and are
-     * a contentEditable element, then the page should handle these keys.
-     */
-    private native boolean  nativePageShouldHandleShiftAndArrows();
-    private native boolean  nativePointInNavCache(int x, int y, int slop);
-    private native void     nativeSelectBestAt(Rect rect);
-    private native void     nativeSelectAt(int x, int y);
-    private native void     nativeSetExtendSelection();
-    private native void     nativeSetFindIsUp(boolean isUp);
-    private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native boolean  nativeSetBaseLayer(int nativeInstance,
-            int layer, Region invalRegion,
-            boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
-    private native int      nativeGetBaseLayer();
-    private native void     nativeShowCursorTimed();
-    private native void     nativeReplaceBaseContent(int content);
-    private native void     nativeCopyBaseContentToPicture(Picture pict);
-    private native boolean  nativeHasContent();
-    private native void     nativeSetSelectionPointer(int nativeInstance,
-            boolean set, float scale, int x, int y);
-    private native boolean  nativeStartSelection(int x, int y);
-    private native void     nativeStopGL();
-    private native Rect     nativeSubtractLayers(Rect content);
-    private native int      nativeTextGeneration();
-    private native void     nativeDiscardAllTextures();
-    private native void     nativeTileProfilingStart();
-    private native float    nativeTileProfilingStop();
-    private native void     nativeTileProfilingClear();
-    private native int      nativeTileProfilingNumFrames();
-    private native int      nativeTileProfilingNumTilesInFrame(int frame);
-    private native int      nativeTileProfilingGetInt(int frame, int tile, String key);
-    private native float    nativeTileProfilingGetFloat(int frame, int tile, String key);
-    // Never call this version except by updateCachedTextfield(String) -
-    // we always want to pass in our generation number.
-    private native void     nativeUpdateCachedTextfield(String updatedText,
-            int generation);
-    private native boolean  nativeWordSelection(int x, int y);
-    // return NO_LEFTEDGE means failure.
-    static final int NO_LEFTEDGE = -1;
-    native int nativeGetBlockLeftEdge(int x, int y, float scale);
+    @Override
+    protected int computeHorizontalScrollOffset() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollOffset();
+    }
 
-    private native void     nativeUseHardwareAccelSkia(boolean enabled);
+    @Override
+    protected int computeVerticalScrollRange() {
+        return mProvider.getScrollDelegate().computeVerticalScrollRange();
+    }
 
-    // Returns a pointer to the scrollable LayerAndroid at the given point.
-    private native int      nativeScrollableLayer(int x, int y, Rect scrollRect,
-            Rect scrollBounds);
-    /**
-     * Scroll the specified layer.
-     * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
-     * @param newX Destination x position to which to scroll.
-     * @param newY Destination y position to which to scroll.
-     * @return True if the layer is successfully scrolled.
-     */
-    private native boolean  nativeScrollLayer(int layer, int newX, int newY);
-    private native void     nativeSetIsScrolling(boolean isScrolling);
-    private native int      nativeGetBackgroundColor();
-    native boolean  nativeSetProperty(String key, String value);
-    native String   nativeGetProperty(String key);
-    /**
-     * See {@link ComponentCallbacks2} for the trim levels and descriptions
-     */
-    private static native void     nativeOnTrimMemory(int level);
-    private static native void nativeSetPauseDrawing(int instance, boolean pause);
-    private static native boolean nativeDisableNavcache();
-    private static native void nativeSetTextSelection(int instance, int selection);
-    private static native int nativeGetHandleLayerId(int instance, int handle,
-            Rect cursorLocation);
-    private static native boolean nativeIsBaseFirst(int instance);
+    @Override
+    protected int computeVerticalScrollOffset() {
+        return mProvider.getScrollDelegate().computeVerticalScrollOffset();
+    }
+
+    @Override
+    protected int computeVerticalScrollExtent() {
+        return mProvider.getScrollDelegate().computeVerticalScrollExtent();
+    }
+
+    @Override
+    public void computeScroll() {
+        mProvider.getScrollDelegate().computeScroll();
+    }
+
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onHoverEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onGenericMotionEvent(event);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTrackballEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event);
+    }
+
+    /*
+    TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not
+    to be delegating them too.
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyPreIme(keyCode, event);
+    }
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyLongPress(keyCode, event);
+    }
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyShortcut(keyCode, event);
+    }
+    */
+
+    @Deprecated
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return mProvider.getViewDelegate().shouldDelayChildPressedState();
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
+    }
+
+    /** @hide */
+    @Override
+    protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+            int l, int t, int r, int b) {
+        mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+    }
+
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+        mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mProvider.getViewDelegate().onWindowVisibilityChanged(visibility);
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        // Not using short-circuit OR: provider does suppress base-class call.
+        return mProvider.getViewDelegate().drawChild(canvas, child, drawingTime) |
+                super.drawChild(canvas, child, drawingTime);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mProvider.getViewDelegate().onDraw(canvas);
+    }
+
+    @Override
+    public boolean performLongClick() {
+        return mProvider.getViewDelegate().performLongClick();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        mProvider.getViewDelegate().onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus);
+        super.onWindowFocusChanged(hasWindowFocus);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect);
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+    }
+
+    /** @hide */
+    @Override
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        return mProvider.getViewDelegate().setFrame(left, top, right, bottom);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int ow, int oh) {
+        super.onSizeChanged(w, h, ow, oh);
+        mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh);
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mProvider.getViewDelegate().dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect);
+    }
+
+    @Deprecated
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
+        return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate);
+    }
+
+    @Override
+    public void setBackgroundColor(int color) {
+        mProvider.getViewDelegate().setBackgroundColor(color);
+    }
 }
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
new file mode 100644
index 0000000..4983003
--- /dev/null
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -0,0 +1,10477 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.animation.ObjectAnimator;
+import android.annotation.Widget;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.DrawFilter;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Picture;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.net.Proxy;
+import android.net.ProxyProperties;
+import android.net.Uri;
+import android.net.http.SslCertificate;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.security.KeyChain;
+import android.speech.tts.TextToSpeech;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.Selection;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.HardwareCanvas;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.SoundEffectConstants;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.HTML5VideoInline;
+import android.webkit.WebTextView.AutoCompleteAdapter;
+import android.webkit.WebView.HitTestResult;
+import android.webkit.WebView.PictureListener;
+import android.webkit.WebViewCore.DrawData;
+import android.webkit.WebViewCore.EventHub;
+import android.webkit.WebViewCore.TextFieldInitData;
+import android.webkit.WebViewCore.TouchEventData;
+import android.webkit.WebViewCore.TouchHighlightData;
+import android.webkit.WebViewCore.WebKitHitTest;
+import android.widget.AbsoluteLayout;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.CheckedTextView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.OverScroller;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import junit.framework.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p>A View that displays web pages. This class is the basis upon which you
+ * can roll your own web browser or simply display some online content within your Activity.
+ * It uses the WebKit rendering engine to display
+ * web pages and includes methods to navigate forward and backward
+ * through a history, zoom in and out, perform text searches and more.</p>
+ * <p>To enable the built-in zoom, set
+ * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
+ * (introduced in API version 3).
+ * <p>Note that, in order for your Activity to access the Internet and load web pages
+ * in a WebView, you must add the {@code INTERNET} permissions to your
+ * Android Manifest file:</p>
+ * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
+ *
+ * <p>This must be a child of the <a
+ * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
+ * element.</p>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
+ * tutorial</a>.</p>
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>By default, a WebView provides no browser-like widgets, does not
+ * enable JavaScript and web page errors are ignored. If your goal is only
+ * to display some HTML as a part of your UI, this is probably fine;
+ * the user won't need to interact with the web page beyond reading
+ * it, and the web page won't need to interact with the user. If you
+ * actually want a full-blown web browser, then you probably want to
+ * invoke the Browser application with a URL Intent rather than show it
+ * with a WebView. For example:
+ * <pre>
+ * Uri uri = Uri.parse("http://www.example.com");
+ * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ * startActivity(intent);
+ * </pre>
+ * <p>See {@link android.content.Intent} for more information.</p>
+ *
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
+ * or set the entire Activity window as a WebView during {@link
+ * android.app.Activity#onCreate(Bundle) onCreate()}:</p>
+ * <pre class="prettyprint">
+ * WebView webview = new WebView(this);
+ * setContentView(webview);
+ * </pre>
+ *
+ * <p>Then load the desired web page:</p>
+ * <pre>
+ * // Simplest usage: note that an exception will NOT be thrown
+ * // if there is an error loading this page (see below).
+ * webview.loadUrl("http://slashdot.org/");
+ *
+ * // OR, you can also load from an HTML string:
+ * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
+ * webview.loadData(summary, "text/html", null);
+ * // ... although note that there are restrictions on what this HTML can do.
+ * // See the JavaDocs for {@link #loadData(String,String,String) loadData()} and {@link
+ * #loadDataWithBaseURL(String,String,String,String,String) loadDataWithBaseURL()} for more info.
+ * </pre>
+ *
+ * <p>A WebView has several customization points where you can add your
+ * own behavior. These are:</p>
+ *
+ * <ul>
+ *   <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
+ *       This class is called when something that might impact a
+ *       browser UI happens, for instance, progress updates and
+ *       JavaScript alerts are sent here (see <a
+ * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
+ *   </li>
+ *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
+ *       It will be called when things happen that impact the
+ *       rendering of the content, eg, errors or form submissions. You
+ *       can also intercept URL loading here (via {@link
+ * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
+ * shouldOverrideUrlLoading()}).</li>
+ *   <li>Modifying the {@link android.webkit.WebSettings}, such as
+ * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
+ * setJavaScriptEnabled()}. </li>
+ *   <li>Injecting Java objects into the WebView using the
+ *       {@link android.webkit.WebView#addJavascriptInterface} method. This
+ *       method allows you to inject Java objects into a page's JavaScript
+ *       context, so that they can be accessed by JavaScript in the page.</li>
+ * </ul>
+ *
+ * <p>Here's a more complicated example, showing error handling,
+ *    settings, and progress notification:</p>
+ *
+ * <pre class="prettyprint">
+ * // Let's display the progress in the activity title bar, like the
+ * // browser app does.
+ * getWindow().requestFeature(Window.FEATURE_PROGRESS);
+ *
+ * webview.getSettings().setJavaScriptEnabled(true);
+ *
+ * final Activity activity = this;
+ * webview.setWebChromeClient(new WebChromeClient() {
+ *   public void onProgressChanged(WebView view, int progress) {
+ *     // Activities and WebViews measure progress with different scales.
+ *     // The progress meter will automatically disappear when we reach 100%
+ *     activity.setProgress(progress * 1000);
+ *   }
+ * });
+ * webview.setWebViewClient(new WebViewClient() {
+ *   public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ *     Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
+ *   }
+ * });
+ *
+ * webview.loadUrl("http://slashdot.org/");
+ * </pre>
+ *
+ * <h3>Cookie and window management</h3>
+ *
+ * <p>For obvious security reasons, your application has its own
+ * cache, cookie store etc.&mdash;it does not share the Browser
+ * application's data. Cookies are managed on a separate thread, so
+ * operations like index building don't block the UI
+ * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
+ * if you want to use cookies in your application.
+ * </p>
+ *
+ * <p>By default, requests by the HTML to open new windows are
+ * ignored. This is true whether they be opened by JavaScript or by
+ * the target attribute on a link. You can customize your
+ * {@link WebChromeClient} to provide your own behaviour for opening multiple windows,
+ * and render them in whatever manner you want.</p>
+ *
+ * <p>The standard behavior for an Activity is to be destroyed and
+ * recreated when the device orientation or any other configuration changes. This will cause
+ * the WebView to reload the current page. If you don't want that, you
+ * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
+ * changes, and then just leave the WebView alone. It'll automatically
+ * re-orient itself as appropriate. Read <a
+ * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
+ * more information about how to handle configuration changes during runtime.</p>
+ *
+ *
+ * <h3>Building web pages to support different screen densities</h3>
+ *
+ * <p>The screen density of a device is based on the screen resolution. A screen with low density
+ * has fewer available pixels per inch, where a screen with high density
+ * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
+ * screen is important because, other things being equal, a UI element (such as a button) whose
+ * height and width are defined in terms of screen pixels will appear larger on the lower density
+ * screen and smaller on the higher density screen.
+ * For simplicity, Android collapses all actual screen densities into three generalized densities:
+ * high, medium, and low.</p>
+ * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
+ * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
+ * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
+ * are bigger).
+ * Starting with API Level 5 (Android 2.0), WebView supports DOM, CSS, and meta tag features to help
+ * you (as a web developer) target screens with different screen densities.</p>
+ * <p>Here's a summary of the features you can use to handle different screen densities:</p>
+ * <ul>
+ * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
+ * default scaling factor used for the current device. For example, if the value of {@code
+ * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
+ * and default scaling is not applied to the web page; if the value is "1.5", then the device is
+ * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
+ * value is "0.75", then the device is considered a low density device (ldpi) and the content is
+ * scaled 0.75x. However, if you specify the {@code "target-densitydpi"} meta property
+ * (discussed below), then you can stop this default scaling behavior.</li>
+ * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
+ * densities for which this style sheet is to be used. The corresponding value should be either
+ * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
+ * density, or high density screens, respectively. For example:
+ * <pre>
+ * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
+ * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5,
+ * which is the high density pixel ratio.</p>
+ * </li>
+ * <li>The {@code target-densitydpi} property for the {@code viewport} meta tag. You can use
+ * this to specify the target density for which the web page is designed, using the following
+ * values:
+ * <ul>
+ * <li>{@code device-dpi} - Use the device's native dpi as the target dpi. Default scaling never
+ * occurs.</li>
+ * <li>{@code high-dpi} - Use hdpi as the target dpi. Medium and low density screens scale down
+ * as appropriate.</li>
+ * <li>{@code medium-dpi} - Use mdpi as the target dpi. High density screens scale up and
+ * low density screens scale down. This is also the default behavior.</li>
+ * <li>{@code low-dpi} - Use ldpi as the target dpi. Medium and high density screens scale up
+ * as appropriate.</li>
+ * <li><em>{@code <value>}</em> - Specify a dpi value to use as the target dpi (accepted
+ * values are 70-400).</li>
+ * </ul>
+ * <p>Here's an example meta tag to specify the target density:</p>
+ * <pre>&lt;meta name="viewport" content="target-densitydpi=device-dpi" /&gt;</pre></li>
+ * </ul>
+ * <p>If you want to modify your web page for different densities, by using the {@code
+ * -webkit-device-pixel-ratio} CSS media query and/or the {@code
+ * window.devicePixelRatio} DOM property, then you should set the {@code target-densitydpi} meta
+ * property to {@code device-dpi}. This stops Android from performing scaling in your web page and
+ * allows you to make the necessary adjustments for each density via CSS and JavaScript.</p>
+ *
+ * <h3>HTML5 Video support</h3>
+ *
+ * <p>In order to support inline HTML5 video in your application, you need to have hardware
+ * acceleration turned on, and set a {@link android.webkit.WebChromeClient}. For full screen support,
+ * implementations of {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
+ * and {@link WebChromeClient#onHideCustomView()} are required,
+ * {@link WebChromeClient#getVideoLoadingProgressView()} is optional.
+ * </p>
+ *
+ * @hide
+ */
+// TODO: Remove duplicated API documentation and @hide from fields and methods, and
+// checkThread() call. (All left in for now to ease branch merging.)
+// TODO: Check if any WebView published API methods are called from within here, and if so
+// we should bounce the call out via the proxy to enable any sub-class to override it.
+@Widget
+public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
+        WebViewProvider.ViewDelegate {
+    private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
+        @Override
+        public void onGlobalLayout() {
+            if (mWebView.isShown()) {
+                setGLRectViewport();
+            }
+        }
+    }
+
+    private class InnerScrollChangedListener implements ViewTreeObserver.OnScrollChangedListener {
+        @Override
+        public void onScrollChanged() {
+            if (mWebView.isShown()) {
+                setGLRectViewport();
+            }
+        }
+    }
+
+    /**
+     * InputConnection used for ContentEditable. This captures changes
+     * to the text and sends them either as key strokes or text changes.
+     */
+    private class WebViewInputConnection extends BaseInputConnection {
+        // Used for mapping characters to keys typed.
+        private KeyCharacterMap mKeyCharacterMap;
+        private boolean mIsKeySentByMe;
+        private int mInputType;
+        private int mImeOptions;
+        private String mHint;
+        private int mMaxLength;
+
+        public WebViewInputConnection() {
+            super(mWebView, true);
+        }
+
+        @Override
+        public boolean sendKeyEvent(KeyEvent event) {
+            // Some IMEs send key events directly using sendKeyEvents.
+            // WebViewInputConnection should treat these as text changes.
+            if (!mIsKeySentByMe) {
+                if (event.getAction() == KeyEvent.ACTION_UP) {
+                    if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
+                        return deleteSurroundingText(1, 0);
+                    } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
+                        return deleteSurroundingText(0, 1);
+                    } else if (event.getUnicodeChar() != 0){
+                        String newComposingText =
+                                Character.toString((char)event.getUnicodeChar());
+                        return commitText(newComposingText, 1);
+                    }
+                } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
+                        (event.getKeyCode() == KeyEvent.KEYCODE_DEL
+                        || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
+                        || event.getUnicodeChar() != 0)) {
+                    return true; // only act on action_down
+                }
+            }
+            return super.sendKeyEvent(event);
+        }
+
+        public void setTextAndKeepSelection(CharSequence text) {
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            text = limitReplaceTextByMaxLength(text, editable.length());
+            editable.replace(0, editable.length(), text);
+            restartInput();
+            // Keep the previous selection.
+            selectionStart = Math.min(selectionStart, editable.length());
+            selectionEnd = Math.min(selectionEnd, editable.length());
+            setSelection(selectionStart, selectionEnd);
+        }
+
+        public void replaceSelection(CharSequence text) {
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
+            setNewText(selectionStart, selectionEnd, text);
+            editable.replace(selectionStart, selectionEnd, text);
+            restartInput();
+            // Move caret to the end of the new text
+            int newCaret = selectionStart + text.length();
+            setSelection(newCaret, newCaret);
+        }
+
+        @Override
+        public boolean setComposingText(CharSequence text, int newCursorPosition) {
+            Editable editable = getEditable();
+            int start = getComposingSpanStart(editable);
+            int end = getComposingSpanEnd(editable);
+            if (start < 0 || end < 0) {
+                start = Selection.getSelectionStart(editable);
+                end = Selection.getSelectionEnd(editable);
+            }
+            if (end < start) {
+                int temp = end;
+                end = start;
+                start = temp;
+            }
+            CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
+            setNewText(start, end, limitedText);
+            if (limitedText != text) {
+                newCursorPosition -= text.length() - limitedText.length();
+            }
+            super.setComposingText(limitedText, newCursorPosition);
+            if (limitedText != text) {
+                restartInput();
+                int lastCaret = start + limitedText.length();
+                finishComposingText();
+                setSelection(lastCaret, lastCaret);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean commitText(CharSequence text, int newCursorPosition) {
+            setComposingText(text, newCursorPosition);
+            int cursorPosition = Selection.getSelectionEnd(getEditable());
+            setComposingRegion(cursorPosition, cursorPosition);
+            return true;
+        }
+
+        @Override
+        public boolean deleteSurroundingText(int leftLength, int rightLength) {
+            Editable editable = getEditable();
+            int cursorPosition = Selection.getSelectionEnd(editable);
+            int startDelete = Math.max(0, cursorPosition - leftLength);
+            int endDelete = Math.min(editable.length(),
+                    cursorPosition + rightLength);
+            setNewText(startDelete, endDelete, "");
+            return super.deleteSurroundingText(leftLength, rightLength);
+        }
+
+        @Override
+        public boolean performEditorAction(int editorAction) {
+
+            boolean handled = true;
+            switch (editorAction) {
+            case EditorInfo.IME_ACTION_NEXT:
+                mWebView.requestFocus(View.FOCUS_FORWARD);
+                break;
+            case EditorInfo.IME_ACTION_PREVIOUS:
+                mWebView.requestFocus(View.FOCUS_BACKWARD);
+                break;
+            case EditorInfo.IME_ACTION_DONE:
+                WebViewClassic.this.hideSoftKeyboard();
+                break;
+            case EditorInfo.IME_ACTION_GO:
+            case EditorInfo.IME_ACTION_SEARCH:
+                WebViewClassic.this.hideSoftKeyboard();
+                String text = getEditable().toString();
+                passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
+                        KeyEvent.KEYCODE_ENTER));
+                passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP,
+                        KeyEvent.KEYCODE_ENTER));
+                break;
+
+            default:
+                handled = super.performEditorAction(editorAction);
+                break;
+            }
+
+            return handled;
+        }
+
+        public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
+            int type = initData.mType;
+            int inputType = InputType.TYPE_CLASS_TEXT
+                    | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
+            int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+                    | EditorInfo.IME_FLAG_NO_FULLSCREEN;
+            if (!initData.mIsSpellCheckEnabled) {
+                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+            }
+            if (WebTextView.TEXT_AREA != type
+                    && initData.mIsTextFieldNext) {
+                imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
+            }
+            switch (type) {
+                case WebTextView.NORMAL_TEXT_FIELD:
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.TEXT_AREA:
+                    inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
+                            | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+                            | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
+                    imeOptions |= EditorInfo.IME_ACTION_NONE;
+                    break;
+                case WebTextView.PASSWORD:
+                    inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.SEARCH:
+                    imeOptions |= EditorInfo.IME_ACTION_SEARCH;
+                    break;
+                case WebTextView.EMAIL:
+                    // inputType needs to be overwritten because of the different text variation.
+                    inputType = InputType.TYPE_CLASS_TEXT
+                            | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.NUMBER:
+                    // inputType needs to be overwritten because of the different class.
+                    inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
+                            | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
+                    // Number and telephone do not have both a Tab key and an
+                    // action, so set the action to NEXT
+                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
+                    break;
+                case WebTextView.TELEPHONE:
+                    // inputType needs to be overwritten because of the different class.
+                    inputType = InputType.TYPE_CLASS_PHONE;
+                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
+                    break;
+                case WebTextView.URL:
+                    // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
+                    // exclude it for now.
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    inputType |= InputType.TYPE_TEXT_VARIATION_URI;
+                    break;
+                default:
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+            }
+            mHint = initData.mLabel;
+            mInputType = inputType;
+            mImeOptions = imeOptions;
+            mMaxLength = initData.mMaxLength;
+        }
+
+        public void setupEditorInfo(EditorInfo outAttrs) {
+            outAttrs.inputType = mInputType;
+            outAttrs.imeOptions = mImeOptions;
+            outAttrs.hintText = mHint;
+            outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
+        }
+
+        /**
+         * Sends a text change to webkit indirectly. If it is a single-
+         * character add or delete, it sends it as a key stroke. If it cannot
+         * be represented as a key stroke, it sends it as a field change.
+         * @param start The start offset (inclusive) of the text being changed.
+         * @param end The end offset (exclusive) of the text being changed.
+         * @param text The new text to replace the changed text.
+         */
+        private void setNewText(int start, int end, CharSequence text) {
+            mIsKeySentByMe = true;
+            Editable editable = getEditable();
+            CharSequence original = editable.subSequence(start, end);
+            boolean isCharacterAdd = false;
+            boolean isCharacterDelete = false;
+            int textLength = text.length();
+            int originalLength = original.length();
+            if (textLength > originalLength) {
+                isCharacterAdd = (textLength == originalLength + 1)
+                        && TextUtils.regionMatches(text, 0, original, 0,
+                                originalLength);
+            } else if (originalLength > textLength) {
+                isCharacterDelete = (textLength == originalLength - 1)
+                        && TextUtils.regionMatches(text, 0, original, 0,
+                                textLength);
+            }
+            if (isCharacterAdd) {
+                sendCharacter(text.charAt(textLength - 1));
+            } else if (isCharacterDelete) {
+                sendKey(KeyEvent.KEYCODE_DEL);
+            } else if ((textLength != originalLength) ||
+                    !TextUtils.regionMatches(text, 0, original, 0,
+                            textLength)) {
+                // Send a message so that key strokes and text replacement
+                // do not come out of order.
+                Message replaceMessage = mPrivateHandler.obtainMessage(
+                        REPLACE_TEXT, start,  end, text.toString());
+                mPrivateHandler.sendMessage(replaceMessage);
+            }
+            mIsKeySentByMe = false;
+        }
+
+        /**
+         * Send a single character to the WebView as a key down and up event.
+         * @param c The character to be sent.
+         */
+        private void sendCharacter(char c) {
+            if (mKeyCharacterMap == null) {
+                mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+            }
+            char[] chars = new char[1];
+            chars[0] = c;
+            KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
+            if (events != null) {
+                for (KeyEvent event : events) {
+                    sendKeyEvent(event);
+                }
+            } else {
+                Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
+                mPrivateHandler.sendMessage(msg);
+            }
+        }
+
+        /**
+         * Send a key event for a specific key code, not a standard
+         * unicode character.
+         * @param keyCode The key code to send.
+         */
+        private void sendKey(int keyCode) {
+            long eventTime = SystemClock.uptimeMillis();
+            sendKeyEvent(new KeyEvent(eventTime, eventTime,
+                    KeyEvent.ACTION_DOWN, keyCode, 0, 0,
+                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+                    KeyEvent.FLAG_SOFT_KEYBOARD));
+            sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+                    KeyEvent.ACTION_UP, keyCode, 0, 0,
+                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+                    KeyEvent.FLAG_SOFT_KEYBOARD));
+        }
+
+        private CharSequence limitReplaceTextByMaxLength(CharSequence text,
+                int numReplaced) {
+            if (mMaxLength > 0) {
+                Editable editable = getEditable();
+                int maxReplace = mMaxLength - editable.length() + numReplaced;
+                if (maxReplace < text.length()) {
+                    maxReplace = Math.max(maxReplace, 0);
+                    // New length is greater than the maximum. trim it down.
+                    text = text.subSequence(0, maxReplace);
+                }
+            }
+            return text;
+        }
+
+        private void restartInput() {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                // Since the text has changed, do not allow the IME to replace the
+                // existing text as though it were a completion.
+                imm.restartInput(mWebView);
+            }
+        }
+    }
+
+    private class PastePopupWindow extends PopupWindow implements View.OnClickListener {
+        private ViewGroup mContentView;
+        private TextView mPasteTextView;
+
+        public PastePopupWindow() {
+            super(mContext, null,
+                    com.android.internal.R.attr.textSelectHandleWindowStyle);
+            setClippingEnabled(true);
+            LinearLayout linearLayout = new LinearLayout(mContext);
+            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView = linearLayout;
+            mContentView.setBackgroundResource(
+                    com.android.internal.R.drawable.text_edit_paste_window);
+
+            LayoutInflater inflater = (LayoutInflater)mContext.
+                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+            ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            mPasteTextView = (TextView) inflater.inflate(
+                    com.android.internal.R.layout.text_edit_action_popup_text, null);
+            mPasteTextView.setLayoutParams(wrapContent);
+            mContentView.addView(mPasteTextView);
+            mPasteTextView.setText(com.android.internal.R.string.paste);
+            mPasteTextView.setOnClickListener(this);
+            this.setContentView(mContentView);
+        }
+
+        public void show(Rect cursorRect, int windowLeft, int windowTop) {
+            measureContent();
+
+            int width = mContentView.getMeasuredWidth();
+            int height = mContentView.getMeasuredHeight();
+            int y = cursorRect.top - height;
+            if (y < windowTop) {
+                // There's not enough room vertically, move it below the
+                // handle.
+                // The selection handle is vertically offset by 1/4 of the
+                // line height.
+                ensureSelectionHandles();
+                y = cursorRect.bottom - (cursorRect.height() / 4) +
+                        mSelectHandleCenter.getIntrinsicHeight();
+            }
+            int x = cursorRect.centerX() - (width / 2);
+            if (x < windowLeft) {
+                x = windowLeft;
+            }
+            if (!isShowing()) {
+                showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y);
+            }
+            update(x, y, width, height);
+        }
+
+        public void hide() {
+            dismiss();
+        }
+
+        @Override
+        public void onClick(View view) {
+            pasteFromClipboard();
+            selectionDone();
+        }
+
+        protected void measureContent() {
+            final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+            mContentView.measure(
+                    View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
+                            View.MeasureSpec.AT_MOST),
+                    View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
+                            View.MeasureSpec.AT_MOST));
+        }
+    }
+
+    // The listener to capture global layout change event.
+    private InnerGlobalLayoutListener mGlobalLayoutListener = null;
+
+    // The listener to capture scroll event.
+    private InnerScrollChangedListener mScrollChangedListener = null;
+
+    // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
+    // the screen all-the-time. Good for profiling our drawing code
+    static private final boolean AUTO_REDRAW_HACK = false;
+    // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
+    private boolean mAutoRedraw;
+
+    // Reference to the AlertDialog displayed by InvokeListBox.
+    // It's used to dismiss the dialog in destroy if not done before.
+    private AlertDialog mListBoxDialog = null;
+
+    static final String LOGTAG = "webview";
+
+    private ZoomManager mZoomManager;
+
+    private final Rect mGLRectViewport = new Rect();
+    private final Rect mViewRectViewport = new Rect();
+    private final RectF mVisibleContentRect = new RectF();
+    private boolean mGLViewportEmpty = false;
+    WebViewInputConnection mInputConnection = null;
+    private int mFieldPointer;
+    private PastePopupWindow mPasteWindow;
+
+    private static class OnTrimMemoryListener implements ComponentCallbacks2 {
+        private static OnTrimMemoryListener sInstance = null;
+
+        static void init(Context c) {
+            if (sInstance == null) {
+                sInstance = new OnTrimMemoryListener(c.getApplicationContext());
+            }
+        }
+
+        private OnTrimMemoryListener(Context c) {
+            c.registerComponentCallbacks(this);
+        }
+
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            // Ignore
+        }
+
+        @Override
+        public void onLowMemory() {
+            // Ignore
+        }
+
+        @Override
+        public void onTrimMemory(int level) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.d("WebView", "onTrimMemory: " + level);
+            }
+            // When framework reset EGL context during high memory pressure, all
+            // the existing GL resources for the html5 video will be destroyed
+            // at native side.
+            // Here we just need to clean up the Surface Texture which is static.
+            HTML5VideoInline.cleanupSurfaceTexture();
+            WebViewClassic.nativeOnTrimMemory(level);
+        }
+
+    }
+
+    // A final CallbackProxy shared by WebViewCore and BrowserFrame.
+    private CallbackProxy mCallbackProxy;
+
+    private WebViewDatabase mDatabase;
+
+    // SSL certificate for the main top-level page (if secure)
+    private SslCertificate mCertificate;
+
+    // Native WebView pointer that is 0 until the native object has been
+    // created.
+    private int mNativeClass;
+    // This would be final but it needs to be set to null when the WebView is
+    // destroyed.
+    private WebViewCore mWebViewCore;
+    // Handler for dispatching UI messages.
+    /* package */ final Handler mPrivateHandler = new PrivateHandler();
+    private WebTextView mWebTextView;
+    // Used to ignore changes to webkit text that arrives to the UI side after
+    // more key events.
+    private int mTextGeneration;
+
+    /* package */ void incrementTextGeneration() { mTextGeneration++; }
+
+    // Used by WebViewCore to create child views.
+    /* package */ ViewManager mViewManager;
+
+    // Used to display in full screen mode
+    PluginFullScreenHolder mFullScreenHolder;
+
+    /**
+     * Position of the last touch event in pixels.
+     * Use integer to prevent loss of dragging delta calculation accuracy;
+     * which was done in float and converted to integer, and resulted in gradual
+     * and compounding touch position and view dragging mismatch.
+     */
+    private int mLastTouchX;
+    private int mLastTouchY;
+    private int mStartTouchX;
+    private int mStartTouchY;
+    private float mAverageAngle;
+
+    /**
+     * Time of the last touch event.
+     */
+    private long mLastTouchTime;
+
+    /**
+     * Time of the last time sending touch event to WebViewCore
+     */
+    private long mLastSentTouchTime;
+
+    /**
+     * The minimum elapsed time before sending another ACTION_MOVE event to
+     * WebViewCore. This really should be tuned for each type of the devices.
+     * For example in Google Map api test case, it takes Dream device at least
+     * 150ms to do a full cycle in the WebViewCore by processing a touch event,
+     * triggering the layout and drawing the picture. While the same process
+     * takes 60+ms on the current high speed device. If we make
+     * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
+     * to WebViewCore queue and the real layout and draw events will be pushed
+     * to further, which slows down the refresh rate. Choose 50 to favor the
+     * current high speed devices. For Dream like devices, 100 is a better
+     * choice. Maybe make this in the buildspec later.
+     * (Update 12/14/2010: changed to 0 since current device should be able to
+     * handle the raw events and Map team voted to have the raw events too.
+     */
+    private static final int TOUCH_SENT_INTERVAL = 0;
+    private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
+
+    /**
+     * Helper class to get velocity for fling
+     */
+    VelocityTracker mVelocityTracker;
+    private int mMaximumFling;
+    private float mLastVelocity;
+    private float mLastVelX;
+    private float mLastVelY;
+
+    // The id of the native layer being scrolled.
+    private int mCurrentScrollingLayerId;
+    private Rect mScrollingLayerRect = new Rect();
+
+    // only trigger accelerated fling if the new velocity is at least
+    // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
+    private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
+
+    /**
+     * Touch mode
+     */
+    private int mTouchMode = TOUCH_DONE_MODE;
+    private static final int TOUCH_INIT_MODE = 1;
+    private static final int TOUCH_DRAG_START_MODE = 2;
+    private static final int TOUCH_DRAG_MODE = 3;
+    private static final int TOUCH_SHORTPRESS_START_MODE = 4;
+    private static final int TOUCH_SHORTPRESS_MODE = 5;
+    private static final int TOUCH_DOUBLE_TAP_MODE = 6;
+    private static final int TOUCH_DONE_MODE = 7;
+    private static final int TOUCH_PINCH_DRAG = 8;
+    private static final int TOUCH_DRAG_LAYER_MODE = 9;
+
+    // Whether to forward the touch events to WebCore
+    // Can only be set by WebKit via JNI.
+    private boolean mForwardTouchEvents = false;
+
+    // Whether to prevent default during touch. The initial value depends on
+    // mForwardTouchEvents. If WebCore wants all the touch events, it says yes
+    // for touch down. Otherwise UI will wait for the answer of the first
+    // confirmed move before taking over the control.
+    private static final int PREVENT_DEFAULT_NO = 0;
+    private static final int PREVENT_DEFAULT_MAYBE_YES = 1;
+    private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2;
+    private static final int PREVENT_DEFAULT_YES = 3;
+    private static final int PREVENT_DEFAULT_IGNORE = 4;
+    private int mPreventDefault = PREVENT_DEFAULT_IGNORE;
+
+    // true when the touch movement exceeds the slop
+    private boolean mConfirmMove;
+
+    // if true, touch events will be first processed by WebCore, if prevent
+    // default is not set, the UI will continue handle them.
+    private boolean mDeferTouchProcess;
+
+    // to avoid interfering with the current touch events, track them
+    // separately. Currently no snapping or fling in the deferred process mode
+    private int mDeferTouchMode = TOUCH_DONE_MODE;
+    private float mLastDeferTouchX;
+    private float mLastDeferTouchY;
+
+    // To keep track of whether the current drag was initiated by a WebTextView,
+    // so that we know not to hide the cursor
+    boolean mDragFromTextInput;
+
+    // Whether or not to draw the cursor ring.
+    private boolean mDrawCursorRing = true;
+
+    // true if onPause has been called (and not onResume)
+    private boolean mIsPaused;
+
+    private HitTestResult mInitialHitTestResult;
+    private WebKitHitTest mFocusedNode;
+
+    /**
+     * Customizable constant
+     */
+    // pre-computed square of ViewConfiguration.getScaledTouchSlop()
+    private int mTouchSlopSquare;
+    // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
+    private int mDoubleTapSlopSquare;
+    // pre-computed density adjusted navigation slop
+    private int mNavSlop;
+    // This should be ViewConfiguration.getTapTimeout()
+    // But system time out is 100ms, which is too short for the browser.
+    // In the browser, if it switches out of tap too soon, jump tap won't work.
+    // In addition, a double tap on a trackpad will always have a duration of
+    // 300ms, so this value must be at least that (otherwise we will timeout the
+    // first tap and convert it to a long press).
+    private static final int TAP_TIMEOUT = 300;
+    // This should be ViewConfiguration.getLongPressTimeout()
+    // But system time out is 500ms, which is too short for the browser.
+    // With a short timeout, it's difficult to treat trigger a short press.
+    private static final int LONG_PRESS_TIMEOUT = 1000;
+    // needed to avoid flinging after a pause of no movement
+    private static final int MIN_FLING_TIME = 250;
+    // draw unfiltered after drag is held without movement
+    private static final int MOTIONLESS_TIME = 100;
+    // The amount of content to overlap between two screens when going through
+    // pages with the space bar, in pixels.
+    private static final int PAGE_SCROLL_OVERLAP = 24;
+
+    /**
+     * These prevent calling requestLayout if either dimension is fixed. This
+     * depends on the layout parameters and the measure specs.
+     */
+    boolean mWidthCanMeasure;
+    boolean mHeightCanMeasure;
+
+    // Remember the last dimensions we sent to the native side so we can avoid
+    // sending the same dimensions more than once.
+    int mLastWidthSent;
+    int mLastHeightSent;
+    // Since view height sent to webkit could be fixed to avoid relayout, this
+    // value records the last sent actual view height.
+    int mLastActualHeightSent;
+
+    private int mContentWidth;   // cache of value from WebViewCore
+    private int mContentHeight;  // cache of value from WebViewCore
+
+    // Need to have the separate control for horizontal and vertical scrollbar
+    // style than the View's single scrollbar style
+    private boolean mOverlayHorizontalScrollbar = true;
+    private boolean mOverlayVerticalScrollbar = false;
+
+    // our standard speed. this way small distances will be traversed in less
+    // time than large distances, but we cap the duration, so that very large
+    // distances won't take too long to get there.
+    private static final int STD_SPEED = 480;  // pixels per second
+    // time for the longest scroll animation
+    private static final int MAX_DURATION = 750;   // milliseconds
+    private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
+
+    // Used by OverScrollGlow
+    OverScroller mScroller;
+
+    private boolean mInOverScrollMode = false;
+    private static Paint mOverScrollBackground;
+    private static Paint mOverScrollBorder;
+
+    private boolean mWrapContent;
+    private static final int MOTIONLESS_FALSE           = 0;
+    private static final int MOTIONLESS_PENDING         = 1;
+    private static final int MOTIONLESS_TRUE            = 2;
+    private static final int MOTIONLESS_IGNORE          = 3;
+    private int mHeldMotionless;
+
+    // An instance for injecting accessibility in WebViews with disabled
+    // JavaScript or ones for which no accessibility script exists
+    private AccessibilityInjector mAccessibilityInjector;
+
+    // flag indicating if accessibility script is injected so we
+    // know to handle Shift and arrows natively first
+    private boolean mAccessibilityScriptInjected;
+
+
+    /**
+     * How long the caret handle will last without being touched.
+     */
+    private static final long CARET_HANDLE_STAMINA_MS = 3000;
+
+    private Drawable mSelectHandleLeft;
+    private Drawable mSelectHandleRight;
+    private Drawable mSelectHandleCenter;
+    private Rect mSelectCursorBase = new Rect();
+    private int mSelectCursorBaseLayerId;
+    private Rect mSelectCursorExtent = new Rect();
+    private int mSelectCursorExtentLayerId;
+    private Rect mSelectDraggingCursor;
+    private Point mSelectDraggingOffset = new Point();
+    private boolean mIsCaretSelection;
+    static final int HANDLE_ID_START = 0;
+    static final int HANDLE_ID_END = 1;
+    static final int HANDLE_ID_BASE = 2;
+    static final int HANDLE_ID_EXTENT = 3;
+
+    static boolean sDisableNavcache = false;
+    static boolean sEnableWebTextView = false;
+    // the color used to highlight the touch rectangles
+    static final int HIGHLIGHT_COLOR = 0x6633b5e5;
+    // the region indicating where the user touched on the screen
+    private Region mTouchHighlightRegion = new Region();
+    // the paint for the touch highlight
+    private Paint mTouchHightlightPaint = new Paint();
+    // debug only
+    private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
+    private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
+    private Paint mTouchCrossHairColor;
+    private int mTouchHighlightX;
+    private int mTouchHighlightY;
+    private long mTouchHighlightRequested;
+
+    // Basically this proxy is used to tell the Video to update layer tree at
+    // SetBaseLayer time and to pause when WebView paused.
+    private HTML5VideoViewProxy mHTML5VideoViewProxy;
+
+    // If we are using a set picture, don't send view updates to webkit
+    private boolean mBlockWebkitViewMessages = false;
+
+    // cached value used to determine if we need to switch drawing models
+    private boolean mHardwareAccelSkia = false;
+
+    /*
+     * Private message ids
+     */
+    private static final int REMEMBER_PASSWORD          = 1;
+    private static final int NEVER_REMEMBER_PASSWORD    = 2;
+    private static final int SWITCH_TO_SHORTPRESS       = 3;
+    private static final int SWITCH_TO_LONGPRESS        = 4;
+    private static final int RELEASE_SINGLE_TAP         = 5;
+    private static final int REQUEST_FORM_DATA          = 6;
+    private static final int DRAG_HELD_MOTIONLESS       = 8;
+    private static final int AWAKEN_SCROLL_BARS         = 9;
+    private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
+    private static final int SCROLL_SELECT_TEXT         = 11;
+
+
+    private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
+    private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
+
+    /*
+     * Package message ids
+     */
+    static final int SCROLL_TO_MSG_ID                   = 101;
+    static final int NEW_PICTURE_MSG_ID                 = 105;
+    static final int UPDATE_TEXT_ENTRY_MSG_ID           = 106;
+    static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
+    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
+    static final int UPDATE_ZOOM_RANGE                  = 109;
+    static final int UNHANDLED_NAV_KEY                  = 110;
+    static final int CLEAR_TEXT_ENTRY                   = 111;
+    static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
+    static final int SHOW_RECT_MSG_ID                   = 113;
+    static final int LONG_PRESS_CENTER                  = 114;
+    static final int PREVENT_TOUCH_ID                   = 115;
+    static final int WEBCORE_NEED_TOUCH_EVENTS          = 116;
+    // obj=Rect in doc coordinates
+    static final int INVAL_RECT_MSG_ID                  = 117;
+    static final int REQUEST_KEYBOARD                   = 118;
+    static final int DO_MOTION_UP                       = 119;
+    static final int SHOW_FULLSCREEN                    = 120;
+    static final int HIDE_FULLSCREEN                    = 121;
+    static final int DOM_FOCUS_CHANGED                  = 122;
+    static final int REPLACE_BASE_CONTENT               = 123;
+    static final int FORM_DID_BLUR                      = 124;
+    static final int RETURN_LABEL                       = 125;
+    static final int UPDATE_MATCH_COUNT                 = 126;
+    static final int CENTER_FIT_RECT                    = 127;
+    static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
+    static final int SET_SCROLLBAR_MODES                = 129;
+    static final int SELECTION_STRING_CHANGED           = 130;
+    static final int HIT_TEST_RESULT                    = 131;
+    static final int SAVE_WEBARCHIVE_FINISHED           = 132;
+
+    static final int SET_AUTOFILLABLE                   = 133;
+    static final int AUTOFILL_COMPLETE                  = 134;
+
+    static final int SELECT_AT                          = 135;
+    static final int SCREEN_ON                          = 136;
+    static final int ENTER_FULLSCREEN_VIDEO             = 137;
+    static final int UPDATE_SELECTION                   = 138;
+    static final int UPDATE_ZOOM_DENSITY                = 139;
+    static final int EXIT_FULLSCREEN_VIDEO              = 140;
+
+    static final int COPY_TO_CLIPBOARD                  = 141;
+    static final int INIT_EDIT_FIELD                    = 142;
+    static final int REPLACE_TEXT                       = 143;
+    static final int CLEAR_CARET_HANDLE                 = 144;
+    static final int KEY_PRESS                          = 145;
+
+    private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
+    private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
+
+    static final String[] HandlerPrivateDebugString = {
+        "REMEMBER_PASSWORD", //              = 1;
+        "NEVER_REMEMBER_PASSWORD", //        = 2;
+        "SWITCH_TO_SHORTPRESS", //           = 3;
+        "SWITCH_TO_LONGPRESS", //            = 4;
+        "RELEASE_SINGLE_TAP", //             = 5;
+        "REQUEST_FORM_DATA", //              = 6;
+        "RESUME_WEBCORE_PRIORITY", //        = 7;
+        "DRAG_HELD_MOTIONLESS", //           = 8;
+        "AWAKEN_SCROLL_BARS", //             = 9;
+        "PREVENT_DEFAULT_TIMEOUT", //        = 10;
+        "SCROLL_SELECT_TEXT" //              = 11;
+    };
+
+    static final String[] HandlerPackageDebugString = {
+        "SCROLL_TO_MSG_ID", //               = 101;
+        "102", //                            = 102;
+        "103", //                            = 103;
+        "104", //                            = 104;
+        "NEW_PICTURE_MSG_ID", //             = 105;
+        "UPDATE_TEXT_ENTRY_MSG_ID", //       = 106;
+        "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
+        "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
+        "UPDATE_ZOOM_RANGE", //              = 109;
+        "UNHANDLED_NAV_KEY", //              = 110;
+        "CLEAR_TEXT_ENTRY", //               = 111;
+        "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
+        "SHOW_RECT_MSG_ID", //               = 113;
+        "LONG_PRESS_CENTER", //              = 114;
+        "PREVENT_TOUCH_ID", //               = 115;
+        "WEBCORE_NEED_TOUCH_EVENTS", //      = 116;
+        "INVAL_RECT_MSG_ID", //              = 117;
+        "REQUEST_KEYBOARD", //               = 118;
+        "DO_MOTION_UP", //                   = 119;
+        "SHOW_FULLSCREEN", //                = 120;
+        "HIDE_FULLSCREEN", //                = 121;
+        "DOM_FOCUS_CHANGED", //              = 122;
+        "REPLACE_BASE_CONTENT", //           = 123;
+        "FORM_DID_BLUR", //                  = 124;
+        "RETURN_LABEL", //                   = 125;
+        "UPDATE_MATCH_COUNT", //             = 126;
+        "CENTER_FIT_RECT", //                = 127;
+        "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
+        "SET_SCROLLBAR_MODES", //            = 129;
+        "SELECTION_STRING_CHANGED", //       = 130;
+        "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
+        "SAVE_WEBARCHIVE_FINISHED", //       = 132;
+        "SET_AUTOFILLABLE", //               = 133;
+        "AUTOFILL_COMPLETE", //              = 134;
+        "SELECT_AT", //                      = 135;
+        "SCREEN_ON", //                      = 136;
+        "ENTER_FULLSCREEN_VIDEO", //         = 137;
+        "UPDATE_SELECTION", //               = 138;
+        "UPDATE_ZOOM_DENSITY" //             = 139;
+    };
+
+    // If the site doesn't use the viewport meta tag to specify the viewport,
+    // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
+    static final int DEFAULT_VIEWPORT_WIDTH = 980;
+
+    // normally we try to fit the content to the minimum preferred width
+    // calculated by the Webkit. To avoid the bad behavior when some site's
+    // minimum preferred width keeps growing when changing the viewport width or
+    // the minimum preferred width is huge, an upper limit is needed.
+    static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
+
+    // initial scale in percent. 0 means using default.
+    private int mInitialScaleInPercent = 0;
+
+    // Whether or not a scroll event should be sent to webkit.  This is only set
+    // to false when restoring the scroll position.
+    private boolean mSendScrollEvent = true;
+
+    private int mSnapScrollMode = SNAP_NONE;
+    private static final int SNAP_NONE = 0;
+    private static final int SNAP_LOCK = 1; // not a separate state
+    private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
+    private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
+    private boolean mSnapPositive;
+
+    // keep these in sync with their counterparts in WebView.cpp
+    private static final int DRAW_EXTRAS_NONE = 0;
+    private static final int DRAW_EXTRAS_SELECTION = 1;
+    private static final int DRAW_EXTRAS_CURSOR_RING = 2;
+
+    // keep this in sync with WebCore:ScrollbarMode in WebKit
+    private static final int SCROLLBAR_AUTO = 0;
+    private static final int SCROLLBAR_ALWAYSOFF = 1;
+    // as we auto fade scrollbar, this is ignored.
+    private static final int SCROLLBAR_ALWAYSON = 2;
+    private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
+    private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
+
+    // constants for determining script injection strategy
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
+
+    // the alias via which accessibility JavaScript interface is exposed
+    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
+
+    // Template for JavaScript that injects a screen-reader.
+    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
+        "javascript:(function() {" +
+        "    var chooser = document.createElement('script');" +
+        "    chooser.type = 'text/javascript';" +
+        "    chooser.src = '%1s';" +
+        "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
+        "  })();";
+
+    // Regular expression that matches the "axs" URL parameter.
+    // The value of 0 means the accessibility script is opted out
+    // The value of 1 means the accessibility script is already injected
+    private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
+
+    // TextToSpeech instance exposed to JavaScript to the injected screenreader.
+    private TextToSpeech mTextToSpeech;
+
+    // variable to cache the above pattern in case accessibility is enabled.
+    private Pattern mMatchAxsUrlParameterPattern;
+
+    /**
+     * Max distance to overscroll by in pixels.
+     * This how far content can be pulled beyond its normal bounds by the user.
+     */
+    private int mOverscrollDistance;
+
+    /**
+     * Max distance to overfling by in pixels.
+     * This is how far flinged content can move beyond the end of its normal bounds.
+     */
+    private int mOverflingDistance;
+
+    private OverScrollGlow mOverScrollGlow;
+
+    // Used to match key downs and key ups
+    private Vector<Integer> mKeysPressed;
+
+    /* package */ static boolean mLogEvent = true;
+
+    // for event log
+    private long mLastTouchUpTime = 0;
+
+    private WebViewCore.AutoFillData mAutoFillData;
+
+    private static boolean sNotificationsEnabled = true;
+
+    /**
+     * URI scheme for telephone number
+     */
+    public static final String SCHEME_TEL = "tel:";
+    /**
+     * URI scheme for email address
+     */
+    public static final String SCHEME_MAILTO = "mailto:";
+    /**
+     * URI scheme for map address
+     */
+    public static final String SCHEME_GEO = "geo:0,0?q=";
+
+    private int mBackgroundColor = Color.WHITE;
+
+    private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
+    private int mAutoScrollX = 0;
+    private int mAutoScrollY = 0;
+    private int mMinAutoScrollX = 0;
+    private int mMaxAutoScrollX = 0;
+    private int mMinAutoScrollY = 0;
+    private int mMaxAutoScrollY = 0;
+    private Rect mScrollingLayerBounds = new Rect();
+    private boolean mSentAutoScrollMessage = false;
+
+    // used for serializing asynchronously handled touch events.
+    private final TouchEventQueue mTouchEventQueue = new TouchEventQueue();
+
+    // Used to track whether picture updating was paused due to a window focus change.
+    private boolean mPictureUpdatePausedForFocusChange = false;
+
+    // Used to notify listeners of a new picture.
+    private PictureListener mPictureListener;
+
+    /**
+     * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
+     */
+    static class FocusNodeHref {
+        static final String TITLE = "title";
+        static final String URL = "url";
+        static final String SRC = "src";
+    }
+
+    public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) {
+        mWebView = webView;
+        mWebViewPrivate = privateAccess;
+        mContext = webView.getContext();
+    }
+
+    /**
+     * Construct a new WebView with layout parameters, a default style and a set
+     * of custom Javscript interfaces to be added to the WebView at initialization
+     * time. This guarantees that these interfaces will be available when the JS
+     * context is initialized.
+     * @param javaScriptInterfaces is a Map of interface names, as keys, and
+     * object implementing those interfaces, as values.
+     * @param privateBrowsing If true the web view will be initialized in private mode.
+     * @hide This is an implementation detail.
+     */
+    @Override
+    public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+        checkThread();
+
+        Context context = mContext;
+
+        // Used by the chrome stack to find application paths
+        JniUtil.setContext(context);
+
+        mCallbackProxy = new CallbackProxy(context, this);
+        mViewManager = new ViewManager(this);
+        L10nUtils.setApplicationContext(context.getApplicationContext());
+        mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
+        mDatabase = WebViewDatabase.getInstance(context);
+        mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
+        mZoomManager = new ZoomManager(this, mCallbackProxy);
+
+        /* The init method must follow the creation of certain member variables,
+         * such as the mZoomManager.
+         */
+        init();
+        setupPackageListener(context);
+        setupProxyListener(context);
+        setupTrustStorageListener(context);
+        updateMultiTouchSupport(context);
+
+        if (privateBrowsing) {
+            startPrivateBrowsing();
+        }
+
+        mAutoFillData = new WebViewCore.AutoFillData();
+    }
+
+    // === START: WebView Proxy binding ===
+    // Keep the webview proxy / SPI related stuff in this section, to minimize merge conflicts.
+
+    static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
+        @Override
+        public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
+            return new WebViewClassic(webView, privateAccess);
+        }
+
+        @Override
+        public Statics getStatics() { return this; }
+
+        @Override
+        public String findAddress(String addr) {
+            return WebViewClassic.findAddress(addr);
+        }
+        @Override
+        public void setPlatformNotificationsEnabled(boolean enable) {
+            if (enable) {
+                WebViewClassic.enablePlatformNotifications();
+            } else {
+                WebViewClassic.disablePlatformNotifications();
+            }
+        }
+
+    }
+
+    // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying
+    // as the first param in the WebViewClient and WebChromeClient callbacks.
+    final private WebView mWebView;
+    // Callback interface, provides priviledged access into the WebView instance.
+    final private WebView.PrivateAccess mWebViewPrivate;
+    // Cached reference to mWebView.getContext(), for convenience.
+    final private Context mContext;
+
+    /**
+     * @return The webview proxy that this classic webview is bound to.
+     */
+    public WebView getWebView() {
+        return mWebView;
+    }
+
+    @Override
+    public ViewDelegate getViewDelegate() {
+        return this;
+    }
+
+    @Override
+    public ScrollDelegate getScrollDelegate() {
+        return this;
+    }
+
+    public static WebViewClassic fromWebView(WebView webView) {
+        return webView == null ? null : (WebViewClassic) webView.getWebViewProvider();
+    }
+
+    // Accessors, purely for convenience (and to reduce code churn during webview proxy migration).
+    int getScrollX() {
+        return mWebView.getScrollX();
+    }
+
+    int getScrollY() {
+        return mWebView.getScrollY();
+    }
+
+    int getWidth() {
+        return mWebView.getWidth();
+    }
+
+    int getHeight() {
+        return mWebView.getHeight();
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    void invalidate() {
+        mWebView.invalidate();
+    }
+
+    // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
+    void setScrollXRaw(int mScrollX) {
+        mWebViewPrivate.setScrollXRaw(mScrollX);
+    }
+
+    void setScrollYRaw(int mScrollY) {
+        mWebViewPrivate.setScrollYRaw(mScrollY);
+    }
+
+    // === END: WebView Proxy binding ===
+
+    private static class TrustStorageListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
+                handleCertTrustChanged();
+            }
+        }
+    }
+    private static TrustStorageListener sTrustStorageListener;
+
+    /**
+     * Handles update to the trust storage.
+     */
+    private static void handleCertTrustChanged() {
+        // send a message for indicating trust storage change
+        WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
+    }
+
+    /*
+     * @param context This method expects this to be a valid context.
+     */
+    private static void setupTrustStorageListener(Context context) {
+        if (sTrustStorageListener != null ) {
+            return;
+        }
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
+        sTrustStorageListener = new TrustStorageListener();
+        Intent current =
+            context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
+        if (current != null) {
+            handleCertTrustChanged();
+        }
+    }
+
+    private static class ProxyReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
+                handleProxyBroadcast(intent);
+            }
+        }
+    }
+
+    /*
+     * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
+     */
+    private static ProxyReceiver sProxyReceiver;
+
+    /*
+     * @param context This method expects this to be a valid context
+     */
+    private static synchronized void setupProxyListener(Context context) {
+        if (sProxyReceiver != null || sNotificationsEnabled == false) {
+            return;
+        }
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
+        sProxyReceiver = new ProxyReceiver();
+        Intent currentProxy = context.getApplicationContext().registerReceiver(
+                sProxyReceiver, filter);
+        if (currentProxy != null) {
+            handleProxyBroadcast(currentProxy);
+        }
+    }
+
+    /*
+     * @param context This method expects this to be a valid context
+     */
+    private static synchronized void disableProxyListener(Context context) {
+        if (sProxyReceiver == null)
+            return;
+
+        context.getApplicationContext().unregisterReceiver(sProxyReceiver);
+        sProxyReceiver = null;
+    }
+
+    private static void handleProxyBroadcast(Intent intent) {
+        ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
+        if (proxyProperties == null || proxyProperties.getHost() == null) {
+            WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
+            return;
+        }
+        WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
+    }
+
+    /*
+     * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
+     * or ACTION_PACKAGE_REMOVED.
+     */
+    private static boolean sPackageInstallationReceiverAdded = false;
+
+    /*
+     * A set of Google packages we monitor for the
+     * navigator.isApplicationInstalled() API. Add additional packages as
+     * needed.
+     */
+    private static Set<String> sGoogleApps;
+    static {
+        sGoogleApps = new HashSet<String>();
+        sGoogleApps.add("com.google.android.youtube");
+    }
+
+    private static class PackageListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final String packageName = intent.getData().getSchemeSpecificPart();
+            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+                // if it is replacing, refreshPlugins() when adding
+                return;
+            }
+
+            if (sGoogleApps.contains(packageName)) {
+                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                    WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
+                } else {
+                    WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
+                }
+            }
+
+            PluginManager pm = PluginManager.getInstance(context);
+            if (pm.containsPluginPermissionAndSignatures(packageName)) {
+                pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
+            }
+        }
+    }
+
+    private void setupPackageListener(Context context) {
+
+        /*
+         * we must synchronize the instance check and the creation of the
+         * receiver to ensure that only ONE receiver exists for all WebView
+         * instances.
+         */
+        synchronized (WebViewClassic.class) {
+
+            // if the receiver already exists then we do not need to register it
+            // again
+            if (sPackageInstallationReceiverAdded) {
+                return;
+            }
+
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            BroadcastReceiver packageListener = new PackageListener();
+            context.getApplicationContext().registerReceiver(packageListener, filter);
+            sPackageInstallationReceiverAdded = true;
+        }
+
+        // check if any of the monitored apps are already installed
+        AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
+
+            @Override
+            protected Set<String> doInBackground(Void... unused) {
+                Set<String> installedPackages = new HashSet<String>();
+                PackageManager pm = mContext.getPackageManager();
+                for (String name : sGoogleApps) {
+                    try {
+                        pm.getPackageInfo(name,
+                                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
+                        installedPackages.add(name);
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // package not found
+                    }
+                }
+                return installedPackages;
+            }
+
+            // Executes on the UI thread
+            @Override
+            protected void onPostExecute(Set<String> installedPackages) {
+                if (mWebViewCore != null) {
+                    mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
+                }
+            }
+        };
+        task.execute();
+    }
+
+    void updateMultiTouchSupport(Context context) {
+        mZoomManager.updateMultiTouchSupport(context);
+    }
+
+    private void init() {
+        OnTrimMemoryListener.init(mContext);
+        sDisableNavcache = nativeDisableNavcache();
+        mWebView.setWillNotDraw(false);
+        mWebView.setFocusable(true);
+        mWebView.setFocusableInTouchMode(true);
+        mWebView.setClickable(true);
+        mWebView.setLongClickable(true);
+
+        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+        int slop = configuration.getScaledTouchSlop();
+        mTouchSlopSquare = slop * slop;
+        slop = configuration.getScaledDoubleTapSlop();
+        mDoubleTapSlopSquare = slop * slop;
+        final float density = mContext.getResources().getDisplayMetrics().density;
+        // use one line height, 16 based on our current default font, for how
+        // far we allow a touch be away from the edge of a link
+        mNavSlop = (int) (16 * density);
+        mZoomManager.init(density);
+        mMaximumFling = configuration.getScaledMaximumFlingVelocity();
+
+        // Compute the inverse of the density squared.
+        DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
+
+        mOverscrollDistance = configuration.getScaledOverscrollDistance();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
+
+        setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle());
+        // Initially use a size of two, since the user is likely to only hold
+        // down two keys at a time (shift + another key)
+        mKeysPressed = new Vector<Integer>(2);
+        mHTML5VideoViewProxy = null ;
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return true;
+    }
+
+    /**
+     * Adds accessibility APIs to JavaScript.
+     *
+     * Note: This method is responsible to performing the necessary
+     *       check if the accessibility APIs should be exposed.
+     */
+    private void addAccessibilityApisToJavaScript() {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()
+                && getSettings().getJavaScriptEnabled()) {
+            // exposing the TTS for now ...
+            final Context ctx = mContext;
+            if (ctx != null) {
+                final String packageName = ctx.getPackageName();
+                if (packageName != null) {
+                    mTextToSpeech = new TextToSpeech(ctx, null, null,
+                            packageName + ".**webview**", true);
+                    addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes accessibility APIs from JavaScript.
+     */
+    private void removeAccessibilityApisFromJavaScript() {
+        // exposing the TTS for now ...
+        if (mTextToSpeech != null) {
+            removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
+            mTextToSpeech.shutdown();
+            mTextToSpeech = null;
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        info.setScrollable(isScrollableForAccessibility());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        event.setScrollable(isScrollableForAccessibility());
+        event.setScrollX(getScrollX());
+        event.setScrollY(getScrollY());
+        final int convertedContentWidth = contentToViewX(getContentWidth());
+        final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft()
+                - mWebView.getPaddingLeft();
+        event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
+        final int convertedContentHeight = contentToViewY(getContentHeight());
+        final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+                - mWebView.getPaddingBottom();
+        event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
+    }
+
+    private boolean isScrollableForAccessibility() {
+        return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
+                - mWebView.getPaddingRight()
+                || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop()
+                - mWebView.getPaddingBottom());
+    }
+
+    @Override
+    public void setOverScrollMode(int mode) {
+        if (mode != View.OVER_SCROLL_NEVER) {
+            if (mOverScrollGlow == null) {
+                mOverScrollGlow = new OverScrollGlow(this);
+            }
+        } else {
+            mOverScrollGlow = null;
+        }
+    }
+
+    /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
+        final float density = mContext.getResources().getDisplayMetrics().density
+                * 100 / zoomDensity;
+        updateDefaultZoomDensity(density);
+    }
+
+    /* package */ void updateDefaultZoomDensity(float density) {
+        mNavSlop = (int) (16 * density);
+        mZoomManager.updateDefaultZoomDensity(density);
+    }
+
+    /* package */ boolean onSavePassword(String schemePlusHost, String username,
+            String password, final Message resumeMsg) {
+       boolean rVal = false;
+       if (resumeMsg == null) {
+           // null resumeMsg implies saving password silently
+           mDatabase.setUsernamePassword(schemePlusHost, username, password);
+       } else {
+            final Message remember = mPrivateHandler.obtainMessage(
+                    REMEMBER_PASSWORD);
+            remember.getData().putString("host", schemePlusHost);
+            remember.getData().putString("username", username);
+            remember.getData().putString("password", password);
+            remember.obj = resumeMsg;
+
+            final Message neverRemember = mPrivateHandler.obtainMessage(
+                    NEVER_REMEMBER_PASSWORD);
+            neverRemember.getData().putString("host", schemePlusHost);
+            neverRemember.getData().putString("username", username);
+            neverRemember.getData().putString("password", password);
+            neverRemember.obj = resumeMsg;
+
+            new AlertDialog.Builder(mContext)
+                    .setTitle(com.android.internal.R.string.save_password_label)
+                    .setMessage(com.android.internal.R.string.save_password_message)
+                    .setPositiveButton(com.android.internal.R.string.save_password_notnow,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            resumeMsg.sendToTarget();
+                        }
+                    })
+                    .setNeutralButton(com.android.internal.R.string.save_password_remember,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            remember.sendToTarget();
+                        }
+                    })
+                    .setNegativeButton(com.android.internal.R.string.save_password_never,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            neverRemember.sendToTarget();
+                        }
+                    })
+                    .setOnCancelListener(new OnCancelListener() {
+                        @Override
+                        public void onCancel(DialogInterface dialog) {
+                            resumeMsg.sendToTarget();
+                        }
+                    }).show();
+            // Return true so that WebViewCore will pause while the dialog is
+            // up.
+            rVal = true;
+        }
+       return rVal;
+    }
+
+    @Override
+    public void setScrollBarStyle(int style) {
+        if (style == View.SCROLLBARS_INSIDE_INSET
+                || style == View.SCROLLBARS_OUTSIDE_INSET) {
+            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
+        } else {
+            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
+        }
+    }
+
+    /**
+     * Specify whether the horizontal scrollbar has overlay style.
+     * @param overlay TRUE if horizontal scrollbar should have overlay style.
+     */
+    public void setHorizontalScrollbarOverlay(boolean overlay) {
+        checkThread();
+        mOverlayHorizontalScrollbar = overlay;
+    }
+
+    /**
+     * Specify whether the vertical scrollbar has overlay style.
+     * @param overlay TRUE if vertical scrollbar should have overlay style.
+     */
+    public void setVerticalScrollbarOverlay(boolean overlay) {
+        checkThread();
+        mOverlayVerticalScrollbar = overlay;
+    }
+
+    /**
+     * Return whether horizontal scrollbar has overlay style
+     * @return TRUE if horizontal scrollbar has overlay style.
+     */
+    public boolean overlayHorizontalScrollbar() {
+        checkThread();
+        return mOverlayHorizontalScrollbar;
+    }
+
+    /**
+     * Return whether vertical scrollbar has overlay style
+     * @return TRUE if vertical scrollbar has overlay style.
+     */
+    public boolean overlayVerticalScrollbar() {
+        checkThread();
+        return mOverlayVerticalScrollbar;
+    }
+
+    /*
+     * Return the width of the view where the content of WebView should render
+     * to.
+     * Note: this can be called from WebCoreThread.
+     */
+    /* package */ int getViewWidth() {
+        if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
+            return getWidth();
+        } else {
+            return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth());
+        }
+    }
+
+    // Interface to enable the browser to override title bar handling.
+    public interface TitleBarDelegate {
+        int getTitleHeight();
+        public void onSetEmbeddedTitleBar(final View title);
+    }
+
+    /**
+     * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
+     * scrolling
+     * @hide
+     */
+    protected int getTitleHeight() {
+        if (mWebView instanceof TitleBarDelegate) {
+            return ((TitleBarDelegate) mWebView).getTitleHeight();
+        }
+        return mTitleBar != null ? mTitleBar.getHeight() : 0;
+    }
+
+    /**
+     * Return the visible height (in pixels) of the embedded title bar (if any).
+     *
+     * @return This method is obsolete and always returns 0.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public int getVisibleTitleHeight() {
+        // Actually, this method returns the height of the embedded title bar if one is set via the
+        // hidden setEmbeddedTitleBar method.
+        checkThread();
+        return getVisibleTitleHeightImpl();
+    }
+
+    private int getVisibleTitleHeightImpl() {
+        // need to restrict mScrollY due to over scroll
+        return Math.max(getTitleHeight() - Math.max(0, getScrollY()),
+                getOverlappingActionModeHeight());
+    }
+
+    private int mCachedOverlappingActionModeHeight = -1;
+
+    private int getOverlappingActionModeHeight() {
+        if (mFindCallback == null) {
+            return 0;
+        }
+        if (mCachedOverlappingActionModeHeight < 0) {
+            mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
+            mCachedOverlappingActionModeHeight = Math.max(0,
+                    mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
+        }
+        return mCachedOverlappingActionModeHeight;
+    }
+
+    /*
+     * Return the height of the view where the content of WebView should render
+     * to.  Note that this excludes mTitleBar, if there is one.
+     * Note: this can be called from WebCoreThread.
+     */
+    /* package */ int getViewHeight() {
+        return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
+    }
+
+    int getViewHeightWithTitle() {
+        int height = getHeight();
+        if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
+            height -= mWebViewPrivate.getHorizontalScrollbarHeight();
+        }
+        return height;
+    }
+
+    /**
+     * @return The SSL certificate for the main top-level page or null if
+     * there is no certificate (the site is not secure).
+     */
+    public SslCertificate getCertificate() {
+        checkThread();
+        return mCertificate;
+    }
+
+    /**
+     * Sets the SSL certificate for the main top-level page.
+     */
+    public void setCertificate(SslCertificate certificate) {
+        checkThread();
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "setCertificate=" + certificate);
+        }
+        // here, the certificate can be null (if the site is not secure)
+        mCertificate = certificate;
+    }
+
+    //-------------------------------------------------------------------------
+    // Methods called by activity
+    //-------------------------------------------------------------------------
+
+    /**
+     * Save the username and password for a particular host in the WebView's
+     * internal database.
+     * @param host The host that required the credentials.
+     * @param username The username for the given host.
+     * @param password The password for the given host.
+     */
+    public void savePassword(String host, String username, String password) {
+        checkThread();
+        mDatabase.setUsernamePassword(host, username, password);
+    }
+
+    /**
+     * Set the HTTP authentication credentials for a given host and realm.
+     *
+     * @param host The host for the credentials.
+     * @param realm The realm for the credentials.
+     * @param username The username for the password. If it is null, it means
+     *                 password can't be saved.
+     * @param password The password
+     */
+    public void setHttpAuthUsernamePassword(String host, String realm,
+            String username, String password) {
+        checkThread();
+        mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
+    }
+
+    /**
+     * Retrieve the HTTP authentication username and password for a given
+     * host & realm pair
+     *
+     * @param host The host for which the credentials apply.
+     * @param realm The realm for which the credentials apply.
+     * @return String[] if found, String[0] is username, which can be null and
+     *         String[1] is password. Return null if it can't find anything.
+     */
+    public String[] getHttpAuthUsernamePassword(String host, String realm) {
+        checkThread();
+        return mDatabase.getHttpAuthUsernamePassword(host, realm);
+    }
+
+    /**
+     * Remove Find or Select ActionModes, if active.
+     */
+    private void clearActionModes() {
+        if (mSelectCallback != null) {
+            mSelectCallback.finish();
+        }
+        if (mFindCallback != null) {
+            mFindCallback.finish();
+        }
+    }
+
+    /**
+     * Called to clear state when moving from one page to another, or changing
+     * in some other way that makes elements associated with the current page
+     * (such as WebTextView or ActionModes) no longer relevant.
+     */
+    private void clearHelpers() {
+        clearTextEntry();
+        clearActionModes();
+        dismissFullScreenMode();
+        cancelSelectDialog();
+    }
+
+    private void cancelSelectDialog() {
+        if (mListBoxDialog != null) {
+            mListBoxDialog.cancel();
+            mListBoxDialog = null;
+        }
+    }
+
+    /**
+     * Destroy the internal state of the WebView. This method should be called
+     * after the WebView has been removed from the view system. No other
+     * methods may be called on a WebView after destroy.
+     */
+    public void destroy() {
+        checkThread();
+        destroyImpl();
+    }
+
+    private void destroyImpl() {
+        clearHelpers();
+        if (mListBoxDialog != null) {
+            mListBoxDialog.dismiss();
+            mListBoxDialog = null;
+        }
+        // remove so that it doesn't cause events
+        if (mWebTextView != null) {
+            mWebTextView.remove();
+            mWebTextView = null;
+        }
+        if (mNativeClass != 0) nativeStopGL();
+        if (mWebViewCore != null) {
+            // Set the handlers to null before destroying WebViewCore so no
+            // more messages will be posted.
+            mCallbackProxy.setWebViewClient(null);
+            mCallbackProxy.setWebChromeClient(null);
+            // Tell WebViewCore to destroy itself
+            synchronized (this) {
+                WebViewCore webViewCore = mWebViewCore;
+                mWebViewCore = null; // prevent using partial webViewCore
+                webViewCore.destroy();
+            }
+            // Remove any pending messages that might not be serviced yet.
+            mPrivateHandler.removeCallbacksAndMessages(null);
+            mCallbackProxy.removeCallbacksAndMessages(null);
+            // Wake up the WebCore thread just in case it is waiting for a
+            // JavaScript dialog.
+            synchronized (mCallbackProxy) {
+                mCallbackProxy.notify();
+            }
+        }
+        if (mNativeClass != 0) {
+            nativeDestroy();
+            mNativeClass = 0;
+        }
+    }
+
+    /**
+     * Enables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void enablePlatformNotifications() {
+        checkThread();
+        synchronized (WebViewClassic.class) {
+            sNotificationsEnabled = true;
+            Context context = JniUtil.getContext();
+            if (context != null)
+                setupProxyListener(context);
+        }
+    }
+
+    /**
+     * Disables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void disablePlatformNotifications() {
+        checkThread();
+        synchronized (WebViewClassic.class) {
+            sNotificationsEnabled = false;
+            Context context = JniUtil.getContext();
+            if (context != null)
+                disableProxyListener(context);
+        }
+    }
+
+    /**
+     * Sets JavaScript engine flags.
+     *
+     * @param flags JS engine flags in a String
+     *
+     * @hide This is an implementation detail.
+     */
+    public void setJsFlags(String flags) {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
+    }
+
+    /**
+     * Inform WebView of the network state. This is used to set
+     * the JavaScript property window.navigator.isOnline and
+     * generates the online/offline event as specified in HTML5, sec. 5.7.7
+     * @param networkUp boolean indicating if network is available
+     */
+    public void setNetworkAvailable(boolean networkUp) {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
+                networkUp ? 1 : 0, 0);
+    }
+
+    /**
+     * Inform WebView about the current network type.
+     * {@hide}
+     */
+    public void setNetworkType(String type, String subtype) {
+        checkThread();
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("type", type);
+        map.put("subtype", subtype);
+        mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
+    }
+    /**
+     * Save the state of this WebView used in
+     * {@link android.app.Activity#onSaveInstanceState}. Please note that this
+     * method no longer stores the display data for this WebView. The previous
+     * behavior could potentially leak files if {@link #restoreState} was never
+     * called. See {@link #savePicture} and {@link #restorePicture} for saving
+     * and restoring the display data.
+     * @param outState The Bundle to store the WebView state.
+     * @return The same copy of the back/forward list used to save the state. If
+     *         saveState fails, the returned list will be null.
+     * @see #savePicture
+     * @see #restorePicture
+     */
+    public WebBackForwardList saveState(Bundle outState) {
+        checkThread();
+        if (outState == null) {
+            return null;
+        }
+        // We grab a copy of the back/forward list because a client of WebView
+        // may have invalidated the history list by calling clearHistory.
+        WebBackForwardList list = copyBackForwardList();
+        final int currentIndex = list.getCurrentIndex();
+        final int size = list.getSize();
+        // We should fail saving the state if the list is empty or the index is
+        // not in a valid range.
+        if (currentIndex < 0 || currentIndex >= size || size == 0) {
+            return null;
+        }
+        outState.putInt("index", currentIndex);
+        // FIXME: This should just be a byte[][] instead of ArrayList but
+        // Parcel.java does not have the code to handle multi-dimensional
+        // arrays.
+        ArrayList<byte[]> history = new ArrayList<byte[]>(size);
+        for (int i = 0; i < size; i++) {
+            WebHistoryItem item = list.getItemAtIndex(i);
+            if (null == item) {
+                // FIXME: this shouldn't happen
+                // need to determine how item got set to null
+                Log.w(LOGTAG, "saveState: Unexpected null history item.");
+                return null;
+            }
+            byte[] data = item.getFlattenedData();
+            if (data == null) {
+                // It would be very odd to not have any data for a given history
+                // item. And we will fail to rebuild the history list without
+                // flattened data.
+                return null;
+            }
+            history.add(data);
+        }
+        outState.putSerializable("history", history);
+        if (mCertificate != null) {
+            outState.putBundle("certificate",
+                               SslCertificate.saveState(mCertificate));
+        }
+        outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
+        mZoomManager.saveZoomState(outState);
+        return list;
+    }
+
+    /**
+     * Save the current display data to the Bundle given. Used in conjunction
+     * with {@link #saveState}.
+     * @param b A Bundle to store the display data.
+     * @param dest The file to store the serialized picture data. Will be
+     *             overwritten with this WebView's picture data.
+     * @return True if the picture was successfully saved.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public boolean savePicture(Bundle b, final File dest) {
+        checkThread();
+        if (dest == null || b == null) {
+            return false;
+        }
+        final Picture p = capturePicture();
+        // Use a temporary file while writing to ensure the destination file
+        // contains valid data.
+        final File temp = new File(dest.getPath() + ".writing");
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                FileOutputStream out = null;
+                try {
+                    out = new FileOutputStream(temp);
+                    p.writeToStream(out);
+                    // Writing the picture succeeded, rename the temporary file
+                    // to the destination.
+                    temp.renameTo(dest);
+                } catch (Exception e) {
+                    // too late to do anything about it.
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (Exception e) {
+                            // Can't do anything about that
+                        }
+                    }
+                    temp.delete();
+                }
+            }
+        }).start();
+        // now update the bundle
+        b.putInt("scrollX", getScrollX());
+        b.putInt("scrollY", getScrollY());
+        mZoomManager.saveZoomState(b);
+        return true;
+    }
+
+    private void restoreHistoryPictureFields(Picture p, Bundle b) {
+        int sx = b.getInt("scrollX", 0);
+        int sy = b.getInt("scrollY", 0);
+
+        mDrawHistory = true;
+        mHistoryPicture = p;
+
+        setScrollXRaw(sx);
+        setScrollYRaw(sy);
+        mZoomManager.restoreZoomState(b);
+        final float scale = mZoomManager.getScale();
+        mHistoryWidth = Math.round(p.getWidth() * scale);
+        mHistoryHeight = Math.round(p.getHeight() * scale);
+
+        invalidate();
+    }
+
+    /**
+     * Restore the display data that was save in {@link #savePicture}. Used in
+     * conjunction with {@link #restoreState}.
+     *
+     * Note that this will not work if the WebView is hardware accelerated.
+     * @param b A Bundle containing the saved display data.
+     * @param src The file where the picture data was stored.
+     * @return True if the picture was successfully restored.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public boolean restorePicture(Bundle b, File src) {
+        checkThread();
+        if (src == null || b == null) {
+            return false;
+        }
+        if (!src.exists()) {
+            return false;
+        }
+        try {
+            final FileInputStream in = new FileInputStream(src);
+            final Bundle copy = new Bundle(b);
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        final Picture p = Picture.createFromStream(in);
+                        if (p != null) {
+                            // Post a runnable on the main thread to update the
+                            // history picture fields.
+                            mPrivateHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    restoreHistoryPictureFields(p, copy);
+                                }
+                            });
+                        }
+                    } finally {
+                        try {
+                            in.close();
+                        } catch (Exception e) {
+                            // Nothing we can do now.
+                        }
+                    }
+                }
+            }).start();
+        } catch (FileNotFoundException e){
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    /**
+     * Saves the view data to the output stream. The output is highly
+     * version specific, and may not be able to be loaded by newer versions
+     * of WebView.
+     * @param stream The {@link OutputStream} to save to
+     * @return True if saved successfully
+     * @hide
+     */
+    public boolean saveViewState(OutputStream stream) {
+        try {
+            return ViewStateSerializer.serializeViewState(stream, this);
+        } catch (IOException e) {
+            Log.w(LOGTAG, "Failed to saveViewState", e);
+        }
+        return false;
+    }
+
+    /**
+     * Loads the view data from the input stream. See
+     * {@link #saveViewState(OutputStream)} for more information.
+     * @param stream The {@link InputStream} to load from
+     * @return True if loaded successfully
+     * @hide
+     */
+    public boolean loadViewState(InputStream stream) {
+        try {
+            mLoadedPicture = ViewStateSerializer.deserializeViewState(stream, this);
+            mBlockWebkitViewMessages = true;
+            setNewPicture(mLoadedPicture, true);
+            mLoadedPicture.mViewState = null;
+            return true;
+        } catch (IOException e) {
+            Log.w(LOGTAG, "Failed to loadViewState", e);
+        }
+        return false;
+    }
+
+    /**
+     * Clears the view state set with {@link #loadViewState(InputStream)}.
+     * This WebView will then switch to showing the content from webkit
+     * @hide
+     */
+    public void clearViewState() {
+        mBlockWebkitViewMessages = false;
+        mLoadedPicture = null;
+        invalidate();
+    }
+
+    /**
+     * Restore the state of this WebView from the given map used in
+     * {@link android.app.Activity#onRestoreInstanceState}. This method should
+     * be called to restore the state of the WebView before using the object. If
+     * it is called after the WebView has had a chance to build state (load
+     * pages, create a back/forward list, etc.) there may be undesirable
+     * side-effects. Please note that this method no longer restores the
+     * display data for this WebView. See {@link #savePicture} and {@link
+     * #restorePicture} for saving and restoring the display data.
+     * @param inState The incoming Bundle of state.
+     * @return The restored back/forward list or null if restoreState failed.
+     * @see #savePicture
+     * @see #restorePicture
+     */
+    public WebBackForwardList restoreState(Bundle inState) {
+        checkThread();
+        WebBackForwardList returnList = null;
+        if (inState == null) {
+            return returnList;
+        }
+        if (inState.containsKey("index") && inState.containsKey("history")) {
+            mCertificate = SslCertificate.restoreState(
+                inState.getBundle("certificate"));
+
+            final WebBackForwardList list = mCallbackProxy.getBackForwardList();
+            final int index = inState.getInt("index");
+            // We can't use a clone of the list because we need to modify the
+            // shared copy, so synchronize instead to prevent concurrent
+            // modifications.
+            synchronized (list) {
+                final List<byte[]> history =
+                        (List<byte[]>) inState.getSerializable("history");
+                final int size = history.size();
+                // Check the index bounds so we don't crash in native code while
+                // restoring the history index.
+                if (index < 0 || index >= size) {
+                    return null;
+                }
+                for (int i = 0; i < size; i++) {
+                    byte[] data = history.remove(0);
+                    if (data == null) {
+                        // If we somehow have null data, we cannot reconstruct
+                        // the item and thus our history list cannot be rebuilt.
+                        return null;
+                    }
+                    WebHistoryItem item = new WebHistoryItem(data);
+                    list.addHistoryItem(item);
+                }
+                // Grab the most recent copy to return to the caller.
+                returnList = copyBackForwardList();
+                // Update the copy to have the correct index.
+                returnList.setCurrentIndex(index);
+            }
+            // Restore private browsing setting.
+            if (inState.getBoolean("privateBrowsingEnabled")) {
+                getSettings().setPrivateBrowsingEnabled(true);
+            }
+            mZoomManager.restoreZoomState(inState);
+            // Remove all pending messages because we are restoring previous
+            // state.
+            mWebViewCore.removeMessages();
+            // Send a restore state message.
+            mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
+        }
+        return returnList;
+    }
+
+    /**
+     * Load the given URL with the specified additional HTTP headers.
+     * @param url The URL of the resource to load.
+     * @param additionalHttpHeaders The additional headers to be used in the
+     *            HTTP request for this URL, specified as a map from name to
+     *            value. Note that if this map contains any of the headers
+     *            that are set by default by the WebView, such as those
+     *            controlling caching, accept types or the User-Agent, their
+     *            values may be overriden by the WebView's defaults.
+     */
+    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+        checkThread();
+        loadUrlImpl(url, additionalHttpHeaders);
+    }
+
+    private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
+        switchOutDrawHistory();
+        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
+        arg.mUrl = url;
+        arg.mExtraHeaders = extraHeaders;
+        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
+        clearHelpers();
+    }
+
+    /**
+     * Load the given URL.
+     * @param url The URL of the resource to load.
+     */
+    public void loadUrl(String url) {
+        checkThread();
+        loadUrlImpl(url);
+    }
+
+    private void loadUrlImpl(String url) {
+        if (url == null) {
+            return;
+        }
+        loadUrlImpl(url, null);
+    }
+
+    /**
+     * Load the url with postData using "POST" method into the WebView. If url
+     * is not a network url, it will be loaded with {link
+     * {@link #loadUrl(String)} instead.
+     *
+     * @param url The url of the resource to load.
+     * @param postData The data will be passed to "POST" request.
+     */
+    public void postUrl(String url, byte[] postData) {
+        checkThread();
+        if (URLUtil.isNetworkUrl(url)) {
+            switchOutDrawHistory();
+            WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
+            arg.mUrl = url;
+            arg.mPostData = postData;
+            mWebViewCore.sendMessage(EventHub.POST_URL, arg);
+            clearHelpers();
+        } else {
+            loadUrlImpl(url);
+        }
+    }
+
+    /**
+     * Load the given data into the WebView using a 'data' scheme URL.
+     * <p>
+     * Note that JavaScript's same origin policy means that script running in a
+     * page loaded using this method will be unable to access content loaded
+     * using any scheme other than 'data', including 'http(s)'. To avoid this
+     * restriction, use {@link
+     * #loadDataWithBaseURL(String,String,String,String,String)
+     * loadDataWithBaseURL()} with an appropriate base URL.
+     * <p>
+     * If the value of the encoding parameter is 'base64', then the data must
+     * be encoded as base64. Otherwise, the data must use ASCII encoding for
+     * octets inside the range of safe URL characters and use the standard %xx
+     * hex encoding of URLs for octets outside that range. For example,
+     * '#', '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
+     * <p>
+     * The 'data' scheme URL formed by this method uses the default US-ASCII
+     * charset. If you need need to set a different charset, you should form a
+     * 'data' scheme URL which explicitly specifies a charset parameter in the
+     * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
+     * Note that the charset obtained from the mediatype portion of a data URL
+     * always overrides that specified in the HTML or XML document itself.
+     * @param data A String of data in the given encoding.
+     * @param mimeType The MIME type of the data, e.g. 'text/html'.
+     * @param encoding The encoding of the data.
+     */
+    public void loadData(String data, String mimeType, String encoding) {
+        checkThread();
+        loadDataImpl(data, mimeType, encoding);
+    }
+
+    private void loadDataImpl(String data, String mimeType, String encoding) {
+        StringBuilder dataUrl = new StringBuilder("data:");
+        dataUrl.append(mimeType);
+        if ("base64".equals(encoding)) {
+            dataUrl.append(";base64");
+        }
+        dataUrl.append(",");
+        dataUrl.append(data);
+        loadUrlImpl(dataUrl.toString());
+    }
+
+    /**
+     * Load the given data into the WebView, using baseUrl as the base URL for
+     * the content. The base URL is used both to resolve relative URLs and when
+     * applying JavaScript's same origin policy. The historyUrl is used for the
+     * history entry.
+     * <p>
+     * Note that content specified in this way can access local device files
+     * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
+     * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
+     * <p>
+     * If the base URL uses the data scheme, this method is equivalent to
+     * calling {@link #loadData(String,String,String) loadData()} and the
+     * historyUrl is ignored.
+     * @param baseUrl URL to use as the page's base URL. If null defaults to
+     *            'about:blank'
+     * @param data A String of data in the given encoding.
+     * @param mimeType The MIMEType of the data, e.g. 'text/html'. If null,
+     *            defaults to 'text/html'.
+     * @param encoding The encoding of the data.
+     * @param historyUrl URL to use as the history entry, if null defaults to
+     *            'about:blank'.
+     */
+    public void loadDataWithBaseURL(String baseUrl, String data,
+            String mimeType, String encoding, String historyUrl) {
+        checkThread();
+
+        if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
+            loadDataImpl(data, mimeType, encoding);
+            return;
+        }
+        switchOutDrawHistory();
+        WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
+        arg.mBaseUrl = baseUrl;
+        arg.mData = data;
+        arg.mMimeType = mimeType;
+        arg.mEncoding = encoding;
+        arg.mHistoryUrl = historyUrl;
+        mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
+        clearHelpers();
+    }
+
+    /**
+     * Saves the current view as a web archive.
+     *
+     * @param filename The filename where the archive should be placed.
+     */
+    public void saveWebArchive(String filename) {
+        checkThread();
+        saveWebArchiveImpl(filename, false, null);
+    }
+
+    /* package */ static class SaveWebArchiveMessage {
+        SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
+            mBasename = basename;
+            mAutoname = autoname;
+            mCallback = callback;
+        }
+
+        /* package */ final String mBasename;
+        /* package */ final boolean mAutoname;
+        /* package */ final ValueCallback<String> mCallback;
+        /* package */ String mResultFile;
+    }
+
+    /**
+     * Saves the current view as a web archive.
+     *
+     * @param basename The filename where the archive should be placed.
+     * @param autoname If false, takes basename to be a file. If true, basename
+     *                 is assumed to be a directory in which a filename will be
+     *                 chosen according to the url of the current page.
+     * @param callback Called after the web archive has been saved. The
+     *                 parameter for onReceiveValue will either be the filename
+     *                 under which the file was saved, or null if saving the
+     *                 file failed.
+     */
+    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
+        checkThread();
+        saveWebArchiveImpl(basename, autoname, callback);
+    }
+
+    private void saveWebArchiveImpl(String basename, boolean autoname,
+            ValueCallback<String> callback) {
+        mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
+            new SaveWebArchiveMessage(basename, autoname, callback));
+    }
+
+    /**
+     * Stop the current load.
+     */
+    public void stopLoading() {
+        checkThread();
+        // TODO: should we clear all the messages in the queue before sending
+        // STOP_LOADING?
+        switchOutDrawHistory();
+        mWebViewCore.sendMessage(EventHub.STOP_LOADING);
+    }
+
+    /**
+     * Reload the current url.
+     */
+    public void reload() {
+        checkThread();
+        clearHelpers();
+        switchOutDrawHistory();
+        mWebViewCore.sendMessage(EventHub.RELOAD);
+    }
+
+    /**
+     * Return true if this WebView has a back history item.
+     * @return True iff this WebView has a back history item.
+     */
+    public boolean canGoBack() {
+        checkThread();
+        WebBackForwardList l = mCallbackProxy.getBackForwardList();
+        synchronized (l) {
+            if (l.getClearPending()) {
+                return false;
+            } else {
+                return l.getCurrentIndex() > 0;
+            }
+        }
+    }
+
+    /**
+     * Go back in the history of this WebView.
+     */
+    public void goBack() {
+        checkThread();
+        goBackOrForwardImpl(-1);
+    }
+
+    /**
+     * Return true if this WebView has a forward history item.
+     * @return True iff this Webview has a forward history item.
+     */
+    public boolean canGoForward() {
+        checkThread();
+        WebBackForwardList l = mCallbackProxy.getBackForwardList();
+        synchronized (l) {
+            if (l.getClearPending()) {
+                return false;
+            } else {
+                return l.getCurrentIndex() < l.getSize() - 1;
+            }
+        }
+    }
+
+    /**
+     * Go forward in the history of this WebView.
+     */
+    public void goForward() {
+        checkThread();
+        goBackOrForwardImpl(1);
+    }
+
+    /**
+     * Return true if the page can go back or forward the given
+     * number of steps.
+     * @param steps The negative or positive number of steps to move the
+     *              history.
+     */
+    public boolean canGoBackOrForward(int steps) {
+        checkThread();
+        WebBackForwardList l = mCallbackProxy.getBackForwardList();
+        synchronized (l) {
+            if (l.getClearPending()) {
+                return false;
+            } else {
+                int newIndex = l.getCurrentIndex() + steps;
+                return newIndex >= 0 && newIndex < l.getSize();
+            }
+        }
+    }
+
+    /**
+     * Go to the history item that is the number of steps away from
+     * the current item. Steps is negative if backward and positive
+     * if forward.
+     * @param steps The number of steps to take back or forward in the back
+     *              forward list.
+     */
+    public void goBackOrForward(int steps) {
+        checkThread();
+        goBackOrForwardImpl(steps);
+    }
+
+    private void goBackOrForwardImpl(int steps) {
+        goBackOrForward(steps, false);
+    }
+
+    private void goBackOrForward(int steps, boolean ignoreSnapshot) {
+        if (steps != 0) {
+            clearHelpers();
+            mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
+                    ignoreSnapshot ? 1 : 0);
+        }
+    }
+
+    /**
+     * Returns true if private browsing is enabled in this WebView.
+     */
+    public boolean isPrivateBrowsingEnabled() {
+        checkThread();
+        return getSettings().isPrivateBrowsingEnabled();
+    }
+
+    private void startPrivateBrowsing() {
+        getSettings().setPrivateBrowsingEnabled(true);
+    }
+
+    private boolean extendScroll(int y) {
+        int finalY = mScroller.getFinalY();
+        int newY = pinLocY(finalY + y);
+        if (newY == finalY) return false;
+        mScroller.setFinalY(newY);
+        mScroller.extendDuration(computeDuration(0, y));
+        return true;
+    }
+
+    /**
+     * Scroll the contents of the view up by half the view size
+     * @param top true to jump to the top of the page
+     * @return true if the page was scrolled
+     */
+    public boolean pageUp(boolean top) {
+        checkThread();
+        if (mNativeClass == 0) {
+            return false;
+        }
+        nativeClearCursor(); // start next trackball movement from page edge
+        if (top) {
+            // go to the top of the document
+            return pinScrollTo(getScrollX(), 0, true, 0);
+        }
+        // Page up
+        int h = getHeight();
+        int y;
+        if (h > 2 * PAGE_SCROLL_OVERLAP) {
+            y = -h + PAGE_SCROLL_OVERLAP;
+        } else {
+            y = -h / 2;
+        }
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
+                : extendScroll(y);
+    }
+
+    /**
+     * Scroll the contents of the view down by half the page size
+     * @param bottom true to jump to bottom of page
+     * @return true if the page was scrolled
+     */
+    public boolean pageDown(boolean bottom) {
+        checkThread();
+        if (mNativeClass == 0) {
+            return false;
+        }
+        nativeClearCursor(); // start next trackball movement from page edge
+        if (bottom) {
+            return pinScrollTo(getScrollX(), computeRealVerticalScrollRange(), true, 0);
+        }
+        // Page down.
+        int h = getHeight();
+        int y;
+        if (h > 2 * PAGE_SCROLL_OVERLAP) {
+            y = h - PAGE_SCROLL_OVERLAP;
+        } else {
+            y = h / 2;
+        }
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
+                : extendScroll(y);
+    }
+
+    /**
+     * Clear the view so that onDraw() will draw nothing but white background,
+     * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
+     */
+    public void clearView() {
+        checkThread();
+        mContentWidth = 0;
+        mContentHeight = 0;
+        setBaseLayer(0, null, false, false);
+        mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
+    }
+
+    /**
+     * Return a new picture that captures the current display of the webview.
+     * This is a copy of the display, and will be unaffected if the webview
+     * later loads a different URL.
+     *
+     * @return a picture containing the current contents of the view. Note this
+     *         picture is of the entire document, and is not restricted to the
+     *         bounds of the view.
+     */
+    public Picture capturePicture() {
+        checkThread();
+        if (mNativeClass == 0) return null;
+        Picture result = new Picture();
+        nativeCopyBaseContentToPicture(result);
+        return result;
+    }
+
+    /**
+     *  Return true if the browser is displaying a TextView for text input.
+     */
+    private boolean inEditingMode() {
+        return mWebTextView != null && mWebTextView.getParent() != null;
+    }
+
+    /**
+     * Remove the WebTextView.
+     */
+    private void clearTextEntry() {
+        if (inEditingMode()) {
+            mWebTextView.remove();
+        } else {
+            // The keyboard may be open with the WebView as the served view
+            hideSoftKeyboard();
+        }
+    }
+
+    /**
+     * Return the current scale of the WebView
+     * @return The current scale.
+     */
+    public float getScale() {
+        checkThread();
+        return mZoomManager.getScale();
+    }
+
+    /**
+     * Compute the reading level scale of the WebView
+     * @param scale The current scale.
+     * @return The reading level scale.
+     */
+    /*package*/ float computeReadingLevelScale(float scale) {
+        return mZoomManager.computeReadingLevelScale(scale);
+    }
+
+    /**
+     * Set the initial scale for the WebView. 0 means default. If
+     * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
+     * way. Otherwise it starts with 100%. If initial scale is greater than 0,
+     * WebView starts with this value as initial scale.
+     * Please note that unlike the scale properties in the viewport meta tag,
+     * this method doesn't take the screen density into account.
+     *
+     * @param scaleInPercent The initial scale in percent.
+     */
+    public void setInitialScale(int scaleInPercent) {
+        checkThread();
+        mZoomManager.setInitialScaleInPercent(scaleInPercent);
+    }
+
+    /**
+     * Invoke the graphical zoom picker widget for this WebView. This will
+     * result in the zoom widget appearing on the screen to control the zoom
+     * level of this WebView.
+     */
+    public void invokeZoomPicker() {
+        checkThread();
+        if (!getSettings().supportZoom()) {
+            Log.w(LOGTAG, "This WebView doesn't support zoom.");
+            return;
+        }
+        clearHelpers();
+        mZoomManager.invokeZoomPicker();
+    }
+
+    /**
+     * Return a HitTestResult based on the current cursor node. If a HTML::a tag
+     * is found and the anchor has a non-JavaScript url, the HitTestResult type
+     * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
+     * anchor does not have a url or if it is a JavaScript url, the type will
+     * be UNKNOWN_TYPE and the url has to be retrieved through
+     * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
+     * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
+     * the "extra" field. A type of
+     * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
+     * a child node. If a phone number is found, the HitTestResult type is set
+     * to PHONE_TYPE and the phone number is set in the "extra" field of
+     * HitTestResult. If a map address is found, the HitTestResult type is set
+     * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
+     * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
+     * and the email is set in the "extra" field of HitTestResult. Otherwise,
+     * HitTestResult type is set to UNKNOWN_TYPE.
+     */
+    public HitTestResult getHitTestResult() {
+        checkThread();
+        return hitTestResult(mInitialHitTestResult);
+    }
+
+    private HitTestResult hitTestResult(HitTestResult fallback) {
+        if (mNativeClass == 0 || sDisableNavcache) {
+            return fallback;
+        }
+
+        HitTestResult result = new HitTestResult();
+        if (nativeHasCursorNode()) {
+            if (nativeCursorIsTextInput()) {
+                result.setType(HitTestResult.EDIT_TEXT_TYPE);
+            } else {
+                String text = nativeCursorText();
+                if (text != null) {
+                    if (text.startsWith(SCHEME_TEL)) {
+                        result.setType(HitTestResult.PHONE_TYPE);
+                        result.setExtra(URLDecoder.decode(text
+                                .substring(SCHEME_TEL.length())));
+                    } else if (text.startsWith(SCHEME_MAILTO)) {
+                        result.setType(HitTestResult.EMAIL_TYPE);
+                        result.setExtra(text.substring(SCHEME_MAILTO.length()));
+                    } else if (text.startsWith(SCHEME_GEO)) {
+                        result.setType(HitTestResult.GEO_TYPE);
+                        result.setExtra(URLDecoder.decode(text
+                                .substring(SCHEME_GEO.length())));
+                    } else if (nativeCursorIsAnchor()) {
+                        result.setType(HitTestResult.SRC_ANCHOR_TYPE);
+                        result.setExtra(text);
+                    }
+                }
+            }
+        } else if (fallback != null) {
+            /* If webkit causes a rebuild while the long press is in progress,
+             * the cursor node may be reset, even if it is still around. This
+             * uses the cursor node saved when the touch began. Since the
+             * nativeImageURI below only changes the result if it is successful,
+             * this uses the data beneath the touch if available or the original
+             * tap data otherwise.
+             */
+            Log.v(LOGTAG, "hitTestResult use fallback");
+            result = fallback;
+        }
+        int type = result.getType();
+        if (type == HitTestResult.UNKNOWN_TYPE
+                || type == HitTestResult.SRC_ANCHOR_TYPE) {
+            // Now check to see if it is an image.
+            int contentX = viewToContentX(mLastTouchX + getScrollX());
+            int contentY = viewToContentY(mLastTouchY + getScrollY());
+            String text = nativeImageURI(contentX, contentY);
+            if (text != null) {
+                result.setType(type == HitTestResult.UNKNOWN_TYPE ?
+                        HitTestResult.IMAGE_TYPE :
+                        HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+                result.setExtra(text);
+            }
+        }
+        return result;
+    }
+
+    int getBlockLeftEdge(int x, int y, float readingScale) {
+        if (!sDisableNavcache) {
+            return nativeGetBlockLeftEdge(x, y, readingScale);
+        }
+
+        float invReadingScale = 1.0f / readingScale;
+        int readingWidth = (int) (getViewWidth() * invReadingScale);
+        int left = NO_LEFTEDGE;
+        if (mFocusedNode != null) {
+            final int length = mFocusedNode.mEnclosingParentRects.length;
+            for (int i = 0; i < length; i++) {
+                Rect rect = mFocusedNode.mEnclosingParentRects[i];
+                if (rect.width() < mFocusedNode.mHitTestSlop) {
+                    // ignore bounding boxes that are too small
+                    continue;
+                } else if (left != NO_LEFTEDGE && rect.width() > readingWidth) {
+                    // stop when bounding box doesn't fit the screen width
+                    // at reading scale
+                    break;
+                }
+
+                left = rect.left;
+            }
+        }
+
+        return left;
+    }
+
+    // Called by JNI when the DOM has changed the focus.  Clear the focus so
+    // that new keys will go to the newly focused field
+    private void domChangedFocus() {
+        if (inEditingMode()) {
+            mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
+        }
+    }
+    /**
+     * Request the anchor or image element URL at the last tapped point.
+     * If hrefMsg is null, this method returns immediately and does not
+     * dispatch hrefMsg to its target. If the tapped point hits an image,
+     * an anchor, or an image in an anchor, the message associates
+     * strings in named keys in its data. The value paired with the key
+     * may be an empty string.
+     *
+     * @param hrefMsg This message will be dispatched with the result of the
+     *                request. The message data contains three keys:
+     *                - "url" returns the anchor's href attribute.
+     *                - "title" returns the anchor's text.
+     *                - "src" returns the image's src attribute.
+     */
+    public void requestFocusNodeHref(Message hrefMsg) {
+        checkThread();
+        if (hrefMsg == null) {
+            return;
+        }
+        int contentX = viewToContentX(mLastTouchX + getScrollX());
+        int contentY = viewToContentY(mLastTouchY + getScrollY());
+        if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
+                && mFocusedNode.mHitTestY == contentY) {
+            hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
+            hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
+            hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
+            hrefMsg.sendToTarget();
+            return;
+        }
+        if (nativeHasCursorNode()) {
+            Rect cursorBounds = cursorRingBounds();
+            if (!cursorBounds.contains(contentX, contentY)) {
+                int slop = viewToContentDimension(mNavSlop);
+                cursorBounds.inset(-slop, -slop);
+                if (cursorBounds.contains(contentX, contentY)) {
+                    contentX = cursorBounds.centerX();
+                    contentY = cursorBounds.centerY();
+                }
+            }
+        }
+        mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
+                contentX, contentY, hrefMsg);
+    }
+
+    /**
+     * Request the url of the image last touched by the user. msg will be sent
+     * to its target with a String representing the url as its object.
+     *
+     * @param msg This message will be dispatched with the result of the request
+     *            as the data member with "url" as key. The result can be null.
+     */
+    public void requestImageRef(Message msg) {
+        checkThread();
+        if (0 == mNativeClass) return; // client isn't initialized
+        int contentX = viewToContentX(mLastTouchX + getScrollX());
+        int contentY = viewToContentY(mLastTouchY + getScrollY());
+        String ref = nativeImageURI(contentX, contentY);
+        Bundle data = msg.getData();
+        data.putString("url", ref);
+        msg.setData(data);
+        msg.sendToTarget();
+    }
+
+    static int pinLoc(int x, int viewMax, int docMax) {
+//        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
+        if (docMax < viewMax) {   // the doc has room on the sides for "blank"
+            // pin the short document to the top/left of the screen
+            x = 0;
+//            Log.d(LOGTAG, "--- center " + x);
+        } else if (x < 0) {
+            x = 0;
+//            Log.d(LOGTAG, "--- zero");
+        } else if (x + viewMax > docMax) {
+            x = docMax - viewMax;
+//            Log.d(LOGTAG, "--- pin " + x);
+        }
+        return x;
+    }
+
+    // Expects x in view coordinates
+    int pinLocX(int x) {
+        if (mInOverScrollMode) return x;
+        return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
+    }
+
+    // Expects y in view coordinates
+    int pinLocY(int y) {
+        if (mInOverScrollMode) return y;
+        return pinLoc(y, getViewHeightWithTitle(),
+                      computeRealVerticalScrollRange() + getTitleHeight());
+    }
+
+    /**
+     * A title bar which is embedded in this WebView, and scrolls along with it
+     * vertically, but not horizontally.
+     */
+    private View mTitleBar;
+
+    /**
+     * the title bar rendering gravity
+     */
+    private int mTitleGravity;
+
+    /**
+     * Add or remove a title bar to be embedded into the WebView, and scroll
+     * along with it vertically, while remaining in view horizontally. Pass
+     * null to remove the title bar from the WebView, and return to drawing
+     * the WebView normally without translating to account for the title bar.
+     * @hide
+     */
+    public void setEmbeddedTitleBar(View v) {
+        if (mWebView instanceof TitleBarDelegate) {
+            ((TitleBarDelegate) mWebView).onSetEmbeddedTitleBar(v);
+        }
+        if (mTitleBar == v) return;
+        if (mTitleBar != null) {
+            mWebView.removeView(mTitleBar);
+        }
+        if (null != v) {
+            mWebView.addView(v, new AbsoluteLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
+        }
+        mTitleBar = v;
+    }
+
+    /**
+     * Set where to render the embedded title bar
+     * NO_GRAVITY at the top of the page
+     * TOP        at the top of the screen
+     * @hide
+     */
+    public void setTitleBarGravity(int gravity) {
+        mTitleGravity = gravity;
+        // force refresh
+        invalidate();
+    }
+
+    /**
+     * Given a distance in view space, convert it to content space. Note: this
+     * does not reflect translation, just scaling, so this should not be called
+     * with coordinates, but should be called for dimensions like width or
+     * height.
+     */
+    private int viewToContentDimension(int d) {
+        return Math.round(d * mZoomManager.getInvScale());
+    }
+
+    /**
+     * Given an x coordinate in view space, convert it to content space.  Also
+     * may be used for absolute heights (such as for the WebTextView's
+     * textSize, which is unaffected by the height of the title bar).
+     */
+    /*package*/ int viewToContentX(int x) {
+        return viewToContentDimension(x);
+    }
+
+    /**
+     * Given a y coordinate in view space, convert it to content space.
+     * Takes into account the height of the title bar if there is one
+     * embedded into the WebView.
+     */
+    /*package*/ int viewToContentY(int y) {
+        return viewToContentDimension(y - getTitleHeight());
+    }
+
+    /**
+     * Given a x coordinate in view space, convert it to content space.
+     * Returns the result as a float.
+     */
+    private float viewToContentXf(int x) {
+        return x * mZoomManager.getInvScale();
+    }
+
+    /**
+     * Given a y coordinate in view space, convert it to content space.
+     * Takes into account the height of the title bar if there is one
+     * embedded into the WebView. Returns the result as a float.
+     */
+    private float viewToContentYf(int y) {
+        return (y - getTitleHeight()) * mZoomManager.getInvScale();
+    }
+
+    /**
+     * Given a distance in content space, convert it to view space. Note: this
+     * does not reflect translation, just scaling, so this should not be called
+     * with coordinates, but should be called for dimensions like width or
+     * height.
+     */
+    /*package*/ int contentToViewDimension(int d) {
+        return Math.round(d * mZoomManager.getScale());
+    }
+
+    /**
+     * Given an x coordinate in content space, convert it to view
+     * space.
+     */
+    /*package*/ int contentToViewX(int x) {
+        return contentToViewDimension(x);
+    }
+
+    /**
+     * Given a y coordinate in content space, convert it to view
+     * space.  Takes into account the height of the title bar.
+     */
+    /*package*/ int contentToViewY(int y) {
+        return contentToViewDimension(y) + getTitleHeight();
+    }
+
+    private Rect contentToViewRect(Rect x) {
+        return new Rect(contentToViewX(x.left), contentToViewY(x.top),
+                        contentToViewX(x.right), contentToViewY(x.bottom));
+    }
+
+    /*  To invalidate a rectangle in content coordinates, we need to transform
+        the rect into view coordinates, so we can then call invalidate(...).
+
+        Normally, we would just call contentToView[XY](...), which eventually
+        calls Math.round(coordinate * mActualScale). However, for invalidates,
+        we need to account for the slop that occurs with antialiasing. To
+        address that, we are a little more liberal in the size of the rect that
+        we invalidate.
+
+        This liberal calculation calls floor() for the top/left, and ceil() for
+        the bottom/right coordinates. This catches the possible extra pixels of
+        antialiasing that we might have missed with just round().
+     */
+
+    // Called by JNI to invalidate the View, given rectangle coordinates in
+    // content space
+    private void viewInvalidate(int l, int t, int r, int b) {
+        final float scale = mZoomManager.getScale();
+        final int dy = getTitleHeight();
+        mWebView.invalidate((int)Math.floor(l * scale),
+                (int)Math.floor(t * scale) + dy,
+                (int)Math.ceil(r * scale),
+                (int)Math.ceil(b * scale) + dy);
+    }
+
+    // Called by JNI to invalidate the View after a delay, given rectangle
+    // coordinates in content space
+    private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
+        final float scale = mZoomManager.getScale();
+        final int dy = getTitleHeight();
+        mWebView.postInvalidateDelayed(delay,
+                (int)Math.floor(l * scale),
+                (int)Math.floor(t * scale) + dy,
+                (int)Math.ceil(r * scale),
+                (int)Math.ceil(b * scale) + dy);
+    }
+
+    private void invalidateContentRect(Rect r) {
+        viewInvalidate(r.left, r.top, r.right, r.bottom);
+    }
+
+    // stop the scroll animation, and don't let a subsequent fling add
+    // to the existing velocity
+    private void abortAnimation() {
+        mScroller.abortAnimation();
+        mLastVelocity = 0;
+    }
+
+    /* call from webcoreview.draw(), so we're still executing in the UI thread
+    */
+    private void recordNewContentSize(int w, int h, boolean updateLayout) {
+
+        // premature data from webkit, ignore
+        if ((w | h) == 0) {
+            return;
+        }
+
+        // don't abort a scroll animation if we didn't change anything
+        if (mContentWidth != w || mContentHeight != h) {
+            // record new dimensions
+            mContentWidth = w;
+            mContentHeight = h;
+            // If history Picture is drawn, don't update scroll. They will be
+            // updated when we get out of that mode.
+            if (!mDrawHistory) {
+                // repin our scroll, taking into account the new content size
+                updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY()));
+                if (!mScroller.isFinished()) {
+                    // We are in the middle of a scroll.  Repin the final scroll
+                    // position.
+                    mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
+                    mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
+                }
+            }
+        }
+        contentSizeChanged(updateLayout);
+    }
+
+    // Used to avoid sending many visible rect messages.
+    private Rect mLastVisibleRectSent = new Rect();
+    private Rect mLastGlobalRect = new Rect();
+    private Rect mVisibleRect = new Rect();
+    private Rect mGlobalVisibleRect = new Rect();
+    private Point mScrollOffset = new Point();
+
+    Rect sendOurVisibleRect() {
+        if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
+        calcOurContentVisibleRect(mVisibleRect);
+        // Rect.equals() checks for null input.
+        if (!mVisibleRect.equals(mLastVisibleRectSent)) {
+            if (!mBlockWebkitViewMessages) {
+                mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
+                mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
+                mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
+                        nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, mScrollOffset);
+            }
+            mLastVisibleRectSent.set(mVisibleRect);
+            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+        }
+        if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect)
+                && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
+                        + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
+                        + mGlobalVisibleRect.bottom);
+            }
+            // TODO: the global offset is only used by windowRect()
+            // in ChromeClientAndroid ; other clients such as touch
+            // and mouse events could return view + screen relative points.
+            if (!mBlockWebkitViewMessages) {
+                mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
+            }
+            mLastGlobalRect.set(mGlobalVisibleRect);
+        }
+        return mVisibleRect;
+    }
+
+    private Point mGlobalVisibleOffset = new Point();
+    // Sets r to be the visible rectangle of our webview in view coordinates
+    private void calcOurVisibleRect(Rect r) {
+        mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset);
+        r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
+    }
+
+    // Sets r to be our visible rectangle in content coordinates
+    private void calcOurContentVisibleRect(Rect r) {
+        calcOurVisibleRect(r);
+        r.left = viewToContentX(r.left);
+        // viewToContentY will remove the total height of the title bar.  Add
+        // the visible height back in to account for the fact that if the title
+        // bar is partially visible, the part of the visible rect which is
+        // displaying our content is displaced by that amount.
+        r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
+        r.right = viewToContentX(r.right);
+        r.bottom = viewToContentY(r.bottom);
+    }
+
+    private Rect mContentVisibleRect = new Rect();
+    // Sets r to be our visible rectangle in content coordinates. We use this
+    // method on the native side to compute the position of the fixed layers.
+    // Uses floating coordinates (necessary to correctly place elements when
+    // the scale factor is not 1)
+    private void calcOurContentVisibleRectF(RectF r) {
+        calcOurVisibleRect(mContentVisibleRect);
+        r.left = viewToContentXf(mContentVisibleRect.left);
+        // viewToContentY will remove the total height of the title bar.  Add
+        // the visible height back in to account for the fact that if the title
+        // bar is partially visible, the part of the visible rect which is
+        // displaying our content is displaced by that amount.
+        r.top = viewToContentYf(mContentVisibleRect.top + getVisibleTitleHeightImpl());
+        r.right = viewToContentXf(mContentVisibleRect.right);
+        r.bottom = viewToContentYf(mContentVisibleRect.bottom);
+    }
+
+    static class ViewSizeData {
+        int mWidth;
+        int mHeight;
+        float mHeightWidthRatio;
+        int mActualViewHeight;
+        int mTextWrapWidth;
+        int mAnchorX;
+        int mAnchorY;
+        float mScale;
+        boolean mIgnoreHeight;
+    }
+
+    /**
+     * Compute unzoomed width and height, and if they differ from the last
+     * values we sent, send them to webkit (to be used as new viewport)
+     *
+     * @param force ensures that the message is sent to webkit even if the width
+     * or height has not changed since the last message
+     *
+     * @return true if new values were sent
+     */
+    boolean sendViewSizeZoom(boolean force) {
+        if (mBlockWebkitViewMessages) return false;
+        if (mZoomManager.isPreventingWebkitUpdates()) return false;
+
+        int viewWidth = getViewWidth();
+        int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
+        // This height could be fixed and be different from actual visible height.
+        int viewHeight = getViewHeightWithTitle() - getTitleHeight();
+        int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
+        // Make the ratio more accurate than (newHeight / newWidth), since the
+        // latter both are calculated and rounded.
+        float heightWidthRatio = (float) viewHeight / viewWidth;
+        /*
+         * Because the native side may have already done a layout before the
+         * View system was able to measure us, we have to send a height of 0 to
+         * remove excess whitespace when we grow our width. This will trigger a
+         * layout and a change in content size. This content size change will
+         * mean that contentSizeChanged will either call this method directly or
+         * indirectly from onSizeChanged.
+         */
+        if (newWidth > mLastWidthSent && mWrapContent) {
+            newHeight = 0;
+            heightWidthRatio = 0;
+        }
+        // Actual visible content height.
+        int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
+        // Avoid sending another message if the dimensions have not changed.
+        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
+                actualViewHeight != mLastActualHeightSent) {
+            ViewSizeData data = new ViewSizeData();
+            data.mWidth = newWidth;
+            data.mHeight = newHeight;
+            data.mHeightWidthRatio = heightWidthRatio;
+            data.mActualViewHeight = actualViewHeight;
+            data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
+            data.mScale = mZoomManager.getScale();
+            data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
+                    && !mHeightCanMeasure;
+            data.mAnchorX = mZoomManager.getDocumentAnchorX();
+            data.mAnchorY = mZoomManager.getDocumentAnchorY();
+            mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
+            mLastWidthSent = newWidth;
+            mLastHeightSent = newHeight;
+            mLastActualHeightSent = actualViewHeight;
+            mZoomManager.clearDocumentAnchor();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Update the double-tap zoom.
+     */
+    /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
+        mZoomManager.updateDoubleTapZoom(doubleTapZoom);
+    }
+
+    private int computeRealHorizontalScrollRange() {
+        if (mDrawHistory) {
+            return mHistoryWidth;
+        } else {
+            // to avoid rounding error caused unnecessary scrollbar, use floor
+            return (int) Math.floor(mContentWidth * mZoomManager.getScale());
+        }
+    }
+
+    @Override
+    public int computeHorizontalScrollRange() {
+        int range = computeRealHorizontalScrollRange();
+
+        // Adjust reported range if overscrolled to compress the scroll bars
+        final int scrollX = getScrollX();
+        final int overscrollRight = computeMaxScrollX();
+        if (scrollX < 0) {
+            range -= scrollX;
+        } else if (scrollX > overscrollRight) {
+            range += scrollX - overscrollRight;
+        }
+
+        return range;
+    }
+
+    @Override
+    public int computeHorizontalScrollOffset() {
+        return Math.max(getScrollX(), 0);
+    }
+
+    private int computeRealVerticalScrollRange() {
+        if (mDrawHistory) {
+            return mHistoryHeight;
+        } else {
+            // to avoid rounding error caused unnecessary scrollbar, use floor
+            return (int) Math.floor(mContentHeight * mZoomManager.getScale());
+        }
+    }
+
+    @Override
+    public int computeVerticalScrollRange() {
+        int range = computeRealVerticalScrollRange();
+
+        // Adjust reported range if overscrolled to compress the scroll bars
+        final int scrollY = getScrollY();
+        final int overscrollBottom = computeMaxScrollY();
+        if (scrollY < 0) {
+            range -= scrollY;
+        } else if (scrollY > overscrollBottom) {
+            range += scrollY - overscrollBottom;
+        }
+
+        return range;
+    }
+
+    @Override
+    public int computeVerticalScrollOffset() {
+        return Math.max(getScrollY() - getTitleHeight(), 0);
+    }
+
+    @Override
+    public int computeVerticalScrollExtent() {
+        return getViewHeight();
+    }
+
+    /** @hide */
+    @Override
+    public void onDrawVerticalScrollBar(Canvas canvas,
+                                           Drawable scrollBar,
+                                           int l, int t, int r, int b) {
+        if (getScrollY() < 0) {
+            t -= getScrollY();
+        }
+        scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
+        scrollBar.draw(canvas);
+    }
+
+    @Override
+    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
+            boolean clampedY) {
+        // Special-case layer scrolling so that we do not trigger normal scroll
+        // updating.
+        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+            scrollLayerTo(scrollX, scrollY);
+            return;
+        }
+        mInOverScrollMode = false;
+        int maxX = computeMaxScrollX();
+        int maxY = computeMaxScrollY();
+        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;
+        }
+        if (scrollY < 0 || scrollY > maxY) {
+            mInOverScrollMode = true;
+        }
+
+        int oldX = getScrollX();
+        int oldY = getScrollY();
+
+        mWebViewPrivate.super_scrollTo(scrollX, scrollY);
+
+        if (mOverScrollGlow != null) {
+            mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
+        }
+    }
+
+    /**
+     * Get the url for the current page. This is not always the same as the url
+     * passed to WebViewClient.onPageStarted because although the load for
+     * that url has begun, the current page may not have changed.
+     * @return The url for the current page.
+     */
+    public String getUrl() {
+        checkThread();
+        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+        return h != null ? h.getUrl() : null;
+    }
+
+    /**
+     * Get the original url for the current page. This is not always the same
+     * as the url passed to WebViewClient.onPageStarted because although the
+     * load for that url has begun, the current page may not have changed.
+     * Also, there may have been redirects resulting in a different url to that
+     * originally requested.
+     * @return The url that was originally requested for the current page.
+     */
+    public String getOriginalUrl() {
+        checkThread();
+        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+        return h != null ? h.getOriginalUrl() : null;
+    }
+
+    /**
+     * Get the title for the current page. This is the title of the current page
+     * until WebViewClient.onReceivedTitle is called.
+     * @return The title for the current page.
+     */
+    public String getTitle() {
+        checkThread();
+        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+        return h != null ? h.getTitle() : null;
+    }
+
+    /**
+     * Get the favicon for the current page. This is the favicon of the current
+     * page until WebViewClient.onReceivedIcon is called.
+     * @return The favicon for the current page.
+     */
+    public Bitmap getFavicon() {
+        checkThread();
+        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+        return h != null ? h.getFavicon() : null;
+    }
+
+    /**
+     * Get the touch icon url for the apple-touch-icon <link> element, or
+     * a URL on this site's server pointing to the standard location of a
+     * touch icon.
+     * @hide
+     */
+    public String getTouchIconUrl() {
+        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+        return h != null ? h.getTouchIconUrl() : null;
+    }
+
+    /**
+     * Get the progress for the current page.
+     * @return The progress for the current page between 0 and 100.
+     */
+    public int getProgress() {
+        checkThread();
+        return mCallbackProxy.getProgress();
+    }
+
+    /**
+     * @return the height of the HTML content.
+     */
+    public int getContentHeight() {
+        checkThread();
+        return mContentHeight;
+    }
+
+    /**
+     * @return the width of the HTML content.
+     * @hide
+     */
+    public int getContentWidth() {
+        return mContentWidth;
+    }
+
+    /**
+     * @hide
+     */
+    public int getPageBackgroundColor() {
+        return nativeGetBackgroundColor();
+    }
+
+    /**
+     * Pause all layout, parsing, and JavaScript timers for all webviews. This
+     * is a global requests, not restricted to just this webview. This can be
+     * useful if the application has been paused.
+     */
+    public void pauseTimers() {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
+    }
+
+    /**
+     * Resume all layout, parsing, and JavaScript timers for all webviews.
+     * This will resume dispatching all timers.
+     */
+    public void resumeTimers() {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
+    }
+
+    /**
+     * Call this to pause any extra processing associated with this WebView and
+     * its associated DOM, plugins, JavaScript etc. For example, if the WebView
+     * is taken offscreen, this could be called to reduce unnecessary CPU or
+     * network traffic. When the WebView is again "active", call onResume().
+     *
+     * Note that this differs from pauseTimers(), which affects all WebViews.
+     */
+    public void onPause() {
+        checkThread();
+        if (!mIsPaused) {
+            mIsPaused = true;
+            mWebViewCore.sendMessage(EventHub.ON_PAUSE);
+            // We want to pause the current playing video when switching out
+            // from the current WebView/tab.
+            if (mHTML5VideoViewProxy != null) {
+                mHTML5VideoViewProxy.pauseAndDispatch();
+            }
+            if (mNativeClass != 0) {
+                nativeSetPauseDrawing(mNativeClass, true);
+            }
+
+            cancelSelectDialog();
+            WebCoreThreadWatchdog.pause();
+        }
+    }
+
+    @Override
+    public void onWindowVisibilityChanged(int visibility) {
+        updateDrawingState();
+    }
+
+    void updateDrawingState() {
+        if (mNativeClass == 0 || mIsPaused) return;
+        if (mWebView.getWindowVisibility() != View.VISIBLE) {
+            nativeSetPauseDrawing(mNativeClass, true);
+        } else if (mWebView.getVisibility() != View.VISIBLE) {
+            nativeSetPauseDrawing(mNativeClass, true);
+        } else {
+            nativeSetPauseDrawing(mNativeClass, false);
+        }
+    }
+
+    /**
+     * Call this to resume a WebView after a previous call to onPause().
+     */
+    public void onResume() {
+        checkThread();
+        if (mIsPaused) {
+            mIsPaused = false;
+            mWebViewCore.sendMessage(EventHub.ON_RESUME);
+            if (mNativeClass != 0) {
+                nativeSetPauseDrawing(mNativeClass, false);
+            }
+        }
+        // Ensure that the watchdog has a currently valid Context to be able to display
+        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
+        // thread was blocked and the Activity is started again, we may reuse the blocked
+        // thread, but we'll have a new Activity.
+        WebCoreThreadWatchdog.updateContext(mContext);
+        // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
+        // to ensure that the Watchdog thread is running for the new WebView, so call
+        // it outside the if block above.
+        WebCoreThreadWatchdog.resume();
+    }
+
+    /**
+     * Returns true if the view is paused, meaning onPause() was called. Calling
+     * onResume() sets the paused state back to false.
+     * @hide
+     */
+    public boolean isPaused() {
+        return mIsPaused;
+    }
+
+    /**
+     * Call this to inform the view that memory is low so that it can
+     * free any available memory.
+     */
+    public void freeMemory() {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
+    }
+
+    /**
+     * Clear the resource cache. Note that the cache is per-application, so
+     * this will clear the cache for all WebViews used.
+     *
+     * @param includeDiskFiles If false, only the RAM cache is cleared.
+     */
+    public void clearCache(boolean includeDiskFiles) {
+        checkThread();
+        // Note: this really needs to be a static method as it clears cache for all
+        // WebView. But we need mWebViewCore to send message to WebCore thread, so
+        // we can't make this static.
+        mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
+                includeDiskFiles ? 1 : 0, 0);
+    }
+
+    /**
+     * Make sure that clearing the form data removes the adapter from the
+     * currently focused textfield if there is one.
+     */
+    public void clearFormData() {
+        checkThread();
+        if (inEditingMode()) {
+            mWebTextView.setAdapterCustom(null);
+        }
+    }
+
+    /**
+     * Tell the WebView to clear its internal back/forward list.
+     */
+    public void clearHistory() {
+        checkThread();
+        mCallbackProxy.getBackForwardList().setClearPending();
+        mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
+    }
+
+    /**
+     * Clear the SSL preferences table stored in response to proceeding with SSL
+     * certificate errors.
+     */
+    public void clearSslPreferences() {
+        checkThread();
+        mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
+    }
+
+    /**
+     * Return the WebBackForwardList for this WebView. This contains the
+     * back/forward list for use in querying each item in the history stack.
+     * This is a copy of the private WebBackForwardList so it contains only a
+     * snapshot of the current state. Multiple calls to this method may return
+     * different objects. The object returned from this method will not be
+     * updated to reflect any new state.
+     */
+    public WebBackForwardList copyBackForwardList() {
+        checkThread();
+        return mCallbackProxy.getBackForwardList().clone();
+    }
+
+    /*
+     * Highlight and scroll to the next occurance of String in findAll.
+     * Wraps the page infinitely, and scrolls.  Must be called after
+     * calling findAll.
+     *
+     * @param forward Direction to search.
+     */
+    public void findNext(boolean forward) {
+        checkThread();
+        if (0 == mNativeClass) return; // client isn't initialized
+        mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
+    }
+
+    /*
+     * Find all instances of find on the page and highlight them.
+     * @param find  String to find.
+     * @return int  The number of occurances of the String "find"
+     *              that were found.
+     */
+    public int findAll(String find) {
+        return findAllBody(find, false);
+    }
+
+    /**
+     * @hide
+     */
+    public void findAllAsync(String find) {
+        findAllBody(find, true);
+    }
+
+    private int findAllBody(String find, boolean isAsync) {
+        checkThread();
+        if (0 == mNativeClass) return 0; // client isn't initialized
+        mLastFind = find;
+        mWebViewCore.removeMessages(EventHub.FIND_ALL);
+        WebViewCore.FindAllRequest request = new
+            WebViewCore.FindAllRequest(find);
+        if (isAsync) {
+            mWebViewCore.sendMessage(EventHub.FIND_ALL, request);
+            return 0; // no need to wait for response
+        }
+        synchronized(request) {
+            try {
+                mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL,
+                    request);
+                while (request.mMatchCount == -1) {
+                    request.wait();
+                }
+            }
+            catch (InterruptedException e) {
+                return 0;
+            }
+        }
+        return request.mMatchCount;
+    }
+
+    /**
+     * Start an ActionMode for finding text in this WebView.  Only works if this
+     *              WebView is attached to the view system.
+     * @param text If non-null, will be the initial text to search for.
+     *             Otherwise, the last String searched for in this WebView will
+     *             be used to start.
+     * @param showIme If true, show the IME, assuming the user will begin typing.
+     *             If false and text is non-null, perform a find all.
+     * @return boolean True if the find dialog is shown, false otherwise.
+     */
+    public boolean showFindDialog(String text, boolean showIme) {
+        checkThread();
+        FindActionModeCallback callback = new FindActionModeCallback(mContext);
+        if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
+            // Could not start the action mode, so end Find on page
+            return false;
+        }
+        mCachedOverlappingActionModeHeight = -1;
+        mFindCallback = callback;
+        setFindIsUp(true);
+        mFindCallback.setWebView(this);
+        if (showIme) {
+            mFindCallback.showSoftInput();
+        } else if (text != null) {
+            mFindCallback.setText(text);
+            mFindCallback.findAll();
+            return true;
+        }
+        if (text == null) {
+            text = mLastFind;
+        }
+        if (text != null) {
+            mFindCallback.setText(text);
+            mFindCallback.findAll();
+        }
+        return true;
+    }
+
+    /**
+     * Keep track of the find callback so that we can remove its titlebar if
+     * necessary.
+     */
+    private FindActionModeCallback mFindCallback;
+
+    /**
+     * Toggle whether the find dialog is showing, for both native and Java.
+     */
+    private void setFindIsUp(boolean isUp) {
+        mFindIsUp = isUp;
+        if (0 == mNativeClass) return; // client isn't initialized
+        nativeSetFindIsUp(isUp);
+    }
+
+    // Used to know whether the find dialog is open.  Affects whether
+    // or not we draw the highlights for matches.
+    private boolean mFindIsUp;
+
+    // Keep track of the last string sent, so we can search again when find is
+    // reopened.
+    private String mLastFind;
+
+    /**
+     * Return the first substring consisting of the address of a physical
+     * location. Currently, only addresses in the United States are detected,
+     * and consist of:
+     * - a house number
+     * - a street name
+     * - a street type (Road, Circle, etc), either spelled out or abbreviated
+     * - a city name
+     * - a state or territory, either spelled out or two-letter abbr.
+     * - an optional 5 digit or 9 digit zip code.
+     *
+     * All names must be correctly capitalized, and the zip code, if present,
+     * must be valid for the state. The street type must be a standard USPS
+     * spelling or abbreviation. The state or territory must also be spelled
+     * or abbreviated using USPS standards. The house number may not exceed
+     * five digits.
+     * @param addr The string to search for addresses.
+     *
+     * @return the address, or if no address is found, return null.
+     */
+    public static String findAddress(String addr) {
+        checkThread();
+        return findAddress(addr, false);
+    }
+
+    /**
+     * @hide
+     * Return the first substring consisting of the address of a physical
+     * location. Currently, only addresses in the United States are detected,
+     * and consist of:
+     * - a house number
+     * - a street name
+     * - a street type (Road, Circle, etc), either spelled out or abbreviated
+     * - a city name
+     * - a state or territory, either spelled out or two-letter abbr.
+     * - an optional 5 digit or 9 digit zip code.
+     *
+     * Names are optionally capitalized, and the zip code, if present,
+     * must be valid for the state. The street type must be a standard USPS
+     * spelling or abbreviation. The state or territory must also be spelled
+     * or abbreviated using USPS standards. The house number may not exceed
+     * five digits.
+     * @param addr The string to search for addresses.
+     * @param caseInsensitive addr Set to true to make search ignore case.
+     *
+     * @return the address, or if no address is found, return null.
+     */
+    public static String findAddress(String addr, boolean caseInsensitive) {
+        return WebViewCore.nativeFindAddress(addr, caseInsensitive);
+    }
+
+    /*
+     * Clear the highlighting surrounding text matches created by findAll.
+     */
+    public void clearMatches() {
+        checkThread();
+        if (mNativeClass == 0)
+            return;
+        mWebViewCore.removeMessages(EventHub.FIND_ALL);
+        mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
+    }
+
+
+    /**
+     * Called when the find ActionMode ends.
+     */
+    void notifyFindDialogDismissed() {
+        mFindCallback = null;
+        mCachedOverlappingActionModeHeight = -1;
+        if (mWebViewCore == null) {
+            return;
+        }
+        clearMatches();
+        setFindIsUp(false);
+        // Now that the dialog has been removed, ensure that we scroll to a
+        // location that is not beyond the end of the page.
+        pinScrollTo(getScrollX(), getScrollY(), false, 0);
+        invalidate();
+    }
+
+    /**
+     * Query the document to see if it contains any image references. The
+     * message object will be dispatched with arg1 being set to 1 if images
+     * were found and 0 if the document does not reference any images.
+     * @param response The message that will be dispatched with the result.
+     */
+    public void documentHasImages(Message response) {
+        checkThread();
+        if (response == null) {
+            return;
+        }
+        mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
+    }
+
+    /**
+     * Request the scroller to abort any ongoing animation
+     *
+     * @hide
+     */
+    public void stopScroll() {
+        mScroller.forceFinished(true);
+        mLastVelocity = 0;
+    }
+
+    @Override
+    public void computeScroll() {
+        if (mScroller.computeScrollOffset()) {
+            int oldX = getScrollX();
+            int oldY = getScrollY();
+            int x = mScroller.getCurrX();
+            int y = mScroller.getCurrY();
+            invalidate();  // So we draw again
+
+            if (!mScroller.isFinished()) {
+                int rangeX = computeMaxScrollX();
+                int rangeY = computeMaxScrollY();
+                int overflingDistance = mOverflingDistance;
+
+                // Use the layer's scroll data if needed.
+                if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+                    oldX = mScrollingLayerRect.left;
+                    oldY = mScrollingLayerRect.top;
+                    rangeX = mScrollingLayerRect.right;
+                    rangeY = mScrollingLayerRect.bottom;
+                    // No overscrolling for layers.
+                    overflingDistance = 0;
+                }
+
+                mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
+                        rangeX, rangeY,
+                        overflingDistance, overflingDistance, false);
+
+                if (mOverScrollGlow != null) {
+                    mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
+                }
+            } else {
+                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+                    setScrollXRaw(x);
+                    setScrollYRaw(y);
+                } else {
+                    // Update the layer position instead of WebView.
+                    scrollLayerTo(x, y);
+                }
+                abortAnimation();
+                nativeSetIsScrolling(false);
+                if (!mBlockWebkitViewMessages) {
+                    WebViewCore.resumePriority();
+                    if (!mSelectingText) {
+                        WebViewCore.resumeUpdatePicture(mWebViewCore);
+                    }
+                }
+                if (oldX != getScrollX() || oldY != getScrollY()) {
+                    sendOurVisibleRect();
+                }
+            }
+        } else {
+            mWebViewPrivate.super_computeScroll();
+        }
+    }
+
+    private void scrollLayerTo(int x, int y) {
+        if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) {
+            return;
+        }
+        if (mSelectingText) {
+            int dx = mScrollingLayerRect.left - x;
+            int dy = mScrollingLayerRect.top - y;
+            if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
+                mSelectCursorBase.offset(dx, dy);
+            }
+            if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
+                mSelectCursorExtent.offset(dx, dy);
+            }
+        }
+        nativeScrollLayer(mCurrentScrollingLayerId, x, y);
+        mScrollingLayerRect.left = x;
+        mScrollingLayerRect.top = y;
+        mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
+                mScrollingLayerRect);
+        mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
+        invalidate();
+    }
+
+    private static int computeDuration(int dx, int dy) {
+        int distance = Math.max(Math.abs(dx), Math.abs(dy));
+        int duration = distance * 1000 / STD_SPEED;
+        return Math.min(duration, MAX_DURATION);
+    }
+
+    // helper to pin the scrollBy parameters (already in view coordinates)
+    // returns true if the scroll was changed
+    private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
+        return pinScrollTo(getScrollX() + dx, getScrollY() + dy, animate, animationDuration);
+    }
+    // helper to pin the scrollTo parameters (already in view coordinates)
+    // returns true if the scroll was changed
+    private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
+        x = pinLocX(x);
+        y = pinLocY(y);
+        int dx = x - getScrollX();
+        int dy = y - getScrollY();
+
+        if ((dx | dy) == 0) {
+            return false;
+        }
+        abortAnimation();
+        if (animate) {
+            //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
+            mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
+                    animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
+            mWebViewPrivate.awakenScrollBars(mScroller.getDuration());
+            invalidate();
+        } else {
+            mWebView.scrollTo(x, y);
+        }
+        return true;
+    }
+
+    // Scale from content to view coordinates, and pin.
+    // Also called by jni webview.cpp
+    private boolean setContentScrollBy(int cx, int cy, boolean animate) {
+        if (mDrawHistory) {
+            // disallow WebView to change the scroll position as History Picture
+            // is used in the view system.
+            // TODO: as we switchOutDrawHistory when trackball or navigation
+            // keys are hit, this should be safe. Right?
+            return false;
+        }
+        cx = contentToViewDimension(cx);
+        cy = contentToViewDimension(cy);
+        if (mHeightCanMeasure) {
+            // move our visible rect according to scroll request
+            if (cy != 0) {
+                Rect tempRect = new Rect();
+                calcOurVisibleRect(tempRect);
+                tempRect.offset(cx, cy);
+                mWebView.requestRectangleOnScreen(tempRect);
+            }
+            // FIXME: We scroll horizontally no matter what because currently
+            // ScrollView and ListView will not scroll horizontally.
+            // FIXME: Why do we only scroll horizontally if there is no
+            // vertical scroll?
+//                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
+            return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
+        } else {
+            return pinScrollBy(cx, cy, animate, 0);
+        }
+    }
+
+    /**
+     * Called by CallbackProxy when the page starts loading.
+     * @param url The URL of the page which has started loading.
+     */
+    /* package */ void onPageStarted(String url) {
+        // every time we start a new page, we want to reset the
+        // WebView certificate:  if the new site is secure, we
+        // will reload it and get a new certificate set;
+        // if the new site is not secure, the certificate must be
+        // null, and that will be the case
+        mWebView.setCertificate(null);
+
+        // reset the flag since we set to true in if need after
+        // loading is see onPageFinished(Url)
+        mAccessibilityScriptInjected = false;
+    }
+
+    /**
+     * Called by CallbackProxy when the page finishes loading.
+     * @param url The URL of the page which has finished loading.
+     */
+    /* package */ void onPageFinished(String url) {
+        if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
+            // If the user is now on a different page, or has scrolled the page
+            // past the point where the title bar is offscreen, ignore the
+            // scroll request.
+            if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
+                    && getScrollX() == 0 && getScrollY() == 0) {
+                pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
+                        SLIDE_TITLE_DURATION);
+            }
+            mPageThatNeedsToSlideTitleBarOffScreen = null;
+        }
+        mZoomManager.onPageFinished(url);
+        injectAccessibilityForUrl(url);
+    }
+
+    /**
+     * This method injects accessibility in the loaded document if accessibility
+     * is enabled. If JavaScript is enabled we try to inject a URL specific script.
+     * If no URL specific script is found or JavaScript is disabled we fallback to
+     * the default {@link AccessibilityInjector} implementation.
+     * </p>
+     * If the URL has the "axs" paramter set to 1 it has already done the
+     * script injection so we do nothing. If the parameter is set to 0
+     * the URL opts out accessibility script injection so we fall back to
+     * the default {@link AccessibilityInjector}.
+     * </p>
+     * Note: If the user has not opted-in the accessibility script injection no scripts
+     * are injected rather the default {@link AccessibilityInjector} implementation
+     * is used.
+     *
+     * @param url The URL loaded by this {@link WebView}.
+     */
+    private void injectAccessibilityForUrl(String url) {
+        if (mWebViewCore == null) {
+            return;
+        }
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+
+        if (!accessibilityManager.isEnabled()) {
+            // it is possible that accessibility was turned off between reloads
+            ensureAccessibilityScriptInjectorInstance(false);
+            return;
+        }
+
+        if (!getSettings().getJavaScriptEnabled()) {
+            // no JS so we fallback to the basic buil-in support
+            ensureAccessibilityScriptInjectorInstance(true);
+            return;
+        }
+
+        // check the URL "axs" parameter to choose appropriate action
+        int axsParameterValue = getAxsUrlParameterValue(url);
+        if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
+            boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
+                    .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
+            if (onDeviceScriptInjectionEnabled) {
+                ensureAccessibilityScriptInjectorInstance(false);
+                // neither script injected nor script injection opted out => we inject
+                mWebView.loadUrl(getScreenReaderInjectingJs());
+                // TODO: Set this flag after successfull script injection. Maybe upon injection
+                // the chooser should update the meta tag and we check it to declare success
+                mAccessibilityScriptInjected = true;
+            } else {
+                // injection disabled so we fallback to the basic built-in support
+                ensureAccessibilityScriptInjectorInstance(true);
+            }
+        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
+            // injection opted out so we fallback to the basic buil-in support
+            ensureAccessibilityScriptInjectorInstance(true);
+        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
+            ensureAccessibilityScriptInjectorInstance(false);
+            // the URL provides accessibility but we still need to add our generic script
+            mWebView.loadUrl(getScreenReaderInjectingJs());
+        } else {
+            Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
+        }
+    }
+
+    /**
+     * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
+     *
+     * @param present True to ensure an insance, false to ensure no instance.
+     */
+    private void ensureAccessibilityScriptInjectorInstance(boolean present) {
+        if (present) {
+            if (mAccessibilityInjector == null) {
+                mAccessibilityInjector = new AccessibilityInjector(this);
+            }
+        } else {
+            mAccessibilityInjector = null;
+        }
+    }
+
+    /**
+     * Gets JavaScript that injects a screen-reader.
+     *
+     * @return The JavaScript snippet.
+     */
+    private String getScreenReaderInjectingJs() {
+        String screenReaderUrl = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
+        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
+    }
+
+    /**
+     * Gets the "axs" URL parameter value.
+     *
+     * @param url A url to fetch the paramter from.
+     * @return The parameter value if such, -1 otherwise.
+     */
+    private int getAxsUrlParameterValue(String url) {
+        if (mMatchAxsUrlParameterPattern == null) {
+            mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
+        }
+        Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
+        if (matcher.find()) {
+            String keyValuePair = url.substring(matcher.start(), matcher.end());
+            return Integer.parseInt(keyValuePair.split("=")[1]);
+        }
+        return -1;
+    }
+
+    /**
+     * The URL of a page that sent a message to scroll the title bar off screen.
+     *
+     * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
+     * title bar off the screen.  Sometimes, the scroll position is set before
+     * the page finishes loading.  Rather than scrolling while the page is still
+     * loading, keep track of the URL and new scroll position so we can perform
+     * the scroll once the page finishes loading.
+     */
+    private String mPageThatNeedsToSlideTitleBarOffScreen;
+
+    /**
+     * The destination Y scroll position to be used when the page finishes
+     * loading.  See mPageThatNeedsToSlideTitleBarOffScreen.
+     */
+    private int mYDistanceToSlideTitleOffScreen;
+
+    // scale from content to view coordinates, and pin
+    // return true if pin caused the final x/y different than the request cx/cy,
+    // and a future scroll may reach the request cx/cy after our size has
+    // changed
+    // return false if the view scroll to the exact position as it is requested,
+    // where negative numbers are taken to mean 0
+    private boolean setContentScrollTo(int cx, int cy) {
+        if (mDrawHistory) {
+            // disallow WebView to change the scroll position as History Picture
+            // is used in the view system.
+            // One known case where this is called is that WebCore tries to
+            // restore the scroll position. As history Picture already uses the
+            // saved scroll position, it is ok to skip this.
+            return false;
+        }
+        int vx;
+        int vy;
+        if ((cx | cy) == 0) {
+            // If the page is being scrolled to (0,0), do not add in the title
+            // bar's height, and simply scroll to (0,0). (The only other work
+            // in contentToView_ is to multiply, so this would not change 0.)
+            vx = 0;
+            vy = 0;
+        } else {
+            vx = contentToViewX(cx);
+            vy = contentToViewY(cy);
+        }
+//        Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
+//                      vx + " " + vy + "]");
+        // Some mobile sites attempt to scroll the title bar off the page by
+        // scrolling to (0,1).  If we are at the top left corner of the
+        // page, assume this is an attempt to scroll off the title bar, and
+        // animate the title bar off screen slowly enough that the user can see
+        // it.
+        if (cx == 0 && cy == 1 && getScrollX() == 0 && getScrollY() == 0
+                && mTitleBar != null) {
+            // FIXME: 100 should be defined somewhere as our max progress.
+            if (getProgress() < 100) {
+                // Wait to scroll the title bar off screen until the page has
+                // finished loading.  Keep track of the URL and the destination
+                // Y position
+                mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
+                mYDistanceToSlideTitleOffScreen = vy;
+            } else {
+                pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
+            }
+            // Since we are animating, we have not yet reached the desired
+            // scroll position.  Do not return true to request another attempt
+            return false;
+        }
+        pinScrollTo(vx, vy, false, 0);
+        // If the request was to scroll to a negative coordinate, treat it as if
+        // it was a request to scroll to 0
+        if ((getScrollX() != vx && cx >= 0) || (getScrollY() != vy && cy >= 0)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // scale from content to view coordinates, and pin
+    private void spawnContentScrollTo(int cx, int cy) {
+        if (mDrawHistory) {
+            // disallow WebView to change the scroll position as History Picture
+            // is used in the view system.
+            return;
+        }
+        int vx = contentToViewX(cx);
+        int vy = contentToViewY(cy);
+        pinScrollTo(vx, vy, true, 0);
+    }
+
+    /**
+     * These are from webkit, and are in content coordinate system (unzoomed)
+     */
+    private void contentSizeChanged(boolean updateLayout) {
+        // suppress 0,0 since we usually see real dimensions soon after
+        // this avoids drawing the prev content in a funny place. If we find a
+        // way to consolidate these notifications, this check may become
+        // obsolete
+        if ((mContentWidth | mContentHeight) == 0) {
+            return;
+        }
+
+        if (mHeightCanMeasure) {
+            if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight)
+                    || updateLayout) {
+                mWebView.requestLayout();
+            }
+        } else if (mWidthCanMeasure) {
+            if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth)
+                    || updateLayout) {
+                mWebView.requestLayout();
+            }
+        } else {
+            // If we don't request a layout, try to send our view size to the
+            // native side to ensure that WebCore has the correct dimensions.
+            sendViewSizeZoom(false);
+        }
+    }
+
+    /**
+     * Set the WebViewClient that will receive various notifications and
+     * requests. This will replace the current handler.
+     * @param client An implementation of WebViewClient.
+     */
+    public void setWebViewClient(WebViewClient client) {
+        checkThread();
+        mCallbackProxy.setWebViewClient(client);
+    }
+
+    /**
+     * Gets the WebViewClient
+     * @return the current WebViewClient instance.
+     *
+     * @hide This is an implementation detail.
+     */
+    public WebViewClient getWebViewClient() {
+        return mCallbackProxy.getWebViewClient();
+    }
+
+    /**
+     * Register the interface to be used when content can not be handled by
+     * the rendering engine, and should be downloaded instead. This will replace
+     * the current handler.
+     * @param listener An implementation of DownloadListener.
+     */
+    public void setDownloadListener(DownloadListener listener) {
+        checkThread();
+        mCallbackProxy.setDownloadListener(listener);
+    }
+
+    /**
+     * Set the chrome handler. This is an implementation of WebChromeClient for
+     * use in handling JavaScript dialogs, favicons, titles, and the progress.
+     * This will replace the current handler.
+     * @param client An implementation of WebChromeClient.
+     */
+    public void setWebChromeClient(WebChromeClient client) {
+        checkThread();
+        mCallbackProxy.setWebChromeClient(client);
+    }
+
+    /**
+     * Gets the chrome handler.
+     * @return the current WebChromeClient instance.
+     *
+     * @hide This is an implementation detail.
+     */
+    public WebChromeClient getWebChromeClient() {
+        return mCallbackProxy.getWebChromeClient();
+    }
+
+    /**
+     * Set the back/forward list client. This is an implementation of
+     * WebBackForwardListClient for handling new items and changes in the
+     * history index.
+     * @param client An implementation of WebBackForwardListClient.
+     * {@hide}
+     */
+    public void setWebBackForwardListClient(WebBackForwardListClient client) {
+        mCallbackProxy.setWebBackForwardListClient(client);
+    }
+
+    /**
+     * Gets the WebBackForwardListClient.
+     * {@hide}
+     */
+    public WebBackForwardListClient getWebBackForwardListClient() {
+        return mCallbackProxy.getWebBackForwardListClient();
+    }
+
+    /**
+     * Set the Picture listener. This is an interface used to receive
+     * notifications of a new Picture.
+     * @param listener An implementation of WebView.PictureListener.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public void setPictureListener(PictureListener listener) {
+        checkThread();
+        mPictureListener = listener;
+    }
+
+    /**
+     * {@hide}
+     */
+    /* FIXME: Debug only! Remove for SDK! */
+    public void externalRepresentation(Message callback) {
+        mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
+    }
+
+    /**
+     * {@hide}
+     */
+    /* FIXME: Debug only! Remove for SDK! */
+    public void documentAsText(Message callback) {
+        mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
+    }
+
+    /**
+     * This method injects the supplied Java object into the WebView. The
+     * object is injected into the JavaScript context of the main frame, using
+     * the supplied name. This allows the Java object to be accessed from
+     * JavaScript. Note that that injected objects will not appear in
+     * JavaScript until the page is next (re)loaded. For example:
+     * <pre> webView.addJavascriptInterface(new Object(), "injectedObject");
+     * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
+     * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
+     * <p><strong>IMPORTANT:</strong>
+     * <ul>
+     * <li> addJavascriptInterface() can be used to allow JavaScript to control
+     * the host application. This is a powerful feature, but also presents a
+     * security risk. Use of this method in a WebView containing untrusted
+     * content could allow an attacker to manipulate the host application in
+     * unintended ways, executing Java code with the permissions of the host
+     * application. Use extreme care when using this method in a WebView which
+     * could contain untrusted content.
+     * <li> JavaScript interacts with Java object on a private, background
+     * thread of the WebView. Care is therefore required to maintain thread
+     * safety.</li>
+     * </ul></p>
+     * @param object The Java object to inject into the WebView's JavaScript
+     *               context. Null values are ignored.
+     * @param name The name used to expose the instance in JavaScript.
+     */
+    public void addJavascriptInterface(Object object, String name) {
+        checkThread();
+        if (object == null) {
+            return;
+        }
+        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+        arg.mObject = object;
+        arg.mInterfaceName = name;
+        mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
+    }
+
+    /**
+     * Removes a previously added JavaScript interface with the given name.
+     * @param interfaceName The name of the interface to remove.
+     */
+    public void removeJavascriptInterface(String interfaceName) {
+        checkThread();
+        if (mWebViewCore != null) {
+            WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+            arg.mInterfaceName = interfaceName;
+            mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
+        }
+    }
+
+    /**
+     * Return the WebSettings object used to control the settings for this
+     * WebView.
+     * @return A WebSettings object that can be used to control this WebView's
+     *         settings.
+     */
+    public WebSettingsClassic getSettings() {
+        checkThread();
+        return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
+    }
+
+   /**
+    * Return the list of currently loaded plugins.
+    * @return The list of currently loaded plugins.
+    *
+    * @hide
+    * @deprecated This was used for Gears, which has been deprecated.
+    */
+    @Deprecated
+    public static synchronized PluginList getPluginList() {
+        checkThread();
+        return new PluginList();
+    }
+
+   /**
+    * @hide
+    * @deprecated This was used for Gears, which has been deprecated.
+    */
+    @Deprecated
+    public void refreshPlugins(boolean reloadOpenPages) {
+        checkThread();
+    }
+
+    //-------------------------------------------------------------------------
+    // Override View methods
+    //-------------------------------------------------------------------------
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mNativeClass != 0) {
+                mPrivateHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        destroy();
+                    }
+                });
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        if (child == mTitleBar) {
+            // When drawing the title bar, move it horizontally to always show
+            // at the top of the WebView.
+            mTitleBar.offsetLeftAndRight(getScrollX() - mTitleBar.getLeft());
+            int newTop = 0;
+            if (mTitleGravity == Gravity.NO_GRAVITY) {
+                newTop = Math.min(0, getScrollY());
+            } else if (mTitleGravity == Gravity.TOP) {
+                newTop = getScrollY();
+            }
+            mTitleBar.setBottom(newTop + mTitleBar.getHeight());
+            mTitleBar.setTop(newTop);
+        }
+        return false;  // We never call invalidate(), so unconditionally returning false.
+    }
+
+    private void drawContent(Canvas canvas, boolean drawRings) {
+        drawCoreAndCursorRing(canvas, mBackgroundColor,
+                mDrawCursorRing && drawRings);
+    }
+
+    /**
+     * Draw the background when beyond bounds
+     * @param canvas Canvas to draw into
+     */
+    private void drawOverScrollBackground(Canvas canvas) {
+        if (mOverScrollBackground == null) {
+            mOverScrollBackground = new Paint();
+            Bitmap bm = BitmapFactory.decodeResource(
+                    mContext.getResources(),
+                    com.android.internal.R.drawable.status_bar_background);
+            mOverScrollBackground.setShader(new BitmapShader(bm,
+                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+            mOverScrollBorder = new Paint();
+            mOverScrollBorder.setStyle(Paint.Style.STROKE);
+            mOverScrollBorder.setStrokeWidth(0);
+            mOverScrollBorder.setColor(0xffbbbbbb);
+        }
+
+        int top = 0;
+        int right = computeRealHorizontalScrollRange();
+        int bottom = top + computeRealVerticalScrollRange();
+        // first draw the background and anchor to the top of the view
+        canvas.save();
+        canvas.translate(getScrollX(), getScrollY());
+        canvas.clipRect(-getScrollX(), top - getScrollY(), right - getScrollX(), bottom
+                - getScrollY(), Region.Op.DIFFERENCE);
+        canvas.drawPaint(mOverScrollBackground);
+        canvas.restore();
+        // then draw the border
+        canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
+        // next clip the region for the content
+        canvas.clipRect(0, top, right, bottom);
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (inFullScreenMode()) {
+            return; // no need to draw anything if we aren't visible.
+        }
+        // if mNativeClass is 0, the WebView is either destroyed or not
+        // initialized. In either case, just draw the background color and return
+        if (mNativeClass == 0) {
+            canvas.drawColor(mBackgroundColor);
+            return;
+        }
+
+        // if both mContentWidth and mContentHeight are 0, it means there is no
+        // valid Picture passed to WebView yet. This can happen when WebView
+        // just starts. Draw the background and return.
+        if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
+            canvas.drawColor(mBackgroundColor);
+            return;
+        }
+
+        if (canvas.isHardwareAccelerated()) {
+            mZoomManager.setHardwareAccelerated();
+        } else {
+            mWebViewCore.resumeWebKitDraw();
+        }
+
+        int saveCount = canvas.save();
+        if (mInOverScrollMode && !getSettings()
+                .getUseWebViewBackgroundForOverscrollBackground()) {
+            drawOverScrollBackground(canvas);
+        }
+        if (mTitleBar != null) {
+            canvas.translate(0, getTitleHeight());
+        }
+        boolean drawNativeRings = !sDisableNavcache;
+        drawContent(canvas, drawNativeRings);
+        canvas.restoreToCount(saveCount);
+
+        if (AUTO_REDRAW_HACK && mAutoRedraw) {
+            invalidate();
+        }
+        mWebViewCore.signalRepaintDone();
+
+        if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
+            invalidate();
+        }
+
+        if (mFocusTransition != null) {
+            mFocusTransition.draw(canvas);
+        } else if (shouldDrawHighlightRect()) {
+            RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
+            Rect r = new Rect();
+            while (iter.next(r)) {
+                canvas.drawRect(r, mTouchHightlightPaint);
+            }
+        }
+        if (DEBUG_TOUCH_HIGHLIGHT) {
+            if (getSettings().getNavDump()) {
+                if ((mTouchHighlightX | mTouchHighlightY) != 0) {
+                    if (mTouchCrossHairColor == null) {
+                        mTouchCrossHairColor = new Paint();
+                        mTouchCrossHairColor.setColor(Color.RED);
+                    }
+                    canvas.drawLine(mTouchHighlightX - mNavSlop,
+                            mTouchHighlightY - mNavSlop, mTouchHighlightX
+                                    + mNavSlop + 1, mTouchHighlightY + mNavSlop
+                                    + 1, mTouchCrossHairColor);
+                    canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
+                            mTouchHighlightY - mNavSlop, mTouchHighlightX
+                                    - mNavSlop,
+                            mTouchHighlightY + mNavSlop + 1,
+                            mTouchCrossHairColor);
+                }
+            }
+        }
+    }
+
+    private void removeTouchHighlight() {
+        mWebViewCore.removeMessages(EventHub.HIT_TEST);
+        mPrivateHandler.removeMessages(HIT_TEST_RESULT);
+        setTouchHighlightRects(null);
+    }
+
+    @Override
+    public void setLayoutParams(ViewGroup.LayoutParams params) {
+        if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) {
+            mWrapContent = true;
+        }
+        mWebViewPrivate.super_setLayoutParams(params);
+    }
+
+    @Override
+    public boolean performLongClick() {
+        // performLongClick() is the result of a delayed message. If we switch
+        // to windows overview, the WebView will be temporarily removed from the
+        // view system. In that case, do nothing.
+        if (mWebView.getParent() == null) return false;
+
+        // A multi-finger gesture can look like a long press; make sure we don't take
+        // long press actions if we're scaling.
+        final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+        if (detector != null && detector.isInProgress()) {
+            return false;
+        }
+
+        if (mNativeClass != 0 && nativeCursorIsTextInput()) {
+            // Send the click so that the textfield is in focus
+            centerKeyPressOnTextField();
+            rebuildWebTextView();
+        } else {
+            clearTextEntry();
+        }
+        if (inEditingMode()) {
+            // Since we just called rebuildWebTextView, the layout is not set
+            // properly.  Update it so it can correctly find the word to select.
+            mWebTextView.ensureLayout();
+            // Provide a touch down event to WebTextView, which will allow it
+            // to store the location to use in performLongClick.
+            AbsoluteLayout.LayoutParams params
+                    = (AbsoluteLayout.LayoutParams) mWebTextView.getLayoutParams();
+            MotionEvent fake = MotionEvent.obtain(mLastTouchTime,
+                    mLastTouchTime, MotionEvent.ACTION_DOWN,
+                    mLastTouchX - params.x + getScrollX(),
+                    mLastTouchY - params.y + getScrollY(), 0);
+            mWebTextView.dispatchTouchEvent(fake);
+            return mWebTextView.performLongClick();
+        }
+        if (mSelectingText) return false; // long click does nothing on selection
+        /* if long click brings up a context menu, the super function
+         * returns true and we're done. Otherwise, nothing happened when
+         * the user clicked. */
+        if (mWebViewPrivate.super_performLongClick()) {
+            return true;
+        }
+        /* In the case where the application hasn't already handled the long
+         * click action, look for a word under the  click. If one is found,
+         * animate the text selection into view.
+         * FIXME: no animation code yet */
+        final boolean isSelecting = selectText();
+        if (isSelecting) {
+            mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        } else if (focusCandidateIsEditableText()) {
+            mSelectCallback = new SelectActionModeCallback();
+            mSelectCallback.setWebView(this);
+            mSelectCallback.setTextSelected(false);
+            mWebView.startActionMode(mSelectCallback);
+        }
+        return isSelecting;
+    }
+
+    /**
+     * Select the word at the last click point.
+     *
+     * @hide This is an implementation detail.
+     */
+    public boolean selectText() {
+        int x = viewToContentX(mLastTouchX + getScrollX());
+        int y = viewToContentY(mLastTouchY + getScrollY());
+        return selectText(x, y);
+    }
+
+    /**
+     * Select the word at the indicated content coordinates.
+     */
+    boolean selectText(int x, int y) {
+        mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
+        return true;
+    }
+
+    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        mCachedOverlappingActionModeHeight = -1;
+        if (mSelectingText && mOrientation != newConfig.orientation) {
+            selectionDone();
+        }
+        mOrientation = newConfig.orientation;
+        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
+            mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
+        }
+    }
+
+    /**
+     * Keep track of the Callback so we can end its ActionMode or remove its
+     * titlebar.
+     */
+    private SelectActionModeCallback mSelectCallback;
+
+    // These values are possible options for didUpdateWebTextViewDimensions.
+    private static final int FULLY_ON_SCREEN = 0;
+    private static final int INTERSECTS_SCREEN = 1;
+    private static final int ANYWHERE = 2;
+
+    /**
+     * Check to see if the focused textfield/textarea is still on screen.  If it
+     * is, update the the dimensions and location of WebTextView.  Otherwise,
+     * remove the WebTextView.  Should be called when the zoom level changes.
+     * @param intersection How to determine whether the textfield/textarea is
+     *        still on screen.
+     * @return boolean True if the textfield/textarea is still on screen and the
+     *         dimensions/location of WebTextView have been updated.
+     */
+    private boolean didUpdateWebTextViewDimensions(int intersection) {
+        Rect contentBounds = nativeFocusCandidateNodeBounds();
+        Rect vBox = contentToViewRect(contentBounds);
+        Rect visibleRect = new Rect();
+        calcOurVisibleRect(visibleRect);
+        offsetByLayerScrollPosition(vBox);
+        // If the textfield is on screen, place the WebTextView in
+        // its new place, accounting for our new scroll/zoom values,
+        // and adjust its textsize.
+        boolean onScreen;
+        switch (intersection) {
+            case FULLY_ON_SCREEN:
+                onScreen = visibleRect.contains(vBox);
+                break;
+            case INTERSECTS_SCREEN:
+                onScreen = Rect.intersects(visibleRect, vBox);
+                break;
+            case ANYWHERE:
+                onScreen = true;
+                break;
+            default:
+                throw new AssertionError(
+                        "invalid parameter passed to didUpdateWebTextViewDimensions");
+        }
+        if (onScreen) {
+            mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+                    vBox.height());
+            mWebTextView.updateTextSize();
+            updateWebTextViewPadding();
+            return true;
+        } else {
+            // The textfield is now off screen.  The user probably
+            // was not zooming to see the textfield better.  Remove
+            // the WebTextView.  If the user types a key, and the
+            // textfield is still in focus, we will reconstruct
+            // the WebTextView and scroll it back on screen.
+            mWebTextView.remove();
+            return false;
+        }
+    }
+
+    private void offsetByLayerScrollPosition(Rect box) {
+        if ((mCurrentScrollingLayerId != 0)
+                && (mCurrentScrollingLayerId == nativeFocusCandidateLayerId())) {
+            box.offsetTo(box.left - mScrollingLayerRect.left,
+                    box.top - mScrollingLayerRect.top);
+        }
+    }
+
+    void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator,
+            boolean isPictureAfterFirstLayout) {
+        if (mNativeClass == 0)
+            return;
+        boolean queueFull;
+        queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
+                                       showVisualIndicator, isPictureAfterFirstLayout);
+
+        if (layer == 0 || isPictureAfterFirstLayout) {
+            mWebViewCore.resumeWebKitDraw();
+        } else if (queueFull) {
+            // temporarily disable webkit draw throttling
+            // TODO: re-enable
+            // mWebViewCore.pauseWebKitDraw();
+        }
+
+        if (mHTML5VideoViewProxy != null) {
+            mHTML5VideoViewProxy.setBaseLayer(layer);
+        }
+    }
+
+    int getBaseLayer() {
+        if (mNativeClass == 0) {
+            return 0;
+        }
+        return nativeGetBaseLayer();
+    }
+
+    private void onZoomAnimationStart() {
+        // If it is in password mode, turn it off so it does not draw misplaced.
+        if (inEditingMode()) {
+            mWebTextView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    private void onZoomAnimationEnd() {
+        // adjust the edit text view if needed
+        if (inEditingMode()
+                && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)) {
+            // If it is a password field, start drawing the WebTextView once
+            // again.
+            mWebTextView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    void onFixedLengthZoomAnimationStart() {
+        WebViewCore.pauseUpdatePicture(getWebViewCore());
+        onZoomAnimationStart();
+    }
+
+    void onFixedLengthZoomAnimationEnd() {
+        if (!mBlockWebkitViewMessages && !mSelectingText) {
+            WebViewCore.resumeUpdatePicture(mWebViewCore);
+        }
+        onZoomAnimationEnd();
+    }
+
+    private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
+                                         Paint.DITHER_FLAG |
+                                         Paint.SUBPIXEL_TEXT_FLAG;
+    private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
+                                           Paint.DITHER_FLAG;
+
+    private final DrawFilter mZoomFilter =
+            new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
+    // If we need to trade better quality for speed, set mScrollFilter to null
+    private final DrawFilter mScrollFilter =
+            new PaintFlagsDrawFilter(SCROLL_BITS, 0);
+
+    private void drawCoreAndCursorRing(Canvas canvas, int color,
+        boolean drawCursorRing) {
+        if (mDrawHistory) {
+            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
+            canvas.drawPicture(mHistoryPicture);
+            return;
+        }
+        if (mNativeClass == 0) return;
+
+        boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
+        boolean animateScroll = ((!mScroller.isFinished()
+                || mVelocityTracker != null)
+                && (mTouchMode != TOUCH_DRAG_MODE ||
+                mHeldMotionless != MOTIONLESS_TRUE))
+                || mDeferTouchMode == TOUCH_DRAG_MODE;
+        if (mTouchMode == TOUCH_DRAG_MODE) {
+            if (mHeldMotionless == MOTIONLESS_PENDING) {
+                mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+                mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+                mHeldMotionless = MOTIONLESS_FALSE;
+            }
+            if (mHeldMotionless == MOTIONLESS_FALSE) {
+                mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                        .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
+                mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                        .obtainMessage(AWAKEN_SCROLL_BARS),
+                            ViewConfiguration.getScrollDefaultDelay());
+                mHeldMotionless = MOTIONLESS_PENDING;
+            }
+        }
+        int saveCount = canvas.save();
+        if (animateZoom) {
+            mZoomManager.animateZoom(canvas);
+        } else if (!canvas.isHardwareAccelerated()) {
+            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
+        }
+
+        boolean UIAnimationsRunning = false;
+        // Currently for each draw we compute the animation values;
+        // We may in the future decide to do that independently.
+        if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
+                && nativeEvaluateLayersAnimations(mNativeClass)) {
+            UIAnimationsRunning = true;
+            // If we have unfinished (or unstarted) animations,
+            // we ask for a repaint. We only need to do this in software
+            // rendering (with hardware rendering we already have a different
+            // method of requesting a repaint)
+            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
+            invalidate();
+        }
+
+        // decide which adornments to draw
+        int extras = DRAW_EXTRAS_NONE;
+        if (!mFindIsUp) {
+            if (mSelectingText) {
+                extras = DRAW_EXTRAS_SELECTION;
+            } else if (drawCursorRing) {
+                extras = DRAW_EXTRAS_CURSOR_RING;
+            }
+        }
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
+                    + " mSelectingText=" + mSelectingText
+                    + " nativePageShouldHandleShiftAndArrows()="
+                    + nativePageShouldHandleShiftAndArrows()
+                    + " animateZoom=" + animateZoom
+                    + " extras=" + extras);
+        }
+
+        calcOurContentVisibleRectF(mVisibleContentRect);
+        if (canvas.isHardwareAccelerated()) {
+            Rect glRectViewport = mGLViewportEmpty ? null : mGLRectViewport;
+            Rect viewRectViewport = mGLViewportEmpty ? null : mViewRectViewport;
+
+            int functor = nativeGetDrawGLFunction(mNativeClass, glRectViewport,
+                    viewRectViewport, mVisibleContentRect, getScale(), extras);
+            ((HardwareCanvas) canvas).callDrawGLFunction(functor);
+            if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
+                mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
+                nativeUseHardwareAccelSkia(mHardwareAccelSkia);
+            }
+
+        } else {
+            DrawFilter df = null;
+            if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
+                df = mZoomFilter;
+            } else if (animateScroll) {
+                df = mScrollFilter;
+            }
+            canvas.setDrawFilter(df);
+            // XXX: Revisit splitting content.  Right now it causes a
+            // synchronization problem with layers.
+            int content = nativeDraw(canvas, mVisibleContentRect, color,
+                    extras, false);
+            canvas.setDrawFilter(null);
+            if (!mBlockWebkitViewMessages && content != 0) {
+                mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
+            }
+        }
+
+        canvas.restoreToCount(saveCount);
+        if (mSelectingText) {
+            drawTextSelectionHandles(canvas);
+        }
+
+        if (extras == DRAW_EXTRAS_CURSOR_RING) {
+            if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
+                mTouchMode = TOUCH_SHORTPRESS_MODE;
+            }
+        }
+        if (mFocusSizeChanged) {
+            mFocusSizeChanged = false;
+            // If we are zooming, this will get handled above, when the zoom
+            // finishes.  We also do not need to do this unless the WebTextView
+            // is showing. With hardware acceleration, the pageSwapCallback()
+            // updates the WebTextView position in sync with page swapping
+            if (!canvas.isHardwareAccelerated() && !animateZoom && inEditingMode()) {
+                didUpdateWebTextViewDimensions(ANYWHERE);
+            }
+        }
+    }
+
+    private void ensureSelectionHandles() {
+        if (mSelectHandleCenter == null) {
+            mSelectHandleCenter = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_middle);
+            mSelectHandleLeft = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_left);
+            mSelectHandleRight = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_right);
+        }
+    }
+
+    private void drawTextSelectionHandles(Canvas canvas) {
+        ensureSelectionHandles();
+        int[] handles = new int[4];
+        getSelectionHandles(handles);
+        int start_x = contentToViewDimension(handles[0]);
+        int start_y = contentToViewDimension(handles[1]);
+        int end_x = contentToViewDimension(handles[2]);
+        int end_y = contentToViewDimension(handles[3]);
+
+        if (mIsCaretSelection) {
+            // Caret handle is centered
+            start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2);
+            mSelectHandleCenter.setBounds(start_x, start_y,
+                    start_x + mSelectHandleCenter.getIntrinsicWidth(),
+                    start_y + mSelectHandleCenter.getIntrinsicHeight());
+            mSelectHandleCenter.draw(canvas);
+        } else {
+            // Magic formula copied from TextView
+            start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
+            mSelectHandleLeft.setBounds(start_x, start_y,
+                    start_x + mSelectHandleLeft.getIntrinsicWidth(),
+                    start_y + mSelectHandleLeft.getIntrinsicHeight());
+            end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
+            mSelectHandleRight.setBounds(end_x, end_y,
+                    end_x + mSelectHandleRight.getIntrinsicWidth(),
+                    end_y + mSelectHandleRight.getIntrinsicHeight());
+            mSelectHandleLeft.draw(canvas);
+            mSelectHandleRight.draw(canvas);
+        }
+    }
+
+    /**
+     * Takes an int[4] array as an output param with the values being
+     * startX, startY, endX, endY
+     */
+    private void getSelectionHandles(int[] handles) {
+        handles[0] = mSelectCursorBase.right;
+        handles[1] = mSelectCursorBase.bottom -
+                (mSelectCursorBase.height() / 4);
+        handles[2] = mSelectCursorExtent.left;
+        handles[3] = mSelectCursorExtent.bottom
+                - (mSelectCursorExtent.height() / 4);
+        if (!nativeIsBaseFirst(mNativeClass)) {
+            int swap = handles[0];
+            handles[0] = handles[2];
+            handles[2] = swap;
+            swap = handles[1];
+            handles[1] = handles[3];
+            handles[3] = swap;
+        }
+    }
+
+    // draw history
+    private boolean mDrawHistory = false;
+    private Picture mHistoryPicture = null;
+    private int mHistoryWidth = 0;
+    private int mHistoryHeight = 0;
+
+    // Only check the flag, can be called from WebCore thread
+    boolean drawHistory() {
+        return mDrawHistory;
+    }
+
+    int getHistoryPictureWidth() {
+        return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
+    }
+
+    // Should only be called in UI thread
+    void switchOutDrawHistory() {
+        if (null == mWebViewCore) return; // CallbackProxy may trigger this
+        if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
+            mDrawHistory = false;
+            mHistoryPicture = null;
+            invalidate();
+            int oldScrollX = getScrollX();
+            int oldScrollY = getScrollY();
+            setScrollXRaw(pinLocX(getScrollX()));
+            setScrollYRaw(pinLocY(getScrollY()));
+            if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) {
+                mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
+            } else {
+                sendOurVisibleRect();
+            }
+        }
+    }
+
+    // TODO: Remove this
+    WebViewCore.CursorData cursorData() {
+        if (sDisableNavcache) {
+            return new WebViewCore.CursorData(0, 0, 0, 0);
+        }
+        WebViewCore.CursorData result = cursorDataNoPosition();
+        Point position = nativeCursorPosition();
+        result.mX = position.x;
+        result.mY = position.y;
+        return result;
+    }
+
+    WebViewCore.CursorData cursorDataNoPosition() {
+        WebViewCore.CursorData result = new WebViewCore.CursorData();
+        result.mMoveGeneration = nativeMoveGeneration();
+        result.mFrame = nativeCursorFramePointer();
+        return result;
+    }
+
+    /**
+     *  Delete text from start to end in the focused textfield. If there is no
+     *  focus, or if start == end, silently fail.  If start and end are out of
+     *  order, swap them.
+     *  @param  start   Beginning of selection to delete.
+     *  @param  end     End of selection to delete.
+     */
+    /* package */ void deleteSelection(int start, int end) {
+        mTextGeneration++;
+        WebViewCore.TextSelectionData data
+                = new WebViewCore.TextSelectionData(start, end, 0);
+        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
+                data);
+    }
+
+    /**
+     *  Set the selection to (start, end) in the focused textfield. If start and
+     *  end are out of order, swap them.
+     *  @param  start   Beginning of selection.
+     *  @param  end     End of selection.
+     */
+    /* package */ void setSelection(int start, int end) {
+        if (mWebViewCore != null) {
+            mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
+        }
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        if (mInputConnection == null) {
+            mInputConnection = new WebViewInputConnection();
+        }
+        mInputConnection.setupEditorInfo(outAttrs);
+        return mInputConnection;
+    }
+
+    /**
+     * Called in response to a message from webkit telling us that the soft
+     * keyboard should be launched.
+     */
+    private void displaySoftKeyboard(boolean isTextView) {
+        InputMethodManager imm = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        // bring it back to the default level scale so that user can enter text
+        boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
+        if (zoom) {
+            mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
+            mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
+        }
+        if (isTextView) {
+            rebuildWebTextView();
+            if (inEditingMode()) {
+                imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver());
+                if (zoom) {
+                    didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
+                }
+                return;
+            }
+        }
+        // Used by plugins and contentEditable.
+        // Also used if the navigation cache is out of date, and
+        // does not recognize that a textfield is in focus.  In that
+        // case, use WebView as the targeted view.
+        // see http://b/issue?id=2457459
+        imm.showSoftInput(mWebView, 0);
+    }
+
+    // Called by WebKit to instruct the UI to hide the keyboard
+    private void hideSoftKeyboard() {
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm != null && (imm.isActive(mWebView)
+                || (inEditingMode() && imm.isActive(mWebTextView)))) {
+            imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+        }
+    }
+
+    /*
+     * This method checks the current focus and cursor and potentially rebuilds
+     * mWebTextView to have the appropriate properties, such as password,
+     * multiline, and what text it contains.  It also removes it if necessary.
+     */
+    /* package */ void rebuildWebTextView() {
+        if (!sEnableWebTextView) {
+            return; // always use WebKit's text entry
+        }
+        // If the WebView does not have focus, do nothing until it gains focus.
+        if (!mWebView.hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
+            return;
+        }
+        boolean alreadyThere = inEditingMode();
+        // inEditingMode can only return true if mWebTextView is non-null,
+        // so we can safely call remove() if (alreadyThere)
+        if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
+            if (alreadyThere) {
+                mWebTextView.remove();
+            }
+            return;
+        }
+        // At this point, we know we have found an input field, so go ahead
+        // and create the WebTextView if necessary.
+        if (mWebTextView == null) {
+            mWebTextView = new WebTextView(mContext, WebViewClassic.this, mAutoFillData.getQueryId());
+            // Initialize our generation number.
+            mTextGeneration = 0;
+        }
+        mWebTextView.updateTextSize();
+        updateWebTextViewPosition();
+        String text = nativeFocusCandidateText();
+        int nodePointer = nativeFocusCandidatePointer();
+        // This needs to be called before setType, which may call
+        // requestFormData, and it needs to have the correct nodePointer.
+        mWebTextView.setNodePointer(nodePointer);
+        mWebTextView.setType(nativeFocusCandidateType());
+        // Gravity needs to be set after setType
+        mWebTextView.setGravityForRtl(nativeFocusCandidateIsRtlText());
+        if (null == text) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "rebuildWebTextView null == text");
+            }
+            text = "";
+        }
+        mWebTextView.setTextAndKeepSelection(text);
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm != null && imm.isActive(mWebTextView)) {
+            imm.restartInput(mWebTextView);
+            mWebTextView.clearComposingText();
+        }
+        if (mWebView.isFocused()) {
+            mWebTextView.requestFocus();
+        }
+    }
+
+    private void updateWebTextViewPosition() {
+        Rect visibleRect = new Rect();
+        calcOurContentVisibleRect(visibleRect);
+        // Note that sendOurVisibleRect calls viewToContent, so the coordinates
+        // should be in content coordinates.
+        Rect bounds = nativeFocusCandidateNodeBounds();
+        Rect vBox = contentToViewRect(bounds);
+        offsetByLayerScrollPosition(vBox);
+        mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
+        if (!Rect.intersects(bounds, visibleRect)) {
+            revealSelection();
+        }
+        updateWebTextViewPadding();
+    }
+
+    /**
+     * Update the padding of mWebTextView based on the native textfield/textarea
+     */
+    void updateWebTextViewPadding() {
+        Rect paddingRect = nativeFocusCandidatePaddingRect();
+        if (paddingRect != null) {
+            // Use contentToViewDimension since these are the dimensions of
+            // the padding.
+            mWebTextView.setPadding(
+                    contentToViewDimension(paddingRect.left),
+                    contentToViewDimension(paddingRect.top),
+                    contentToViewDimension(paddingRect.right),
+                    contentToViewDimension(paddingRect.bottom));
+        }
+    }
+
+    /**
+     * Tell webkit to put the cursor on screen.
+     */
+    /* package */ void revealSelection() {
+        if (mWebViewCore != null) {
+            mWebViewCore.sendMessage(EventHub.REVEAL_SELECTION);
+        }
+    }
+
+    /**
+     * Called by WebTextView to find saved form data associated with the
+     * textfield
+     * @param name Name of the textfield.
+     * @param nodePointer Pointer to the node of the textfield, so it can be
+     *          compared to the currently focused textfield when the data is
+     *          retrieved.
+     * @param autoFillable true if WebKit has determined this field is part of
+     *          a form that can be auto filled.
+     * @param autoComplete true if the attribute "autocomplete" is set to true
+     *          on the textfield.
+     */
+    /* package */ void requestFormData(String name, int nodePointer,
+            boolean autoFillable, boolean autoComplete) {
+        if (mWebViewCore.getSettings().getSaveFormData()) {
+            Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
+            update.arg1 = nodePointer;
+            RequestFormData updater = new RequestFormData(name, getUrl(),
+                    update, autoFillable, autoComplete);
+            Thread t = new Thread(updater);
+            t.start();
+        }
+    }
+
+    /**
+     * Pass a message to find out the <label> associated with the <input>
+     * identified by nodePointer
+     * @param framePointer Pointer to the frame containing the <input> node
+     * @param nodePointer Pointer to the node for which a <label> is desired.
+     */
+    /* package */ void requestLabel(int framePointer, int nodePointer) {
+        mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
+                nodePointer);
+    }
+
+    /*
+     * This class requests an Adapter for the WebTextView which shows past
+     * entries stored in the database.  It is a Runnable so that it can be done
+     * in its own thread, without slowing down the UI.
+     */
+    private class RequestFormData implements Runnable {
+        private String mName;
+        private String mUrl;
+        private Message mUpdateMessage;
+        private boolean mAutoFillable;
+        private boolean mAutoComplete;
+        private WebSettingsClassic mWebSettings;
+
+        public RequestFormData(String name, String url, Message msg,
+                boolean autoFillable, boolean autoComplete) {
+            mName = name;
+            mUrl = WebTextView.urlForAutoCompleteData(url);
+            mUpdateMessage = msg;
+            mAutoFillable = autoFillable;
+            mAutoComplete = autoComplete;
+            mWebSettings = getSettings();
+        }
+
+        @Override
+        public void run() {
+            ArrayList<String> pastEntries = new ArrayList<String>();
+
+            if (mAutoFillable) {
+                // Note that code inside the adapter click handler in WebTextView depends
+                // on the AutoFill item being at the top of the drop down list. If you change
+                // the order, make sure to do it there too!
+                if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
+                    pastEntries.add(mWebView.getResources().getText(
+                            com.android.internal.R.string.autofill_this_form).toString() +
+                            " " +
+                            mAutoFillData.getPreviewString());
+                    mWebTextView.setAutoFillProfileIsSet(true);
+                } else {
+                    // There is no autofill profile set up yet, so add an option that
+                    // will invite the user to set their profile up.
+                    pastEntries.add(mWebView.getResources().getText(
+                            com.android.internal.R.string.setup_autofill).toString());
+                    mWebTextView.setAutoFillProfileIsSet(false);
+                }
+            }
+
+            if (mAutoComplete) {
+                pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
+            }
+
+            if (pastEntries.size() > 0) {
+                AutoCompleteAdapter adapter = new
+                        AutoCompleteAdapter(mContext, pastEntries);
+                mUpdateMessage.obj = adapter;
+                mUpdateMessage.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * Dump the display tree to "/sdcard/displayTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpDisplayTree() {
+        nativeDumpDisplayTree(getUrl());
+    }
+
+    /**
+     * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
+     * "/sdcard/domTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpDomTree(boolean toFile) {
+        mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
+    }
+
+    /**
+     * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
+     * to "/sdcard/renderTree.txt"
+     *
+     * @hide debug only
+     */
+    public void dumpRenderTree(boolean toFile) {
+        mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
+    }
+
+    /**
+     * Called by DRT on UI thread, need to proxy to WebCore thread.
+     *
+     * @hide debug only
+     */
+    public void useMockDeviceOrientation() {
+        mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
+    }
+
+    /**
+     * Called by DRT on WebCore thread.
+     *
+     * @hide debug only
+     */
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
+    }
+
+    // This is used to determine long press with the center key.  Does not
+    // affect long press with the trackball/touch.
+    private boolean mGotCenterDown = false;
+
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        if (mBlockWebkitViewMessages) {
+            return false;
+        }
+        // send complex characters to webkit for use by JS and plugins
+        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
+            // pass the key to DOM
+            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+            // return true as DOM handles the key
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isEnterActionKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
+                || keyCode == KeyEvent.KEYCODE_ENTER
+                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
+                    + "keyCode=" + keyCode
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
+        }
+        if (mIsCaretSelection) {
+            selectionDone();
+        }
+        if (mBlockWebkitViewMessages) {
+            return false;
+        }
+
+        // don't implement accelerator keys here; defer to host application
+        if (event.isCtrlPressed()) {
+            return false;
+        }
+
+        if (mNativeClass == 0) {
+            return false;
+        }
+
+        // do this hack up front, so it always works, regardless of touch-mode
+        if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
+            mAutoRedraw = !mAutoRedraw;
+            if (mAutoRedraw) {
+                invalidate();
+            }
+            return true;
+        }
+
+        // Bubble up the key event if
+        // 1. it is a system key; or
+        // 2. the host application wants to handle it;
+        if (event.isSystem()
+                || mCallbackProxy.uiOverrideKeyEvent(event)) {
+            return false;
+        }
+
+        // accessibility support
+        if (accessibilityScriptInjected()) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                // if an accessibility script is injected we delegate to it the key handling.
+                // this script is a screen reader which is a fully fledged solution for blind
+                // users to navigate in and interact with web pages.
+                mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+                return true;
+            } else {
+                // Clean up if accessibility was disabled after loading the current URL.
+                mAccessibilityScriptInjected = false;
+            }
+        } else if (mAccessibilityInjector != null) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                if (mAccessibilityInjector.onKeyEvent(event)) {
+                    // if an accessibility injector is present (no JavaScript enabled or the site
+                    // opts out injecting our JavaScript screen reader) we let it decide whether
+                    // to act on and consume the event.
+                    return true;
+                }
+            } else {
+                // Clean up if accessibility was disabled after loading the current URL.
+                mAccessibilityInjector = null;
+            }
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+            if (event.hasNoModifiers()) {
+                pageUp(false);
+                return true;
+            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                pageUp(true);
+                return true;
+            }
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
+            if (event.hasNoModifiers()) {
+                pageDown(false);
+                return true;
+            } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                pageDown(true);
+                return true;
+            }
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
+            pageUp(true);
+            return true;
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
+            pageDown(true);
+            return true;
+        }
+
+        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+            switchOutDrawHistory();
+            if (nativePageShouldHandleShiftAndArrows()) {
+                letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
+                return true;
+            }
+            if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                switch (keyCode) {
+                    case KeyEvent.KEYCODE_DPAD_UP:
+                        pageUp(true);
+                        return true;
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                        pageDown(true);
+                        return true;
+                    case KeyEvent.KEYCODE_DPAD_LEFT:
+                        nativeClearCursor(); // start next trackball movement from page edge
+                        return pinScrollTo(0, getScrollY(), true, 0);
+                    case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        nativeClearCursor(); // start next trackball movement from page edge
+                        return pinScrollTo(mContentWidth, getScrollY(), true, 0);
+                }
+            }
+            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
+                mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
+                return true;
+            }
+            // Bubble up the key event as WebView doesn't handle it
+            return false;
+        }
+
+        if (isEnterActionKey(keyCode)) {
+            switchOutDrawHistory();
+            boolean wantsKeyEvents = nativeCursorNodePointer() == 0
+                || nativeCursorWantsKeyEvents();
+            if (event.getRepeatCount() == 0) {
+                if (mSelectingText) {
+                    return true; // discard press if copy in progress
+                }
+                mGotCenterDown = true;
+                mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                        .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
+                if (!wantsKeyEvents) return true;
+            }
+            // Bubble up the key event as WebView doesn't handle it
+            if (!wantsKeyEvents) return false;
+        }
+
+        if (getSettings().getNavDump()) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_4:
+                    dumpDisplayTree();
+                    break;
+                case KeyEvent.KEYCODE_5:
+                case KeyEvent.KEYCODE_6:
+                    dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
+                    break;
+                case KeyEvent.KEYCODE_7:
+                case KeyEvent.KEYCODE_8:
+                    dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
+                    break;
+            }
+        }
+
+        if (nativeCursorIsTextInput()) {
+            // This message will put the node in focus, for the DOM's notion
+            // of focus.
+            mWebViewCore.sendMessage(EventHub.FAKE_CLICK, nativeCursorFramePointer(),
+                    nativeCursorNodePointer());
+            // This will bring up the WebTextView and put it in focus, for
+            // our view system's notion of focus
+            rebuildWebTextView();
+            // Now we need to pass the event to it
+            if (inEditingMode()) {
+                mWebTextView.setDefaultSelection();
+                return mWebTextView.dispatchKeyEvent(event);
+            }
+        } else if (nativeHasFocusNode()) {
+            // In this case, the cursor is not on a text input, but the focus
+            // might be.  Check it, and if so, hand over to the WebTextView.
+            rebuildWebTextView();
+            if (inEditingMode()) {
+                mWebTextView.setDefaultSelection();
+                return mWebTextView.dispatchKeyEvent(event);
+            }
+        }
+
+        // TODO: should we pass all the keys to DOM or check the meta tag
+        if (nativeCursorWantsKeyEvents() || true) {
+            // pass the key to DOM
+            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+            // return true as DOM handles the key
+            return true;
+        }
+
+        // Bubble up the key event as WebView doesn't handle it
+        return false;
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
+                    + ", " + event + ", unicode=" + event.getUnicodeChar());
+        }
+        if (mBlockWebkitViewMessages) {
+            return false;
+        }
+
+        if (mNativeClass == 0) {
+            return false;
+        }
+
+        // special CALL handling when cursor node's href is "tel:XXX"
+        if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
+            String text = nativeCursorText();
+            if (!nativeCursorIsTextInput() && text != null
+                    && text.startsWith(SCHEME_TEL)) {
+                Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
+                mContext.startActivity(intent);
+                return true;
+            }
+        }
+
+        // Bubble up the key event if
+        // 1. it is a system key; or
+        // 2. the host application wants to handle it;
+        if (event.isSystem()
+                || mCallbackProxy.uiOverrideKeyEvent(event)) {
+            return false;
+        }
+
+        // accessibility support
+        if (accessibilityScriptInjected()) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                // if an accessibility script is injected we delegate to it the key handling.
+                // this script is a screen reader which is a fully fledged solution for blind
+                // users to navigate in and interact with web pages.
+                mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+                return true;
+            } else {
+                // Clean up if accessibility was disabled after loading the current URL.
+                mAccessibilityScriptInjected = false;
+            }
+        } else if (mAccessibilityInjector != null) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                if (mAccessibilityInjector.onKeyEvent(event)) {
+                    // if an accessibility injector is present (no JavaScript enabled or the site
+                    // opts out injecting our JavaScript screen reader) we let it decide whether to
+                    // act on and consume the event.
+                    return true;
+                }
+            } else {
+                // Clean up if accessibility was disabled after loading the current URL.
+                mAccessibilityInjector = null;
+            }
+        }
+
+        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+            if (nativePageShouldHandleShiftAndArrows()) {
+                letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
+                return true;
+            }
+            // always handle the navigation keys in the UI thread
+            // Bubble up the key event as WebView doesn't handle it
+            return false;
+        }
+
+        if (isEnterActionKey(keyCode)) {
+            // remove the long press message first
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+            mGotCenterDown = false;
+
+            if (mSelectingText) {
+                copySelection();
+                selectionDone();
+                return true; // discard press if copy in progress
+            }
+
+            if (!sDisableNavcache) {
+                // perform the single click
+                Rect visibleRect = sendOurVisibleRect();
+                // Note that sendOurVisibleRect calls viewToContent, so the
+                // coordinates should be in content coordinates.
+                if (!nativeCursorIntersects(visibleRect)) {
+                    return false;
+                }
+                WebViewCore.CursorData data = cursorData();
+                mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+                mWebView.playSoundEffect(SoundEffectConstants.CLICK);
+                if (nativeCursorIsTextInput()) {
+                    rebuildWebTextView();
+                    centerKeyPressOnTextField();
+                    if (inEditingMode()) {
+                        mWebTextView.setDefaultSelection();
+                    }
+                    return true;
+                }
+                clearTextEntry();
+                nativeShowCursorTimed();
+                if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
+                    return true;
+                }
+                if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
+                    mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+                            nativeCursorNodePointer());
+                    return true;
+                }
+            }
+        }
+
+        // TODO: should we pass all the keys to DOM or check the meta tag
+        if (nativeCursorWantsKeyEvents() || true) {
+            // pass the key to DOM
+            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+            // return true as DOM handles the key
+            return true;
+        }
+
+        // Bubble up the key event as WebView doesn't handle it
+        return false;
+    }
+
+    private boolean startSelectActionMode() {
+        mSelectCallback = new SelectActionModeCallback();
+        mSelectCallback.setTextSelected(!mIsCaretSelection);
+        mSelectCallback.setWebView(this);
+        if (mWebView.startActionMode(mSelectCallback) == null) {
+            // There is no ActionMode, so do not allow the user to modify a
+            // selection.
+            selectionDone();
+            return false;
+        }
+        mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        return true;
+    }
+
+    private void showPasteWindow() {
+        ClipboardManager cm = (ClipboardManager)(mContext
+                .getSystemService(Context.CLIPBOARD_SERVICE));
+        if (cm.hasPrimaryClip()) {
+            Rect cursorRect = contentToViewRect(mSelectCursorBase);
+            int[] location = new int[2];
+            mWebView.getLocationInWindow(location);
+            cursorRect.offset(location[0] - getScrollX(), location[1] - getScrollY());
+            if (mPasteWindow == null) {
+                mPasteWindow = new PastePopupWindow();
+            }
+            mPasteWindow.show(cursorRect, location[0], location[1]);
+        }
+    }
+
+    private void hidePasteButton() {
+        if (mPasteWindow != null) {
+            mPasteWindow.hide();
+        }
+    }
+
+    private void syncSelectionCursors() {
+        mSelectCursorBaseLayerId =
+                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase);
+        mSelectCursorExtentLayerId =
+                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent);
+    }
+
+    private boolean setupWebkitSelect() {
+        syncSelectionCursors();
+        if (mIsCaretSelection) {
+            showPasteWindow();
+        } else if (!startSelectActionMode()) {
+            selectionDone();
+            return false;
+        }
+        mSelectingText = true;
+        mTouchMode = TOUCH_DRAG_MODE;
+        return true;
+    }
+
+    private void updateWebkitSelection() {
+        int[] handles = null;
+        if (mIsCaretSelection) {
+            mSelectCursorExtent.set(mSelectCursorBase);
+        }
+        if (mSelectingText) {
+            handles = new int[4];
+            handles[0] = mSelectCursorBase.centerX();
+            handles[1] = mSelectCursorBase.centerY();
+            handles[2] = mSelectCursorExtent.centerX();
+            handles[3] = mSelectCursorExtent.centerY();
+        } else {
+            nativeSetTextSelection(mNativeClass, 0);
+        }
+        mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
+        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles);
+    }
+
+    private void resetCaretTimer() {
+        mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
+        if (!mSelectionStarted) {
+            mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
+                    CARET_HANDLE_STAMINA_MS);
+        }
+    }
+
+    /**
+     * Use this method to put the WebView into text selection mode.
+     * Do not rely on this functionality; it will be deprecated in the future.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public void emulateShiftHeld() {
+        checkThread();
+    }
+
+    /**
+     * Select all of the text in this WebView.
+     *
+     * @hide This is an implementation detail.
+     */
+    public void selectAll() {
+        mWebViewCore.sendMessage(EventHub.SELECT_ALL);
+    }
+
+    /**
+     * Called when the selection has been removed.
+     */
+    void selectionDone() {
+        if (mSelectingText) {
+            hidePasteButton();
+            mSelectingText = false;
+            // finish is idempotent, so this is fine even if selectionDone was
+            // called by mSelectCallback.onDestroyActionMode
+            if (mSelectCallback != null) {
+                mSelectCallback.finish();
+                mSelectCallback = null;
+            }
+            if (!mIsCaretSelection) {
+                updateWebkitSelection();
+            }
+            mIsCaretSelection = false;
+            invalidate(); // redraw without selection
+            mAutoScrollX = 0;
+            mAutoScrollY = 0;
+            mSentAutoScrollMessage = false;
+        }
+    }
+
+    /**
+     * Copy the selection to the clipboard
+     *
+     * @hide This is an implementation detail.
+     */
+    public boolean copySelection() {
+        boolean copiedSomething = false;
+        String selection = getSelection();
+        if (selection != null && selection != "") {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "copySelection \"" + selection + "\"");
+            }
+            Toast.makeText(mContext
+                    , com.android.internal.R.string.text_copied
+                    , Toast.LENGTH_SHORT).show();
+            copiedSomething = true;
+            ClipboardManager cm = (ClipboardManager)mContext
+                    .getSystemService(Context.CLIPBOARD_SERVICE);
+            cm.setText(selection);
+            int[] handles = new int[4];
+            getSelectionHandles(handles);
+            mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
+        }
+        invalidate(); // remove selection region and pointer
+        return copiedSomething;
+    }
+
+    /**
+     * Cut the selected text into the clipboard
+     *
+     * @hide This is an implementation detail
+     */
+    public void cutSelection() {
+        copySelection();
+        int[] handles = new int[4];
+        getSelectionHandles(handles);
+        mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
+    }
+
+    /**
+     * Paste text from the clipboard to the cursor position.
+     *
+     * @hide This is an implementation detail
+     */
+    public void pasteFromClipboard() {
+        ClipboardManager cm = (ClipboardManager)mContext
+                .getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipData clipData = cm.getPrimaryClip();
+        if (clipData != null) {
+            ClipData.Item clipItem = clipData.getItemAt(0);
+            CharSequence pasteText = clipItem.getText();
+            if (mInputConnection != null) {
+                mInputConnection.replaceSelection(pasteText);
+            }
+        }
+    }
+
+    /**
+     * @hide This is an implementation detail.
+     */
+    public SearchBox getSearchBox() {
+        if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
+            return null;
+        }
+        return mWebViewCore.getBrowserFrame().getSearchBox();
+    }
+
+    /**
+     * Returns the currently highlighted text as a string.
+     */
+    String getSelection() {
+        if (mNativeClass == 0) return "";
+        return nativeGetSelection();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        if (mWebView.hasWindowFocus()) setActive(true);
+        final ViewTreeObserver treeObserver = mWebView.getViewTreeObserver();
+        if (mGlobalLayoutListener == null) {
+            mGlobalLayoutListener = new InnerGlobalLayoutListener();
+            treeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
+        }
+        if (mScrollChangedListener == null) {
+            mScrollChangedListener = new InnerScrollChangedListener();
+            treeObserver.addOnScrollChangedListener(mScrollChangedListener);
+        }
+
+        addAccessibilityApisToJavaScript();
+
+        mTouchEventQueue.reset();
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        clearHelpers();
+        mZoomManager.dismissZoomPicker();
+        if (mWebView.hasWindowFocus()) setActive(false);
+
+        final ViewTreeObserver treeObserver = mWebView.getViewTreeObserver();
+        if (mGlobalLayoutListener != null) {
+            treeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+            mGlobalLayoutListener = null;
+        }
+        if (mScrollChangedListener != null) {
+            treeObserver.removeOnScrollChangedListener(mScrollChangedListener);
+            mScrollChangedListener = null;
+        }
+
+        removeAccessibilityApisFromJavaScript();
+    }
+
+    @Override
+    public void onVisibilityChanged(View changedView, int visibility) {
+        // The zoomManager may be null if the webview is created from XML that
+        // specifies the view's visibility param as not visible (see http://b/2794841)
+        if (visibility != View.VISIBLE && mZoomManager != null) {
+            mZoomManager.dismissZoomPicker();
+        }
+        updateDrawingState();
+    }
+
+    void setActive(boolean active) {
+        if (active) {
+            if (mWebView.hasFocus()) {
+                // If our window regained focus, and we have focus, then begin
+                // drawing the cursor ring
+                mDrawCursorRing = !inEditingMode();
+                setFocusControllerActive(true);
+            } else {
+                mDrawCursorRing = false;
+                if (!inEditingMode()) {
+                    // If our window gained focus, but we do not have it, do not
+                    // draw the cursor ring.
+                    setFocusControllerActive(false);
+                }
+                // We do not call recordButtons here because we assume
+                // that when we lost focus, or window focus, it got called with
+                // false for the first parameter
+            }
+        } else {
+            if (!mZoomManager.isZoomPickerVisible()) {
+                /*
+                 * The external zoom controls come in their own window, so our
+                 * window loses focus. Our policy is to not draw the cursor ring
+                 * if our window is not focused, but this is an exception since
+                 * the user can still navigate the web page with the zoom
+                 * controls showing.
+                 */
+                mDrawCursorRing = false;
+            }
+            mKeysPressed.clear();
+            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+            mTouchMode = TOUCH_DONE_MODE;
+            setFocusControllerActive(false);
+        }
+        invalidate();
+    }
+
+    // To avoid drawing the cursor ring, and remove the TextView when our window
+    // loses focus.
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        setActive(hasWindowFocus);
+        if (hasWindowFocus) {
+            JWebCoreJavaBridge.setActiveWebView(this);
+            if (mPictureUpdatePausedForFocusChange) {
+                WebViewCore.resumeUpdatePicture(mWebViewCore);
+                mPictureUpdatePausedForFocusChange = false;
+            }
+        } else {
+            JWebCoreJavaBridge.removeActiveWebView(this);
+            final WebSettings settings = getSettings();
+            if (settings != null && settings.enableSmoothTransition() &&
+                    mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
+                WebViewCore.pauseUpdatePicture(mWebViewCore);
+                mPictureUpdatePausedForFocusChange = true;
+            }
+        }
+    }
+
+    /*
+     * Pass a message to WebCore Thread, telling the WebCore::Page's
+     * FocusController to be  "inactive" so that it will
+     * not draw the blinking cursor.  It gets set to "active" to draw the cursor
+     * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
+     */
+    /* package */ void setFocusControllerActive(boolean active) {
+        if (mWebViewCore == null) return;
+        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
+        // Need to send this message after the document regains focus.
+        if (active && mListBoxMessage != null) {
+            mWebViewCore.sendMessage(mListBoxMessage);
+            mListBoxMessage = null;
+        }
+    }
+
+    @Override
+    public void onFocusChanged(boolean focused, int direction,
+            Rect previouslyFocusedRect) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
+        }
+        if (focused) {
+            // When we regain focus, if we have window focus, resume drawing
+            // the cursor ring
+            if (mWebView.hasWindowFocus()) {
+                mDrawCursorRing = !inEditingMode();
+                setFocusControllerActive(true);
+            //} else {
+                // The WebView has gained focus while we do not have
+                // windowfocus.  When our window lost focus, we should have
+                // called recordButtons(false...)
+            }
+        } else {
+            // When we lost focus, unless focus went to the TextView (which is
+            // true if we are in editing mode), stop drawing the cursor ring.
+            mDrawCursorRing = false;
+            if (!inEditingMode()) {
+                setFocusControllerActive(false);
+            }
+            mKeysPressed.clear();
+        }
+    }
+
+    void setGLRectViewport() {
+        // Use the getGlobalVisibleRect() to get the intersection among the parents
+        // visible == false means we're clipped - send a null rect down to indicate that
+        // we should not draw
+        boolean visible = mWebView.getGlobalVisibleRect(mGLRectViewport);
+        if (visible) {
+            // Then need to invert the Y axis, just for GL
+            View rootView = mWebView.getRootView();
+            int rootViewHeight = rootView.getHeight();
+            mViewRectViewport.set(mGLRectViewport);
+            int savedWebViewBottom = mGLRectViewport.bottom;
+            mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeightImpl();
+            mGLRectViewport.top = rootViewHeight - savedWebViewBottom;
+            mGLViewportEmpty = false;
+        } else {
+            mGLViewportEmpty = true;
+        }
+        calcOurContentVisibleRectF(mVisibleContentRect);
+        nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
+                mGLViewportEmpty ? null : mViewRectViewport,
+                mVisibleContentRect, getScale());
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean setFrame(int left, int top, int right, int bottom) {
+        boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
+        if (!changed && mHeightCanMeasure) {
+            // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
+            // in WebViewCore after we get the first layout. We do call
+            // requestLayout() when we get contentSizeChanged(). But the View
+            // system won't call onSizeChanged if the dimension is not changed.
+            // In this case, we need to call sendViewSizeZoom() explicitly to
+            // notify the WebKit about the new dimensions.
+            sendViewSizeZoom(false);
+        }
+        setGLRectViewport();
+        return changed;
+    }
+
+    @Override
+    public void onSizeChanged(int w, int h, int ow, int oh) {
+        // adjust the max viewport width depending on the view dimensions. This
+        // is to ensure the scaling is not going insane. So do not shrink it if
+        // the view size is temporarily smaller, e.g. when soft keyboard is up.
+        int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
+        if (newMaxViewportWidth > sMaxViewportWidth) {
+            sMaxViewportWidth = newMaxViewportWidth;
+        }
+
+        mZoomManager.onSizeChanged(w, h, ow, oh);
+
+        if (mLoadedPicture != null && mDelaySetPicture == null) {
+            // Size changes normally result in a new picture
+            // Re-set the loaded picture to simulate that
+            // However, do not update the base layer as that hasn't changed
+            setNewPicture(mLoadedPicture, false);
+        }
+    }
+
+    @Override
+    public void onScrollChanged(int l, int t, int oldl, int oldt) {
+        if (!mInOverScrollMode) {
+            sendOurVisibleRect();
+            // update WebKit if visible title bar height changed. The logic is same
+            // as getVisibleTitleHeightImpl.
+            int titleHeight = getTitleHeight();
+            if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
+                sendViewSizeZoom(false);
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        switch (event.getAction()) {
+            case KeyEvent.ACTION_DOWN:
+                mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
+                break;
+            case KeyEvent.ACTION_MULTIPLE:
+                // Always accept the action.
+                break;
+            case KeyEvent.ACTION_UP:
+                int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
+                if (location == -1) {
+                    // We did not receive the key down for this key, so do not
+                    // handle the key up.
+                    return false;
+                } else {
+                    // We did receive the key down.  Handle the key up, and
+                    // remove it from our pressed keys.
+                    mKeysPressed.remove(location);
+                }
+                break;
+            default:
+                // Accept the action.  This should not happen, unless a new
+                // action is added to KeyEvent.
+                break;
+        }
+        if (inEditingMode() && mWebTextView.isFocused()) {
+            // Ensure that the WebTextView gets the event, even if it does
+            // not currently have a bounds.
+            return mWebTextView.dispatchKeyEvent(event);
+        } else {
+            return mWebViewPrivate.super_dispatchKeyEvent(event);
+        }
+    }
+
+    /*
+     * Here is the snap align logic:
+     * 1. If it starts nearly horizontally or vertically, snap align;
+     * 2. If there is a dramitic direction change, let it go;
+     *
+     * Adjustable parameters. Angle is the radians on a unit circle, limited
+     * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
+     */
+    private static final float HSLOPE_TO_START_SNAP = .25f;
+    private static final float HSLOPE_TO_BREAK_SNAP = .4f;
+    private static final float VSLOPE_TO_START_SNAP = 1.25f;
+    private static final float VSLOPE_TO_BREAK_SNAP = .95f;
+    /*
+     *  These values are used to influence the average angle when entering
+     *  snap mode. If is is the first movement entering snap, we set the average
+     *  to the appropriate ideal. If the user is entering into snap after the
+     *  first movement, then we average the average angle with these values.
+     */
+    private static final float ANGLE_VERT = 2f;
+    private static final float ANGLE_HORIZ = 0f;
+    /*
+     *  The modified moving average weight.
+     *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
+     */
+    private static final float MMA_WEIGHT_N = 5;
+
+    private boolean hitFocusedPlugin(int contentX, int contentY) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
+            Rect r = nativeFocusNodeBounds();
+            Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
+                    + ", " + r.right + ", " + r.bottom + ")");
+        }
+        return nativeFocusIsPlugin()
+                && nativeFocusNodeBounds().contains(contentX, contentY);
+    }
+
+    private boolean shouldForwardTouchEvent() {
+        if (mFullScreenHolder != null) return true;
+        if (mBlockWebkitViewMessages) return false;
+        return mForwardTouchEvents
+                && !mSelectingText
+                && mPreventDefault != PREVENT_DEFAULT_IGNORE
+                && mPreventDefault != PREVENT_DEFAULT_NO;
+    }
+
+    private boolean inFullScreenMode() {
+        return mFullScreenHolder != null;
+    }
+
+    private void dismissFullScreenMode() {
+        if (inFullScreenMode()) {
+            mFullScreenHolder.hide();
+            mFullScreenHolder = null;
+            invalidate();
+        }
+    }
+
+    void onPinchToZoomAnimationStart() {
+        // cancel the single touch handling
+        cancelTouch();
+        onZoomAnimationStart();
+    }
+
+    void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
+        onZoomAnimationEnd();
+        // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
+        // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
+        // as it may trigger the unwanted fling.
+        mTouchMode = TOUCH_PINCH_DRAG;
+        mConfirmMove = true;
+        startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
+    }
+
+    // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
+    // layer is found.
+    private void startScrollingLayer(float x, float y) {
+        int contentX = viewToContentX((int) x + getScrollX());
+        int contentY = viewToContentY((int) y + getScrollY());
+        mCurrentScrollingLayerId = nativeScrollableLayer(contentX, contentY,
+                mScrollingLayerRect, mScrollingLayerBounds);
+        if (mCurrentScrollingLayerId != 0) {
+            mTouchMode = TOUCH_DRAG_LAYER_MODE;
+        }
+    }
+
+    // 1/(density * density) used to compute the distance between points.
+    // Computed in init().
+    private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
+
+    // The distance between two points reported in onTouchEvent scaled by the
+    // density of the screen.
+    private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
+
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        if (mNativeClass == 0) {
+            return false;
+        }
+        WebViewCore.CursorData data = cursorDataNoPosition();
+        data.mX = viewToContentX((int) event.getX() + getScrollX());
+        data.mY = viewToContentY((int) event.getY() + getScrollY());
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+        return true;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
+            return false;
+        }
+
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, ev + " at " + ev.getEventTime()
+                + " mTouchMode=" + mTouchMode
+                + " numPointers=" + ev.getPointerCount());
+        }
+
+        // If WebKit wasn't interested in this multitouch gesture, enqueue
+        // the event for handling directly rather than making the round trip
+        // to WebKit and back.
+        if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) {
+            passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence());
+        } else {
+            mTouchEventQueue.enqueueTouchEvent(ev);
+        }
+
+        // Since all events are handled asynchronously, we always want the gesture stream.
+        return true;
+    }
+
+    private float calculateDragAngle(int dx, int dy) {
+        dx = Math.abs(dx);
+        dy = Math.abs(dy);
+        return (float) Math.atan2(dy, dx);
+    }
+
+    /*
+     * Common code for single touch and multi-touch.
+     * (x, y) denotes current focus point, which is the touch point for single touch
+     * and the middle point for multi-touch.
+     */
+    private boolean handleTouchEventCommon(MotionEvent ev, int action, int x, int y) {
+        long eventTime = ev.getEventTime();
+
+        // Due to the touch screen edge effect, a touch closer to the edge
+        // always snapped to the edge. As getViewWidth() can be different from
+        // getWidth() due to the scrollbar, adjusting the point to match
+        // getViewWidth(). Same applied to the height.
+        x = Math.min(x, getViewWidth() - 1);
+        y = Math.min(y, getViewHeightWithTitle() - 1);
+
+        int deltaX = mLastTouchX - x;
+        int deltaY = mLastTouchY - y;
+        int contentX = viewToContentX(x + getScrollX());
+        int contentY = viewToContentY(y + getScrollY());
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN: {
+                mPreventDefault = PREVENT_DEFAULT_NO;
+                mConfirmMove = false;
+                mInitialHitTestResult = null;
+                if (!mScroller.isFinished()) {
+                    // stop the current scroll animation, but if this is
+                    // the start of a fling, allow it to add to the current
+                    // fling's velocity
+                    mScroller.abortAnimation();
+                    mTouchMode = TOUCH_DRAG_START_MODE;
+                    mConfirmMove = true;
+                    nativeSetIsScrolling(false);
+                } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
+                    mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
+                    if (sDisableNavcache) {
+                        removeTouchHighlight();
+                    }
+                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
+                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
+                    } else {
+                        // commit the short press action for the previous tap
+                        doShortPress();
+                        mTouchMode = TOUCH_INIT_MODE;
+                        mDeferTouchProcess = !mBlockWebkitViewMessages
+                                && (!inFullScreenMode() && mForwardTouchEvents)
+                                ? hitFocusedPlugin(contentX, contentY)
+                                : false;
+                    }
+                } else { // the normal case
+                    mTouchMode = TOUCH_INIT_MODE;
+                    mDeferTouchProcess = !mBlockWebkitViewMessages
+                            && (!inFullScreenMode() && mForwardTouchEvents)
+                            ? hitFocusedPlugin(contentX, contentY)
+                            : false;
+                    if (!mBlockWebkitViewMessages) {
+                        mWebViewCore.sendMessage(
+                                EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
+                    }
+                    if (sDisableNavcache) {
+                        TouchHighlightData data = new TouchHighlightData();
+                        data.mX = contentX;
+                        data.mY = contentY;
+                        data.mNativeLayerRect = new Rect();
+                        data.mNativeLayer = nativeScrollableLayer(
+                                contentX, contentY, data.mNativeLayerRect, null);
+                        data.mSlop = viewToContentDimension(mNavSlop);
+                        mTouchHighlightRegion.setEmpty();
+                        if (!mBlockWebkitViewMessages) {
+                            mTouchHighlightRequested = System.currentTimeMillis();
+                            mWebViewCore.sendMessageAtFrontOfQueue(
+                                    EventHub.HIT_TEST, data);
+                        }
+                        if (DEBUG_TOUCH_HIGHLIGHT) {
+                            if (getSettings().getNavDump()) {
+                                mTouchHighlightX = x + getScrollX();
+                                mTouchHighlightY = y + getScrollY();
+                                mPrivateHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        mTouchHighlightX = mTouchHighlightY = 0;
+                                        invalidate();
+                                    }
+                                }, TOUCH_HIGHLIGHT_ELAPSE_TIME);
+                            }
+                        }
+                    }
+                    if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
+                        EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
+                                (eventTime - mLastTouchUpTime), eventTime);
+                    }
+                    mSelectionStarted = false;
+                    if (mSelectingText) {
+                        int shiftedY = y - getTitleHeight() + getScrollY();
+                        int shiftedX = x + getScrollX();
+                        if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds()
+                                .contains(shiftedX, shiftedY)) {
+                            mSelectionStarted = true;
+                            mSelectDraggingCursor = mSelectCursorBase;
+                            mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
+                            hidePasteButton();
+                        } else if (mSelectHandleLeft != null
+                                && mSelectHandleLeft.getBounds()
+                                    .contains(shiftedX, shiftedY)) {
+                                mSelectionStarted = true;
+                                mSelectDraggingCursor = mSelectCursorBase;
+                        } else if (mSelectHandleRight != null
+                                && mSelectHandleRight.getBounds()
+                                .contains(shiftedX, shiftedY)) {
+                            mSelectionStarted = true;
+                            mSelectDraggingCursor = mSelectCursorExtent;
+                        } else if (mIsCaretSelection) {
+                            selectionDone();
+                        }
+                        if (mSelectDraggingCursor != null) {
+                            mSelectDraggingOffset.set(
+                                    mSelectDraggingCursor.left - contentX,
+                                    mSelectDraggingCursor.top - contentY);
+                        }
+                        if (DebugFlags.WEB_VIEW) {
+                            Log.v(LOGTAG, "select=" + contentX + "," + contentY);
+                        }
+                    }
+                }
+                // Trigger the link
+                if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
+                        || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
+                    mPrivateHandler.sendEmptyMessageDelayed(
+                            SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
+                    mPrivateHandler.sendEmptyMessageDelayed(
+                            SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
+                    if (inFullScreenMode() || mDeferTouchProcess) {
+                        mPreventDefault = PREVENT_DEFAULT_YES;
+                    } else if (!mBlockWebkitViewMessages && mForwardTouchEvents) {
+                        mPreventDefault = PREVENT_DEFAULT_MAYBE_YES;
+                    } else {
+                        mPreventDefault = PREVENT_DEFAULT_NO;
+                    }
+                    // pass the touch events from UI thread to WebCore thread
+                    if (shouldForwardTouchEvent()) {
+                        TouchEventData ted = new TouchEventData();
+                        ted.mAction = action;
+                        ted.mIds = new int[1];
+                        ted.mIds[0] = ev.getPointerId(0);
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(contentX, contentY);
+                        ted.mPointsInView = new Point[1];
+                        ted.mPointsInView[0] = new Point(x, y);
+                        ted.mMetaState = ev.getMetaState();
+                        ted.mReprocess = mDeferTouchProcess;
+                        ted.mNativeLayer = nativeScrollableLayer(
+                                contentX, contentY, ted.mNativeLayerRect, null);
+                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
+                        mTouchEventQueue.preQueueTouchEventData(ted);
+                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                        if (mDeferTouchProcess) {
+                            // still needs to set them for compute deltaX/Y
+                            mLastTouchX = x;
+                            mLastTouchY = y;
+                            break;
+                        }
+                        if (!inFullScreenMode()) {
+                            mPrivateHandler.removeMessages(PREVENT_DEFAULT_TIMEOUT);
+                            mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                                    .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
+                                            action, 0), TAP_TIMEOUT);
+                        }
+                    }
+                }
+                startTouch(x, y, eventTime);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                boolean firstMove = false;
+                if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
+                        >= mTouchSlopSquare) {
+                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                    mConfirmMove = true;
+                    firstMove = true;
+                    if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+                        mTouchMode = TOUCH_INIT_MODE;
+                    }
+                    if (sDisableNavcache) {
+                        removeTouchHighlight();
+                    }
+                }
+                if (mSelectingText && mSelectionStarted) {
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
+                    }
+                    ViewParent parent = mWebView.getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    if (deltaX != 0 || deltaY != 0) {
+                        mSelectDraggingCursor.offsetTo(
+                                contentX + mSelectDraggingOffset.x,
+                                contentY + mSelectDraggingOffset.y);
+                        updateWebkitSelection();
+                        mLastTouchX = x;
+                        mLastTouchY = y;
+                        invalidate();
+                    }
+                    break;
+                }
+
+                // pass the touch events from UI thread to WebCore thread
+                if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
+                        || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
+                    TouchEventData ted = new TouchEventData();
+                    ted.mAction = action;
+                    ted.mIds = new int[1];
+                    ted.mIds[0] = ev.getPointerId(0);
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
+                    ted.mPointsInView = new Point[1];
+                    ted.mPointsInView[0] = new Point(x, y);
+                    ted.mMetaState = ev.getMetaState();
+                    ted.mReprocess = mDeferTouchProcess;
+                    ted.mNativeLayer = mCurrentScrollingLayerId;
+                    ted.mNativeLayerRect.set(mScrollingLayerRect);
+                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
+                    mTouchEventQueue.preQueueTouchEventData(ted);
+                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                    mLastSentTouchTime = eventTime;
+                    if (mDeferTouchProcess) {
+                        break;
+                    }
+                    if (firstMove && !inFullScreenMode()) {
+                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                                .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
+                                        action, 0), TAP_TIMEOUT);
+                    }
+                }
+                if (mTouchMode == TOUCH_DONE_MODE
+                        || mPreventDefault == PREVENT_DEFAULT_YES) {
+                    // no dragging during scroll zoom animation, or when prevent
+                    // default is yes
+                    break;
+                }
+                if (mVelocityTracker == null) {
+                    Log.e(LOGTAG, "Got null mVelocityTracker when "
+                            + "mPreventDefault = " + mPreventDefault
+                            + " mDeferTouchProcess = " + mDeferTouchProcess
+                            + " mTouchMode = " + mTouchMode);
+                } else {
+                    mVelocityTracker.addMovement(ev);
+                }
+
+                if (mTouchMode != TOUCH_DRAG_MODE &&
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+
+                    if (!mConfirmMove) {
+                        break;
+                    }
+
+                    if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
+                            || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
+                        // track mLastTouchTime as we may need to do fling at
+                        // ACTION_UP
+                        mLastTouchTime = eventTime;
+                        break;
+                    }
+
+                    // Only lock dragging to one axis if we don't have a scale in progress.
+                    // Scaling implies free-roaming movement. Note this is only ever a question
+                    // if mZoomManager.supportsPanDuringZoom() is true.
+                    final ScaleGestureDetector detector =
+                      mZoomManager.getMultiTouchGestureDetector();
+                    mAverageAngle = calculateDragAngle(deltaX, deltaY);
+                    if (detector == null || !detector.isInProgress()) {
+                        // if it starts nearly horizontal or vertical, enforce it
+                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
+                            mSnapScrollMode = SNAP_X;
+                            mSnapPositive = deltaX > 0;
+                            mAverageAngle = ANGLE_HORIZ;
+                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
+                            mSnapScrollMode = SNAP_Y;
+                            mSnapPositive = deltaY > 0;
+                            mAverageAngle = ANGLE_VERT;
+                        }
+                    }
+
+                    mTouchMode = TOUCH_DRAG_MODE;
+                    mLastTouchX = x;
+                    mLastTouchY = y;
+                    deltaX = 0;
+                    deltaY = 0;
+
+                    startScrollingLayer(x, y);
+                    startDrag();
+                }
+
+                // do pan
+                boolean done = false;
+                boolean keepScrollBarsVisible = false;
+                if (deltaX == 0 && deltaY == 0) {
+                    keepScrollBarsVisible = done = true;
+                } else {
+                    mAverageAngle +=
+                        (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
+                        / MMA_WEIGHT_N;
+                    if (mSnapScrollMode != SNAP_NONE) {
+                        if (mSnapScrollMode == SNAP_Y) {
+                            // radical change means getting out of snap mode
+                            if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
+                                mSnapScrollMode = SNAP_NONE;
+                            }
+                        }
+                        if (mSnapScrollMode == SNAP_X) {
+                            // radical change means getting out of snap mode
+                            if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
+                                mSnapScrollMode = SNAP_NONE;
+                            }
+                        }
+                    } else {
+                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
+                            mSnapScrollMode = SNAP_X;
+                            mSnapPositive = deltaX > 0;
+                            mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
+                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
+                            mSnapScrollMode = SNAP_Y;
+                            mSnapPositive = deltaY > 0;
+                            mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
+                        }
+                    }
+                    if (mSnapScrollMode != SNAP_NONE) {
+                        if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
+                            deltaY = 0;
+                        } else {
+                            deltaX = 0;
+                        }
+                    }
+                    mLastTouchX = x;
+                    mLastTouchY = y;
+
+                    if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
+                        mHeldMotionless = MOTIONLESS_FALSE;
+                        nativeSetIsScrolling(true);
+                    } else {
+                        mHeldMotionless = MOTIONLESS_TRUE;
+                        nativeSetIsScrolling(false);
+                        keepScrollBarsVisible = true;
+                    }
+
+                    mLastTouchTime = eventTime;
+                }
+
+                doDrag(deltaX, deltaY);
+
+                // Turn off scrollbars when dragging a layer.
+                if (keepScrollBarsVisible &&
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+                    if (mHeldMotionless != MOTIONLESS_TRUE) {
+                        mHeldMotionless = MOTIONLESS_TRUE;
+                        invalidate();
+                    }
+                    // keep the scrollbar on the screen even there is no scroll
+                    mWebViewPrivate.awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
+                            false);
+                    // Post a message so that we'll keep them alive while we're not scrolling.
+                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                            .obtainMessage(AWAKEN_SCROLL_BARS),
+                            ViewConfiguration.getScrollDefaultDelay());
+                    // return false to indicate that we can't pan out of the
+                    // view space
+                    return !done;
+                } else {
+                    mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_UP: {
+                if (!mWebView.isFocused()) mWebView.requestFocus();
+                // pass the touch events from UI thread to WebCore thread
+                if (shouldForwardTouchEvent()) {
+                    TouchEventData ted = new TouchEventData();
+                    ted.mIds = new int[1];
+                    ted.mIds[0] = ev.getPointerId(0);
+                    ted.mAction = action;
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
+                    ted.mPointsInView = new Point[1];
+                    ted.mPointsInView[0] = new Point(x, y);
+                    ted.mMetaState = ev.getMetaState();
+                    ted.mReprocess = mDeferTouchProcess;
+                    ted.mNativeLayer = mCurrentScrollingLayerId;
+                    ted.mNativeLayerRect.set(mScrollingLayerRect);
+                    ted.mSequence = mTouchEventQueue.nextTouchSequence();
+                    mTouchEventQueue.preQueueTouchEventData(ted);
+                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                }
+                mLastTouchUpTime = eventTime;
+                if (mSentAutoScrollMessage) {
+                    mAutoScrollX = mAutoScrollY = 0;
+                }
+                switch (mTouchMode) {
+                    case TOUCH_DOUBLE_TAP_MODE: // double tap
+                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                        if (inFullScreenMode() || mDeferTouchProcess) {
+                            TouchEventData ted = new TouchEventData();
+                            ted.mIds = new int[1];
+                            ted.mIds[0] = ev.getPointerId(0);
+                            ted.mAction = WebViewCore.ACTION_DOUBLETAP;
+                            ted.mPoints = new Point[1];
+                            ted.mPoints[0] = new Point(contentX, contentY);
+                            ted.mPointsInView = new Point[1];
+                            ted.mPointsInView[0] = new Point(x, y);
+                            ted.mMetaState = ev.getMetaState();
+                            ted.mReprocess = mDeferTouchProcess;
+                            ted.mNativeLayer = nativeScrollableLayer(
+                                    contentX, contentY,
+                                    ted.mNativeLayerRect, null);
+                            ted.mSequence = mTouchEventQueue.nextTouchSequence();
+                            mTouchEventQueue.preQueueTouchEventData(ted);
+                            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                        } else if (mPreventDefault != PREVENT_DEFAULT_YES){
+                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
+                            mTouchMode = TOUCH_DONE_MODE;
+                        }
+                        break;
+                    case TOUCH_INIT_MODE: // tap
+                    case TOUCH_SHORTPRESS_START_MODE:
+                    case TOUCH_SHORTPRESS_MODE:
+                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                        if (mConfirmMove) {
+                            Log.w(LOGTAG, "Miss a drag as we are waiting for" +
+                                    " WebCore's response for touch down.");
+                            if (mPreventDefault != PREVENT_DEFAULT_YES
+                                    && (computeMaxScrollX() > 0
+                                            || computeMaxScrollY() > 0)) {
+                                // If the user has performed a very quick touch
+                                // sequence it is possible that we may get here
+                                // before WebCore has had a chance to process the events.
+                                // In this case, any call to preventDefault in the
+                                // JS touch handler will not have been executed yet.
+                                // Hence we will see both the UI (now) and WebCore
+                                // (when context switches) handling the event,
+                                // regardless of whether the web developer actually
+                                // doeses preventDefault in their touch handler. This
+                                // is the nature of our asynchronous touch model.
+
+                                // we will not rewrite drag code here, but we
+                                // will try fling if it applies.
+                                WebViewCore.reducePriority();
+                                // to get better performance, pause updating the
+                                // picture
+                                WebViewCore.pauseUpdatePicture(mWebViewCore);
+                                // fall through to TOUCH_DRAG_MODE
+                            } else {
+                                // WebKit may consume the touch event and modify
+                                // DOM. drawContentPicture() will be called with
+                                // animateSroll as true for better performance.
+                                // Force redraw in high-quality.
+                                invalidate();
+                                break;
+                            }
+                        } else {
+                            if (mSelectingText) {
+                                // tapping on selection or controls does nothing
+                                if (!mSelectionStarted) {
+                                    selectionDone();
+                                }
+                                break;
+                            }
+                            // only trigger double tap if the WebView is
+                            // scalable
+                            if (mTouchMode == TOUCH_INIT_MODE
+                                    && (canZoomIn() || canZoomOut())) {
+                                mPrivateHandler.sendEmptyMessageDelayed(
+                                        RELEASE_SINGLE_TAP, ViewConfiguration
+                                                .getDoubleTapTimeout());
+                            } else {
+                                doShortPress();
+                            }
+                            break;
+                        }
+                    case TOUCH_DRAG_MODE:
+                    case TOUCH_DRAG_LAYER_MODE:
+                        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+                        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+                        // if the user waits a while w/o moving before the
+                        // up, we don't want to do a fling
+                        if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
+                            if (mVelocityTracker == null) {
+                                Log.e(LOGTAG, "Got null mVelocityTracker when "
+                                        + "mPreventDefault = "
+                                        + mPreventDefault
+                                        + " mDeferTouchProcess = "
+                                        + mDeferTouchProcess);
+                            } else {
+                                mVelocityTracker.addMovement(ev);
+                            }
+                            // set to MOTIONLESS_IGNORE so that it won't keep
+                            // removing and sending message in
+                            // drawCoreAndCursorRing()
+                            mHeldMotionless = MOTIONLESS_IGNORE;
+                            doFling();
+                            break;
+                        } else {
+                            if (mScroller.springBack(getScrollX(), getScrollY(), 0,
+                                    computeMaxScrollX(), 0,
+                                    computeMaxScrollY())) {
+                                invalidate();
+                            }
+                        }
+                        // redraw in high-quality, as we're done dragging
+                        mHeldMotionless = MOTIONLESS_TRUE;
+                        invalidate();
+                        // fall through
+                    case TOUCH_DRAG_START_MODE:
+                        // TOUCH_DRAG_START_MODE should not happen for the real
+                        // device as we almost certain will get a MOVE. But this
+                        // is possible on emulator.
+                        mLastVelocity = 0;
+                        WebViewCore.resumePriority();
+                        if (!mSelectingText) {
+                            WebViewCore.resumeUpdatePicture(mWebViewCore);
+                        }
+                        break;
+                }
+                stopTouch();
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                if (mTouchMode == TOUCH_DRAG_MODE) {
+                    mScroller.springBack(getScrollX(), getScrollY(), 0,
+                            computeMaxScrollX(), 0, computeMaxScrollY());
+                    invalidate();
+                }
+                cancelWebCoreTouchEvent(contentX, contentY, false);
+                cancelTouch();
+                break;
+            }
+        }
+        return true;
+    }
+
+    private void passMultiTouchToWebKit(MotionEvent ev, long sequence) {
+        TouchEventData ted = new TouchEventData();
+        ted.mAction = ev.getActionMasked();
+        final int count = ev.getPointerCount();
+        ted.mIds = new int[count];
+        ted.mPoints = new Point[count];
+        ted.mPointsInView = new Point[count];
+        for (int c = 0; c < count; c++) {
+            ted.mIds[c] = ev.getPointerId(c);
+            int x = viewToContentX((int) ev.getX(c) + getScrollX());
+            int y = viewToContentY((int) ev.getY(c) + getScrollY());
+            ted.mPoints[c] = new Point(x, y);
+            ted.mPointsInView[c] = new Point((int) ev.getX(c), (int) ev.getY(c));
+        }
+        if (ted.mAction == MotionEvent.ACTION_POINTER_DOWN
+            || ted.mAction == MotionEvent.ACTION_POINTER_UP) {
+            ted.mActionIndex = ev.getActionIndex();
+        }
+        ted.mMetaState = ev.getMetaState();
+        ted.mReprocess = true;
+        ted.mMotionEvent = MotionEvent.obtain(ev);
+        ted.mSequence = sequence;
+        mTouchEventQueue.preQueueTouchEventData(ted);
+        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+        mWebView.cancelLongPress();
+        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+    }
+
+    void handleMultiTouchInWebView(MotionEvent ev) {
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "multi-touch: " + ev + " at " + ev.getEventTime()
+                + " mTouchMode=" + mTouchMode
+                + " numPointers=" + ev.getPointerCount()
+                + " scrolloffset=(" + getScrollX() + "," + getScrollY() + ")");
+        }
+
+        final ScaleGestureDetector detector =
+            mZoomManager.getMultiTouchGestureDetector();
+
+        // A few apps use WebView but don't instantiate gesture detector.
+        // We don't need to support multi touch for them.
+        if (detector == null) return;
+
+        float x = ev.getX();
+        float y = ev.getY();
+
+        if (mPreventDefault != PREVENT_DEFAULT_YES) {
+            detector.onTouchEvent(ev);
+
+            if (detector.isInProgress()) {
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "detector is in progress");
+                }
+                mLastTouchTime = ev.getEventTime();
+                x = detector.getFocusX();
+                y = detector.getFocusY();
+
+                mWebView.cancelLongPress();
+                mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                if (!mZoomManager.supportsPanDuringZoom()) {
+                    return;
+                }
+                mTouchMode = TOUCH_DRAG_MODE;
+                if (mVelocityTracker == null) {
+                    mVelocityTracker = VelocityTracker.obtain();
+                }
+            }
+        }
+
+        int action = ev.getActionMasked();
+        if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            cancelTouch();
+            action = MotionEvent.ACTION_DOWN;
+        } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
+            // set mLastTouchX/Y to the remaining points for multi-touch.
+            mLastTouchX = Math.round(x);
+            mLastTouchY = Math.round(y);
+        } else if (action == MotionEvent.ACTION_MOVE) {
+            // negative x or y indicate it is on the edge, skip it.
+            if (x < 0 || y < 0) {
+                return;
+            }
+        }
+
+        handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
+    }
+
+    private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
+        if (shouldForwardTouchEvent()) {
+            if (removeEvents) {
+                mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
+            }
+            TouchEventData ted = new TouchEventData();
+            ted.mIds = new int[1];
+            ted.mIds[0] = 0;
+            ted.mPoints = new Point[1];
+            ted.mPoints[0] = new Point(x, y);
+            ted.mPointsInView = new Point[1];
+            int viewX = contentToViewX(x) - getScrollX();
+            int viewY = contentToViewY(y) - getScrollY();
+            ted.mPointsInView[0] = new Point(viewX, viewY);
+            ted.mAction = MotionEvent.ACTION_CANCEL;
+            ted.mNativeLayer = nativeScrollableLayer(
+                    x, y, ted.mNativeLayerRect, null);
+            ted.mSequence = mTouchEventQueue.nextTouchSequence();
+            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+            mPreventDefault = PREVENT_DEFAULT_IGNORE;
+
+            if (removeEvents) {
+                // Mark this after sending the message above; we should
+                // be willing to ignore the cancel event that we just sent.
+                mTouchEventQueue.ignoreCurrentlyMissingEvents();
+            }
+        }
+    }
+
+    private void startTouch(float x, float y, long eventTime) {
+        // Remember where the motion event started
+        mStartTouchX = mLastTouchX = Math.round(x);
+        mStartTouchY = mLastTouchY = Math.round(y);
+        mLastTouchTime = eventTime;
+        mVelocityTracker = VelocityTracker.obtain();
+        mSnapScrollMode = SNAP_NONE;
+        mPrivateHandler.sendEmptyMessageDelayed(UPDATE_SELECTION,
+                ViewConfiguration.getTapTimeout());
+    }
+
+    private void startDrag() {
+        WebViewCore.reducePriority();
+        // to get better performance, pause updating the picture
+        WebViewCore.pauseUpdatePicture(mWebViewCore);
+        nativeSetIsScrolling(true);
+
+        if (!mDragFromTextInput) {
+            nativeHideCursor();
+        }
+
+        if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
+                || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
+            mZoomManager.invokeZoomPicker();
+        }
+    }
+
+    private void doDrag(int deltaX, int deltaY) {
+        if ((deltaX | deltaY) != 0) {
+            int oldX = getScrollX();
+            int oldY = getScrollY();
+            int rangeX = computeMaxScrollX();
+            int rangeY = computeMaxScrollY();
+            // Check for the original scrolling layer in case we change
+            // directions.  mTouchMode might be TOUCH_DRAG_MODE if we have
+            // reached the edge of a layer but mScrollingLayer will be non-zero
+            // if we initiated the drag on a layer.
+            if (mCurrentScrollingLayerId != 0) {
+                final int contentX = viewToContentDimension(deltaX);
+                final int contentY = viewToContentDimension(deltaY);
+
+                // Check the scrolling bounds to see if we will actually do any
+                // scrolling.  The rectangle is in document coordinates.
+                final int maxX = mScrollingLayerRect.right;
+                final int maxY = mScrollingLayerRect.bottom;
+                final int resultX = Math.max(0,
+                        Math.min(mScrollingLayerRect.left + contentX, maxX));
+                final int resultY = Math.max(0,
+                        Math.min(mScrollingLayerRect.top + contentY, maxY));
+
+                if (resultX != mScrollingLayerRect.left ||
+                        resultY != mScrollingLayerRect.top) {
+                    // In case we switched to dragging the page.
+                    mTouchMode = TOUCH_DRAG_LAYER_MODE;
+                    deltaX = contentX;
+                    deltaY = contentY;
+                    oldX = mScrollingLayerRect.left;
+                    oldY = mScrollingLayerRect.top;
+                    rangeX = maxX;
+                    rangeY = maxY;
+                } else {
+                    // Scroll the main page if we are not going to scroll the
+                    // layer.  This does not reset mScrollingLayer in case the
+                    // user changes directions and the layer can scroll the
+                    // other way.
+                    mTouchMode = TOUCH_DRAG_MODE;
+                }
+            }
+
+            if (mOverScrollGlow != null) {
+                mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
+            }
+
+            mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY,
+                    rangeX, rangeY,
+                    mOverscrollDistance, mOverscrollDistance, true);
+            if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
+                invalidate();
+            }
+        }
+        mZoomManager.keepZoomPickerVisible();
+    }
+
+    private void stopTouch() {
+        if (mScroller.isFinished() && !mSelectingText
+                && (mTouchMode == TOUCH_DRAG_MODE || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
+            WebViewCore.resumePriority();
+            WebViewCore.resumeUpdatePicture(mWebViewCore);
+            nativeSetIsScrolling(false);
+        }
+
+        // we also use mVelocityTracker == null to tell us that we are
+        // not "moving around", so we can take the slower/prettier
+        // mode in the drawing code
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+
+        // Release any pulled glows
+        if (mOverScrollGlow != null) {
+            mOverScrollGlow.releaseAll();
+        }
+
+        if (mSelectingText) {
+            mSelectionStarted = false;
+            syncSelectionCursors();
+            if (mIsCaretSelection) {
+                resetCaretTimer();
+                showPasteWindow();
+            }
+            invalidate();
+        }
+    }
+
+    private void cancelTouch() {
+        // we also use mVelocityTracker == null to tell us that we are
+        // not "moving around", so we can take the slower/prettier
+        // mode in the drawing code
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+
+        if ((mTouchMode == TOUCH_DRAG_MODE
+                || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
+            WebViewCore.resumePriority();
+            WebViewCore.resumeUpdatePicture(mWebViewCore);
+            nativeSetIsScrolling(false);
+        }
+        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
+        mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+        if (sDisableNavcache) {
+            removeTouchHighlight();
+        }
+        mHeldMotionless = MOTIONLESS_TRUE;
+        mTouchMode = TOUCH_DONE_MODE;
+        nativeHideCursor();
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_SCROLL: {
+                    final float vscroll;
+                    final float hscroll;
+                    if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
+                        vscroll = 0;
+                        hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                    } else {
+                        vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                        hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+                    }
+                    if (hscroll != 0 || vscroll != 0) {
+                        final int vdelta = (int) (vscroll *
+                                mWebViewPrivate.getVerticalScrollFactor());
+                        final int hdelta = (int) (hscroll *
+                                mWebViewPrivate.getHorizontalScrollFactor());
+                        if (pinScrollBy(hdelta, vdelta, false, 0)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return mWebViewPrivate.super_onGenericMotionEvent(event);
+    }
+
+    private long mTrackballFirstTime = 0;
+    private long mTrackballLastTime = 0;
+    private float mTrackballRemainsX = 0.0f;
+    private float mTrackballRemainsY = 0.0f;
+    private int mTrackballXMove = 0;
+    private int mTrackballYMove = 0;
+    private boolean mSelectingText = false;
+    private boolean mSelectionStarted = false;
+    private static final int TRACKBALL_KEY_TIMEOUT = 1000;
+    private static final int TRACKBALL_TIMEOUT = 200;
+    private static final int TRACKBALL_WAIT = 100;
+    private static final int TRACKBALL_SCALE = 400;
+    private static final int TRACKBALL_SCROLL_COUNT = 5;
+    private static final int TRACKBALL_MOVE_COUNT = 10;
+    private static final int TRACKBALL_MULTIPLIER = 3;
+    private static final int SELECT_CURSOR_OFFSET = 16;
+    private static final int SELECT_SCROLL = 5;
+    private int mSelectX = 0;
+    private int mSelectY = 0;
+    private boolean mFocusSizeChanged = false;
+    private boolean mTrackballDown = false;
+    private long mTrackballUpTime = 0;
+    private long mLastCursorTime = 0;
+    private Rect mLastCursorBounds;
+
+    // Set by default; BrowserActivity clears to interpret trackball data
+    // directly for movement. Currently, the framework only passes
+    // arrow key events, not trackball events, from one child to the next
+    private boolean mMapTrackballToArrowKeys = true;
+
+    private DrawData mDelaySetPicture;
+    private DrawData mLoadedPicture;
+
+    public void setMapTrackballToArrowKeys(boolean setMap) {
+        checkThread();
+        mMapTrackballToArrowKeys = setMap;
+    }
+
+    void resetTrackballTime() {
+        mTrackballLastTime = 0;
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent ev) {
+        long time = ev.getEventTime();
+        if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
+            if (ev.getY() > 0) pageDown(true);
+            if (ev.getY() < 0) pageUp(true);
+            return true;
+        }
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            if (mSelectingText) {
+                return true; // discard press if copy in progress
+            }
+            mTrackballDown = true;
+            if (mNativeClass == 0) {
+                return false;
+            }
+            if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
+                    && !mLastCursorBounds.equals(cursorRingBounds())) {
+                nativeSelectBestAt(mLastCursorBounds);
+            }
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
+                        + " time=" + time
+                        + " mLastCursorTime=" + mLastCursorTime);
+            }
+            if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch();
+            return false; // let common code in onKeyDown at it
+        }
+        if (ev.getAction() == MotionEvent.ACTION_UP) {
+            // LONG_PRESS_CENTER is set in common onKeyDown
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+            mTrackballDown = false;
+            mTrackballUpTime = time;
+            if (mSelectingText) {
+                copySelection();
+                selectionDone();
+                return true; // discard press if copy in progress
+            }
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
+                        + " time=" + time
+                );
+            }
+            return false; // let common code in onKeyUp at it
+        }
+        if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
+                AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
+            return false;
+        }
+        if (mTrackballDown) {
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
+            return true; // discard move if trackball is down
+        }
+        if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
+            return true;
+        }
+        // TODO: alternatively we can do panning as touch does
+        switchOutDrawHistory();
+        if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "onTrackballEvent time="
+                        + time + " last=" + mTrackballLastTime);
+            }
+            mTrackballFirstTime = time;
+            mTrackballXMove = mTrackballYMove = 0;
+        }
+        mTrackballLastTime = time;
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
+        }
+        mTrackballRemainsX += ev.getX();
+        mTrackballRemainsY += ev.getY();
+        doTrackball(time, ev.getMetaState());
+        return true;
+    }
+
+    private int scaleTrackballX(float xRate, int width) {
+        int xMove = (int) (xRate / TRACKBALL_SCALE * width);
+        int nextXMove = xMove;
+        if (xMove > 0) {
+            if (xMove > mTrackballXMove) {
+                xMove -= mTrackballXMove;
+            }
+        } else if (xMove < mTrackballXMove) {
+            xMove -= mTrackballXMove;
+        }
+        mTrackballXMove = nextXMove;
+        return xMove;
+    }
+
+    private int scaleTrackballY(float yRate, int height) {
+        int yMove = (int) (yRate / TRACKBALL_SCALE * height);
+        int nextYMove = yMove;
+        if (yMove > 0) {
+            if (yMove > mTrackballYMove) {
+                yMove -= mTrackballYMove;
+            }
+        } else if (yMove < mTrackballYMove) {
+            yMove -= mTrackballYMove;
+        }
+        mTrackballYMove = nextYMove;
+        return yMove;
+    }
+
+    private int keyCodeToSoundsEffect(int keyCode) {
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return SoundEffectConstants.NAVIGATION_UP;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return SoundEffectConstants.NAVIGATION_RIGHT;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                return SoundEffectConstants.NAVIGATION_DOWN;
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                return SoundEffectConstants.NAVIGATION_LEFT;
+        }
+        throw new IllegalArgumentException("keyCode must be one of " +
+                "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
+                "KEYCODE_DPAD_LEFT}.");
+    }
+
+    private void doTrackball(long time, int metaState) {
+        int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
+        if (elapsed == 0) {
+            elapsed = TRACKBALL_TIMEOUT;
+        }
+        float xRate = mTrackballRemainsX * 1000 / elapsed;
+        float yRate = mTrackballRemainsY * 1000 / elapsed;
+        int viewWidth = getViewWidth();
+        int viewHeight = getViewHeight();
+        float ax = Math.abs(xRate);
+        float ay = Math.abs(yRate);
+        float maxA = Math.max(ax, ay);
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
+                    + " xRate=" + xRate
+                    + " yRate=" + yRate
+                    + " mTrackballRemainsX=" + mTrackballRemainsX
+                    + " mTrackballRemainsY=" + mTrackballRemainsY);
+        }
+        int width = mContentWidth - viewWidth;
+        int height = mContentHeight - viewHeight;
+        if (width < 0) width = 0;
+        if (height < 0) height = 0;
+        ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
+        ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
+        maxA = Math.max(ax, ay);
+        int count = Math.max(0, (int) maxA);
+        int oldScrollX = getScrollX();
+        int oldScrollY = getScrollY();
+        if (count > 0) {
+            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
+                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
+                    mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
+                    KeyEvent.KEYCODE_DPAD_RIGHT;
+            count = Math.min(count, TRACKBALL_MOVE_COUNT);
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
+                        + " count=" + count
+                        + " mTrackballRemainsX=" + mTrackballRemainsX
+                        + " mTrackballRemainsY=" + mTrackballRemainsY);
+            }
+            if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
+                for (int i = 0; i < count; i++) {
+                    letPageHandleNavKey(selectKeyCode, time, true, metaState);
+                }
+                letPageHandleNavKey(selectKeyCode, time, false, metaState);
+            } else if (navHandledKey(selectKeyCode, count, false, time)) {
+                mWebView.playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
+            }
+            mTrackballRemainsX = mTrackballRemainsY = 0;
+        }
+        if (count >= TRACKBALL_SCROLL_COUNT) {
+            int xMove = scaleTrackballX(xRate, width);
+            int yMove = scaleTrackballY(yRate, height);
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "doTrackball pinScrollBy"
+                        + " count=" + count
+                        + " xMove=" + xMove + " yMove=" + yMove
+                        + " mScrollX-oldScrollX=" + (getScrollX()-oldScrollX)
+                        + " mScrollY-oldScrollY=" + (getScrollY()-oldScrollY)
+                        );
+            }
+            if (Math.abs(getScrollX() - oldScrollX) > Math.abs(xMove)) {
+                xMove = 0;
+            }
+            if (Math.abs(getScrollY() - oldScrollY) > Math.abs(yMove)) {
+                yMove = 0;
+            }
+            if (xMove != 0 || yMove != 0) {
+                pinScrollBy(xMove, yMove, true, 0);
+            }
+        }
+    }
+
+    /**
+     * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
+     * @return Maximum horizontal scroll position within real content
+     */
+    int computeMaxScrollX() {
+        return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
+    }
+
+    /**
+     * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
+     * @return Maximum vertical scroll position within real content
+     */
+    int computeMaxScrollY() {
+        return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
+                - getViewHeightWithTitle(), 0);
+    }
+
+    boolean updateScrollCoordinates(int x, int y) {
+        int oldX = getScrollX();
+        int oldY = getScrollY();
+        setScrollXRaw(x);
+        setScrollYRaw(y);
+        if (oldX != getScrollX() || oldY != getScrollY()) {
+            mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void flingScroll(int vx, int vy) {
+        checkThread();
+        mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0,
+                computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
+        invalidate();
+    }
+
+    private void doFling() {
+        if (mVelocityTracker == null) {
+            return;
+        }
+        int maxX = computeMaxScrollX();
+        int maxY = computeMaxScrollY();
+
+        mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
+        int vx = (int) mVelocityTracker.getXVelocity();
+        int vy = (int) mVelocityTracker.getYVelocity();
+
+        int scrollX = getScrollX();
+        int scrollY = getScrollY();
+        int overscrollDistance = mOverscrollDistance;
+        int overflingDistance = mOverflingDistance;
+
+        // Use the layer's scroll data if applicable.
+        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+            scrollX = mScrollingLayerRect.left;
+            scrollY = mScrollingLayerRect.top;
+            maxX = mScrollingLayerRect.right;
+            maxY = mScrollingLayerRect.bottom;
+            // No overscrolling for layers.
+            overscrollDistance = overflingDistance = 0;
+        }
+
+        if (mSnapScrollMode != SNAP_NONE) {
+            if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
+                vy = 0;
+            } else {
+                vx = 0;
+            }
+        }
+        if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
+            WebViewCore.resumePriority();
+            if (!mSelectingText) {
+                WebViewCore.resumeUpdatePicture(mWebViewCore);
+            }
+            if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
+                invalidate();
+            }
+            return;
+        }
+        float currentVelocity = mScroller.getCurrVelocity();
+        float velocity = (float) Math.hypot(vx, vy);
+        if (mLastVelocity > 0 && currentVelocity > 0 && velocity
+                > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
+            float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
+                    - Math.atan2(vy, vx)));
+            final float circle = (float) (Math.PI) * 2.0f;
+            if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
+                vx += currentVelocity * mLastVelX / mLastVelocity;
+                vy += currentVelocity * mLastVelY / mLastVelocity;
+                velocity = (float) Math.hypot(vx, vy);
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
+                }
+            } else if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "doFling missed " + deltaR / circle);
+            }
+        } else if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "doFling start last=" + mLastVelocity
+                    + " current=" + currentVelocity
+                    + " vx=" + vx + " vy=" + vy
+                    + " maxX=" + maxX + " maxY=" + maxY
+                    + " scrollX=" + scrollX + " scrollY=" + scrollY
+                    + " layer=" + mCurrentScrollingLayerId);
+        }
+
+        // Allow sloppy flings without overscrolling at the edges.
+        if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
+            vx = 0;
+        }
+        if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
+            vy = 0;
+        }
+
+        if (overscrollDistance < overflingDistance) {
+            if ((vx > 0 && scrollX == -overscrollDistance) ||
+                    (vx < 0 && scrollX == maxX + overscrollDistance)) {
+                vx = 0;
+            }
+            if ((vy > 0 && scrollY == -overscrollDistance) ||
+                    (vy < 0 && scrollY == maxY + overscrollDistance)) {
+                vy = 0;
+            }
+        }
+
+        mLastVelX = vx;
+        mLastVelY = vy;
+        mLastVelocity = velocity;
+
+        // no horizontal overscroll if the content just fits
+        mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
+                maxX == 0 ? 0 : overflingDistance, overflingDistance);
+        // Duration is calculated based on velocity. With range boundaries and overscroll
+        // we may not know how long the final animation will take. (Hence the deprecation
+        // warning on the call below.) It's not a big deal for scroll bars but if webcore
+        // resumes during this effect we will take a performance hit. See computeScroll;
+        // we resume webcore there when the animation is finished.
+        final int time = mScroller.getDuration();
+
+        // Suppress scrollbars for layer scrolling.
+        if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+            mWebViewPrivate.awakenScrollBars(time);
+        }
+
+        invalidate();
+    }
+
+    /**
+     * Returns a view containing zoom controls i.e. +/- buttons. The caller is
+     * in charge of installing this view to the view hierarchy. This view will
+     * become visible when the user starts scrolling via touch and fade away if
+     * the user does not interact with it.
+     * <p/>
+     * API version 3 introduces a built-in zoom mechanism that is shown
+     * automatically by the MapView. This is the preferred approach for
+     * showing the zoom UI.
+     *
+     * @deprecated The built-in zoom mechanism is preferred, see
+     *             {@link WebSettings#setBuiltInZoomControls(boolean)}.
+     */
+    @Deprecated
+    public View getZoomControls() {
+        checkThread();
+        if (!getSettings().supportZoom()) {
+            Log.w(LOGTAG, "This WebView doesn't support zoom.");
+            return null;
+        }
+        return mZoomManager.getExternalZoomPicker();
+    }
+
+    void dismissZoomControl() {
+        mZoomManager.dismissZoomPicker();
+    }
+
+    float getDefaultZoomScale() {
+        return mZoomManager.getDefaultScale();
+    }
+
+    /**
+     * Return the overview scale of the WebView
+     * @return The overview scale.
+     */
+    float getZoomOverviewScale() {
+        return mZoomManager.getZoomOverviewScale();
+    }
+
+    /**
+     * @return TRUE if the WebView can be zoomed in.
+     */
+    public boolean canZoomIn() {
+        checkThread();
+        return mZoomManager.canZoomIn();
+    }
+
+    /**
+     * @return TRUE if the WebView can be zoomed out.
+     */
+    public boolean canZoomOut() {
+        checkThread();
+        return mZoomManager.canZoomOut();
+    }
+
+    /**
+     * Perform zoom in in the webview
+     * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
+     */
+    public boolean zoomIn() {
+        checkThread();
+        return mZoomManager.zoomIn();
+    }
+
+    /**
+     * Perform zoom out in the webview
+     * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
+     */
+    public boolean zoomOut() {
+        checkThread();
+        return mZoomManager.zoomOut();
+    }
+
+    /**
+     * This selects the best clickable target at mLastTouchX and mLastTouchY
+     * and calls showCursorTimed on the native side
+     */
+    private void updateSelection() {
+        if (mNativeClass == 0 || sDisableNavcache) {
+            return;
+        }
+        mPrivateHandler.removeMessages(UPDATE_SELECTION);
+        // mLastTouchX and mLastTouchY are the point in the current viewport
+        int contentX = viewToContentX(mLastTouchX + getScrollX());
+        int contentY = viewToContentY(mLastTouchY + getScrollY());
+        int slop = viewToContentDimension(mNavSlop);
+        Rect rect = new Rect(contentX - slop, contentY - slop,
+                contentX + slop, contentY + slop);
+        nativeSelectBestAt(rect);
+        mInitialHitTestResult = hitTestResult(null);
+    }
+
+    /**
+     * Scroll the focused text field to match the WebTextView
+     * @param xPercent New x position of the WebTextView from 0 to 1.
+     */
+    /*package*/ void scrollFocusedTextInputX(float xPercent) {
+        if (!inEditingMode() || mWebViewCore == null) {
+            return;
+        }
+        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
+                new Float(xPercent));
+    }
+
+    /**
+     * Scroll the focused textarea vertically to match the WebTextView
+     * @param y New y position of the WebTextView in view coordinates
+     */
+    /* package */ void scrollFocusedTextInputY(int y) {
+        if (!inEditingMode() || mWebViewCore == null) {
+            return;
+        }
+        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, viewToContentDimension(y));
+    }
+
+    /**
+     * Set our starting point and time for a drag from the WebTextView.
+     */
+    /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
+        if (!inEditingMode()) {
+            return;
+        }
+        mLastTouchX = Math.round(x + mWebTextView.getLeft() - getScrollX());
+        mLastTouchY = Math.round(y + mWebTextView.getTop() - getScrollY());
+        mLastTouchTime = eventTime;
+        if (!mScroller.isFinished()) {
+            abortAnimation();
+        }
+        mSnapScrollMode = SNAP_NONE;
+        mVelocityTracker = VelocityTracker.obtain();
+        mTouchMode = TOUCH_DRAG_START_MODE;
+    }
+
+    /**
+     * Given a motion event from the WebTextView, set its location to our
+     * coordinates, and handle the event.
+     */
+    /*package*/ boolean textFieldDrag(MotionEvent event) {
+        if (!inEditingMode()) {
+            return false;
+        }
+        mDragFromTextInput = true;
+        event.offsetLocation((mWebTextView.getLeft() - getScrollX()),
+                (mWebTextView.getTop() - getScrollY()));
+        boolean result = onTouchEvent(event);
+        mDragFromTextInput = false;
+        return result;
+    }
+
+    /**
+     * Due a touch up from a WebTextView.  This will be handled by webkit to
+     * change the selection.
+     * @param event MotionEvent in the WebTextView's coordinates.
+     */
+    /*package*/ void touchUpOnTextField(MotionEvent event) {
+        if (!inEditingMode()) {
+            return;
+        }
+        int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
+        int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
+        int slop = viewToContentDimension(mNavSlop);
+        nativeMotionUp(x, y, slop);
+    }
+
+    /**
+     * Called when pressing the center key or trackball on a textfield.
+     */
+    /*package*/ void centerKeyPressOnTextField() {
+        mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
+                    nativeCursorNodePointer());
+    }
+
+    private void doShortPress() {
+        if (mNativeClass == 0) {
+            return;
+        }
+        if (mPreventDefault == PREVENT_DEFAULT_YES) {
+            return;
+        }
+        mTouchMode = TOUCH_DONE_MODE;
+        updateSelection();
+        switchOutDrawHistory();
+        // mLastTouchX and mLastTouchY are the point in the current viewport
+        int contentX = viewToContentX(mLastTouchX + getScrollX());
+        int contentY = viewToContentY(mLastTouchY + getScrollY());
+        int slop = viewToContentDimension(mNavSlop);
+        if (sDisableNavcache && !mTouchHighlightRegion.isEmpty()) {
+            // set mTouchHighlightRequested to 0 to cause an immediate
+            // drawing of the touch rings
+            mTouchHighlightRequested = 0;
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
+            mPrivateHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    removeTouchHighlight();
+                }
+            }, ViewConfiguration.getPressedStateDuration());
+        }
+        if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
+            mWebView.playSoundEffect(SoundEffectConstants.CLICK);
+            overrideLoading(mFocusedNode.mIntentUrl);
+        } else if (sDisableNavcache) {
+            WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
+            // use "0" as generation id to inform WebKit to use the same x/y as
+            // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
+            touchUpData.mMoveGeneration = 0;
+            mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
+        } else if (nativePointInNavCache(contentX, contentY, slop)) {
+            WebViewCore.MotionUpData motionUpData = new WebViewCore
+                    .MotionUpData();
+            motionUpData.mFrame = nativeCacheHitFramePointer();
+            motionUpData.mNode = nativeCacheHitNodePointer();
+            motionUpData.mBounds = nativeCacheHitNodeBounds();
+            motionUpData.mX = contentX;
+            motionUpData.mY = contentY;
+            mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
+                    motionUpData);
+        } else {
+            doMotionUp(contentX, contentY);
+        }
+    }
+
+    private void doMotionUp(int contentX, int contentY) {
+        int slop = viewToContentDimension(mNavSlop);
+        if (nativeMotionUp(contentX, contentY, slop) && mLogEvent) {
+            EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
+        }
+        if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
+            mWebView.playSoundEffect(SoundEffectConstants.CLICK);
+        }
+    }
+
+    void sendPluginDrawMsg() {
+        mWebViewCore.sendMessage(EventHub.PLUGIN_SURFACE_READY);
+    }
+
+    /**
+     * Returns plugin bounds if x/y in content coordinates corresponds to a
+     * plugin. Otherwise a NULL rectangle is returned.
+     */
+    Rect getPluginBounds(int x, int y) {
+        int slop = viewToContentDimension(mNavSlop);
+        if (nativePointInNavCache(x, y, slop) && nativeCacheHitIsPlugin()) {
+            return nativeCacheHitNodeBounds();
+        } else {
+            return null;
+        }
+    }
+
+    /*
+     * Return true if the rect (e.g. plugin) is fully visible and maximized
+     * inside the WebView.
+     */
+    boolean isRectFitOnScreen(Rect rect) {
+        final int rectWidth = rect.width();
+        final int rectHeight = rect.height();
+        final int viewWidth = getViewWidth();
+        final int viewHeight = getViewHeightWithTitle();
+        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
+        scale = mZoomManager.computeScaleWithLimits(scale);
+        return !mZoomManager.willScaleTriggerZoom(scale)
+                && contentToViewX(rect.left) >= getScrollX()
+                && contentToViewX(rect.right) <= getScrollX() + viewWidth
+                && contentToViewY(rect.top) >= getScrollY()
+                && contentToViewY(rect.bottom) <= getScrollY() + viewHeight;
+    }
+
+    /*
+     * Maximize and center the rectangle, specified in the document coordinate
+     * space, 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. The rect is in document
+     * coordinates
+     */
+    void centerFitRect(Rect rect) {
+        final int rectWidth = rect.width();
+        final int rectHeight = rect.height();
+        final int viewWidth = getViewWidth();
+        final int viewHeight = getViewHeightWithTitle();
+        float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
+                / rectHeight);
+        scale = mZoomManager.computeScaleWithLimits(scale);
+        if (!mZoomManager.willScaleTriggerZoom(scale)) {
+            pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
+                    contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
+                    true, 0);
+        } else {
+            float actualScale = mZoomManager.getScale();
+            float oldScreenX = rect.left * actualScale - getScrollX();
+            float rectViewX = rect.left * scale;
+            float rectViewWidth = rectWidth * scale;
+            float newMaxWidth = mContentWidth * scale;
+            float newScreenX = (viewWidth - rectViewWidth) / 2;
+            // pin the newX to the WebView
+            if (newScreenX > rectViewX) {
+                newScreenX = rectViewX;
+            } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
+                newScreenX = viewWidth - (newMaxWidth - rectViewX);
+            }
+            float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
+                    / (scale - actualScale);
+            float oldScreenY = rect.top * actualScale + getTitleHeight()
+                    - getScrollY();
+            float rectViewY = rect.top * scale + getTitleHeight();
+            float rectViewHeight = rectHeight * scale;
+            float newMaxHeight = mContentHeight * scale + getTitleHeight();
+            float newScreenY = (viewHeight - rectViewHeight) / 2;
+            // pin the newY to the WebView
+            if (newScreenY > rectViewY) {
+                newScreenY = rectViewY;
+            } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
+                newScreenY = viewHeight - (newMaxHeight - rectViewY);
+            }
+            float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
+                    / (scale - actualScale);
+            mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
+            mZoomManager.startZoomAnimation(scale, false);
+        }
+    }
+
+    // Called by JNI to handle a touch on a node representing an email address,
+    // address, or phone number
+    private void overrideLoading(String url) {
+        mCallbackProxy.uiOverrideUrlLoading(url);
+    }
+
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        // FIXME: If a subwindow is showing find, and the user touches the
+        // background window, it can steal focus.
+        if (mFindIsUp) return false;
+        boolean result = false;
+        if (inEditingMode()) {
+            result = mWebTextView.requestFocus(direction,
+                    previouslyFocusedRect);
+        } else {
+            result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
+            if (mWebViewCore.getSettings().getNeedInitialFocus() && !mWebView.isInTouchMode()) {
+                // For cases such as GMail, where we gain focus from a direction,
+                // we want to move to the first available link.
+                // FIXME: If there are no visible links, we may not want to
+                int fakeKeyDirection = 0;
+                switch(direction) {
+                    case View.FOCUS_UP:
+                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
+                        break;
+                    case View.FOCUS_DOWN:
+                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
+                        break;
+                    case View.FOCUS_LEFT:
+                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
+                        break;
+                    case View.FOCUS_RIGHT:
+                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
+                        break;
+                    default:
+                        return result;
+                }
+                if (mNativeClass != 0 && !nativeHasCursorNode()) {
+                    navHandledKey(fakeKeyDirection, 1, true, 0);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+        int measuredHeight = heightSize;
+        int measuredWidth = widthSize;
+
+        // Grab the content size from WebViewCore.
+        int contentHeight = contentToViewDimension(mContentHeight);
+        int contentWidth = contentToViewDimension(mContentWidth);
+
+//        Log.d(LOGTAG, "------- measure " + heightMode);
+
+        if (heightMode != MeasureSpec.EXACTLY) {
+            mHeightCanMeasure = true;
+            measuredHeight = contentHeight;
+            if (heightMode == MeasureSpec.AT_MOST) {
+                // If we are larger than the AT_MOST height, then our height can
+                // no longer be measured and we should scroll internally.
+                if (measuredHeight > heightSize) {
+                    measuredHeight = heightSize;
+                    mHeightCanMeasure = false;
+                    measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
+                }
+            }
+        } else {
+            mHeightCanMeasure = false;
+        }
+        if (mNativeClass != 0) {
+            nativeSetHeightCanMeasure(mHeightCanMeasure);
+        }
+        // For the width, always use the given size unless unspecified.
+        if (widthMode == MeasureSpec.UNSPECIFIED) {
+            mWidthCanMeasure = true;
+            measuredWidth = contentWidth;
+        } else {
+            if (measuredWidth < contentWidth) {
+                measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
+            }
+            mWidthCanMeasure = false;
+        }
+
+        synchronized (this) {
+            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child,
+                                                 Rect rect,
+                                                 boolean immediate) {
+        if (mNativeClass == 0) {
+            return false;
+        }
+        // don't scroll while in zoom animation. When it is done, we will adjust
+        // the necessary components (e.g., WebTextView if it is in editing mode)
+        if (mZoomManager.isFixedLengthAnimationInProgress()) {
+            return false;
+        }
+
+        rect.offset(child.getLeft() - child.getScrollX(),
+                child.getTop() - child.getScrollY());
+
+        Rect content = new Rect(viewToContentX(getScrollX()),
+                viewToContentY(getScrollY()),
+                viewToContentX(getScrollX() + getWidth()
+                - mWebView.getVerticalScrollbarWidth()),
+                viewToContentY(getScrollY() + getViewHeightWithTitle()));
+        content = nativeSubtractLayers(content);
+        int screenTop = contentToViewY(content.top);
+        int screenBottom = contentToViewY(content.bottom);
+        int height = screenBottom - screenTop;
+        int scrollYDelta = 0;
+
+        if (rect.bottom > screenBottom) {
+            int oneThirdOfScreenHeight = height / 3;
+            if (rect.height() > 2 * oneThirdOfScreenHeight) {
+                // If the rectangle is too tall to fit in the bottom two thirds
+                // of the screen, place it at the top.
+                scrollYDelta = rect.top - screenTop;
+            } else {
+                // If the rectangle will still fit on screen, we want its
+                // top to be in the top third of the screen.
+                scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
+            }
+        } else if (rect.top < screenTop) {
+            scrollYDelta = rect.top - screenTop;
+        }
+
+        int screenLeft = contentToViewX(content.left);
+        int screenRight = contentToViewX(content.right);
+        int width = screenRight - screenLeft;
+        int scrollXDelta = 0;
+
+        if (rect.right > screenRight && rect.left > screenLeft) {
+            if (rect.width() > width) {
+                scrollXDelta += (rect.left - screenLeft);
+            } else {
+                scrollXDelta += (rect.right - screenRight);
+            }
+        } else if (rect.left < screenLeft) {
+            scrollXDelta -= (screenLeft - rect.left);
+        }
+
+        if ((scrollYDelta | scrollXDelta) != 0) {
+            return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
+        }
+
+        return false;
+    }
+
+    /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
+            String replace, int newStart, int newEnd) {
+        WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
+        arg.mReplace = replace;
+        arg.mNewStart = newStart;
+        arg.mNewEnd = newEnd;
+        mTextGeneration++;
+        arg.mTextGeneration = mTextGeneration;
+        mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
+    }
+
+    /* package */ void passToJavaScript(String currentText, KeyEvent event) {
+        // check if mWebViewCore has been destroyed
+        if (mWebViewCore == null) {
+            return;
+        }
+        WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
+        arg.mEvent = event;
+        arg.mCurrentText = currentText;
+        // Increase our text generation number, and pass it to webcore thread
+        mTextGeneration++;
+        mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
+        // WebKit's document state is not saved until about to leave the page.
+        // To make sure the host application, like Browser, has the up to date
+        // document state when it goes to background, we force to save the
+        // document state.
+        mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
+        mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
+                cursorData(), 1000);
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized WebViewCore getWebViewCore() {
+        return mWebViewCore;
+    }
+
+    /**
+     * Used only by TouchEventQueue to store pending touch events.
+     */
+    private static class QueuedTouch {
+        long mSequence;
+        MotionEvent mEvent; // Optional
+        TouchEventData mTed; // Optional
+
+        QueuedTouch mNext;
+
+        public QueuedTouch set(TouchEventData ted) {
+            mSequence = ted.mSequence;
+            mTed = ted;
+            mEvent = null;
+            mNext = null;
+            return this;
+        }
+
+        public QueuedTouch set(MotionEvent ev, long sequence) {
+            mEvent = MotionEvent.obtain(ev);
+            mSequence = sequence;
+            mTed = null;
+            mNext = null;
+            return this;
+        }
+
+        public QueuedTouch add(QueuedTouch other) {
+            if (other.mSequence < mSequence) {
+                other.mNext = this;
+                return other;
+            }
+
+            QueuedTouch insertAt = this;
+            while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) {
+                insertAt = insertAt.mNext;
+            }
+            other.mNext = insertAt.mNext;
+            insertAt.mNext = other;
+            return this;
+        }
+    }
+
+    /**
+     * WebView handles touch events asynchronously since some events must be passed to WebKit
+     * for potentially slower processing. TouchEventQueue serializes touch events regardless
+     * of which path they take to ensure that no events are ever processed out of order
+     * by WebView.
+     */
+    private class TouchEventQueue {
+        private long mNextTouchSequence = Long.MIN_VALUE + 1;
+        private long mLastHandledTouchSequence = Long.MIN_VALUE;
+        private long mIgnoreUntilSequence = Long.MIN_VALUE + 1;
+
+        // Events waiting to be processed.
+        private QueuedTouch mTouchEventQueue;
+
+        // Known events that are waiting on a response before being enqueued.
+        private QueuedTouch mPreQueue;
+
+        // Pool of QueuedTouch objects saved for later use.
+        private QueuedTouch mQueuedTouchRecycleBin;
+        private int mQueuedTouchRecycleCount;
+
+        private long mLastEventTime = Long.MAX_VALUE;
+        private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;
+
+        // milliseconds until we abandon hope of getting all of a previous gesture
+        private static final int QUEUED_GESTURE_TIMEOUT = 1000;
+
+        private QueuedTouch obtainQueuedTouch() {
+            if (mQueuedTouchRecycleBin != null) {
+                QueuedTouch result = mQueuedTouchRecycleBin;
+                mQueuedTouchRecycleBin = result.mNext;
+                mQueuedTouchRecycleCount--;
+                return result;
+            }
+            return new QueuedTouch();
+        }
+
+        /**
+         * Allow events with any currently missing sequence numbers to be skipped in processing.
+         */
+        public void ignoreCurrentlyMissingEvents() {
+            mIgnoreUntilSequence = mNextTouchSequence;
+
+            // Run any events we have available and complete, pre-queued or otherwise.
+            runQueuedAndPreQueuedEvents();
+        }
+
+        private void runQueuedAndPreQueuedEvents() {
+            QueuedTouch qd = mPreQueue;
+            boolean fromPreQueue = true;
+            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
+                handleQueuedTouch(qd);
+                QueuedTouch recycleMe = qd;
+                if (fromPreQueue) {
+                    mPreQueue = qd.mNext;
+                } else {
+                    mTouchEventQueue = qd.mNext;
+                }
+                recycleQueuedTouch(recycleMe);
+                mLastHandledTouchSequence++;
+
+                long nextPre = mPreQueue != null ? mPreQueue.mSequence : Long.MAX_VALUE;
+                long nextQueued = mTouchEventQueue != null ?
+                        mTouchEventQueue.mSequence : Long.MAX_VALUE;
+                fromPreQueue = nextPre < nextQueued;
+                qd = fromPreQueue ? mPreQueue : mTouchEventQueue;
+            }
+        }
+
+        /**
+         * Add a TouchEventData to the pre-queue.
+         *
+         * An event in the pre-queue is an event that we know about that
+         * has been sent to webkit, but that we haven't received back and
+         * enqueued into the normal touch queue yet. If webkit ever times
+         * out and we need to ignore currently missing events, we'll run
+         * events from the pre-queue to patch the holes.
+         *
+         * @param ted TouchEventData to pre-queue
+         */
+        public void preQueueTouchEventData(TouchEventData ted) {
+            QueuedTouch newTouch = obtainQueuedTouch().set(ted);
+            if (mPreQueue == null) {
+                mPreQueue = newTouch;
+            } else {
+                QueuedTouch insertionPoint = mPreQueue;
+                while (insertionPoint.mNext != null &&
+                        insertionPoint.mNext.mSequence < newTouch.mSequence) {
+                    insertionPoint = insertionPoint.mNext;
+                }
+                newTouch.mNext = insertionPoint.mNext;
+                insertionPoint.mNext = newTouch;
+            }
+        }
+
+        private void recycleQueuedTouch(QueuedTouch qd) {
+            if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
+                qd.mNext = mQueuedTouchRecycleBin;
+                mQueuedTouchRecycleBin = qd;
+                mQueuedTouchRecycleCount++;
+            }
+        }
+
+        /**
+         * Reset the touch event queue. This will dump any pending events
+         * and reset the sequence numbering.
+         */
+        public void reset() {
+            mNextTouchSequence = Long.MIN_VALUE + 1;
+            mLastHandledTouchSequence = Long.MIN_VALUE;
+            mIgnoreUntilSequence = Long.MIN_VALUE + 1;
+            while (mTouchEventQueue != null) {
+                QueuedTouch recycleMe = mTouchEventQueue;
+                mTouchEventQueue = mTouchEventQueue.mNext;
+                recycleQueuedTouch(recycleMe);
+            }
+            while (mPreQueue != null) {
+                QueuedTouch recycleMe = mPreQueue;
+                mPreQueue = mPreQueue.mNext;
+                recycleQueuedTouch(recycleMe);
+            }
+        }
+
+        /**
+         * Return the next valid sequence number for tagging incoming touch events.
+         * @return The next touch event sequence number
+         */
+        public long nextTouchSequence() {
+            return mNextTouchSequence++;
+        }
+
+        /**
+         * Enqueue a touch event in the form of TouchEventData.
+         * The sequence number will be read from the mSequence field of the argument.
+         *
+         * If the touch event's sequence number is the next in line to be processed, it will
+         * be handled before this method returns. Any subsequent events that have already
+         * been queued will also be processed in their proper order.
+         *
+         * @param ted Touch data to be processed in order.
+         * @return true if the event was processed before returning, false if it was just enqueued.
+         */
+        public boolean enqueueTouchEvent(TouchEventData ted) {
+            // Remove from the pre-queue if present
+            QueuedTouch preQueue = mPreQueue;
+            if (preQueue != null) {
+                // On exiting this block, preQueue is set to the pre-queued QueuedTouch object
+                // if it was present in the pre-queue, and removed from the pre-queue itself.
+                if (preQueue.mSequence == ted.mSequence) {
+                    mPreQueue = preQueue.mNext;
+                } else {
+                    QueuedTouch prev = preQueue;
+                    preQueue = null;
+                    while (prev.mNext != null) {
+                        if (prev.mNext.mSequence == ted.mSequence) {
+                            preQueue = prev.mNext;
+                            prev.mNext = preQueue.mNext;
+                            break;
+                        } else {
+                            prev = prev.mNext;
+                        }
+                    }
+                }
+            }
+
+            if (ted.mSequence < mLastHandledTouchSequence) {
+                // Stale event and we already moved on; drop it. (Should not be common.)
+                Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) +
+                        " received from webcore; ignoring");
+                return false;
+            }
+
+            if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) {
+                return false;
+            }
+
+            // dropStaleGestures above might have fast-forwarded us to
+            // an event we have already.
+            runNextQueuedEvents();
+
+            if (mLastHandledTouchSequence + 1 == ted.mSequence) {
+                if (preQueue != null) {
+                    recycleQueuedTouch(preQueue);
+                    preQueue = null;
+                }
+                handleQueuedTouchEventData(ted);
+
+                mLastHandledTouchSequence++;
+
+                // Do we have any more? Run them if so.
+                runNextQueuedEvents();
+            } else {
+                // Reuse the pre-queued object if we had it.
+                QueuedTouch qd = preQueue != null ? preQueue : obtainQueuedTouch().set(ted);
+                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
+            }
+            return true;
+        }
+
+        /**
+         * Enqueue a touch event in the form of a MotionEvent from the framework.
+         *
+         * If the touch event's sequence number is the next in line to be processed, it will
+         * be handled before this method returns. Any subsequent events that have already
+         * been queued will also be processed in their proper order.
+         *
+         * @param ev MotionEvent to be processed in order
+         */
+        public void enqueueTouchEvent(MotionEvent ev) {
+            final long sequence = nextTouchSequence();
+
+            if (dropStaleGestures(ev, sequence)) {
+                return;
+            }
+
+            // dropStaleGestures above might have fast-forwarded us to
+            // an event we have already.
+            runNextQueuedEvents();
+
+            if (mLastHandledTouchSequence + 1 == sequence) {
+                handleQueuedMotionEvent(ev);
+
+                mLastHandledTouchSequence++;
+
+                // Do we have any more? Run them if so.
+                runNextQueuedEvents();
+            } else {
+                QueuedTouch qd = obtainQueuedTouch().set(ev, sequence);
+                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
+            }
+        }
+
+        private void runNextQueuedEvents() {
+            QueuedTouch qd = mTouchEventQueue;
+            while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
+                handleQueuedTouch(qd);
+                QueuedTouch recycleMe = qd;
+                qd = qd.mNext;
+                recycleQueuedTouch(recycleMe);
+                mLastHandledTouchSequence++;
+            }
+            mTouchEventQueue = qd;
+        }
+
+        private boolean dropStaleGestures(MotionEvent ev, long sequence) {
+            if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) {
+                // This is to make sure that we don't attempt to process a tap
+                // or long press when webkit takes too long to get back to us.
+                // The movement will be properly confirmed when we process the
+                // enqueued event later.
+                final int dx = Math.round(ev.getX()) - mLastTouchX;
+                final int dy = Math.round(ev.getY()) - mLastTouchY;
+                if (dx * dx + dy * dy > mTouchSlopSquare) {
+                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                }
+            }
+
+            if (mTouchEventQueue == null) {
+                return sequence <= mLastHandledTouchSequence;
+            }
+
+            // If we have a new down event and it's been a while since the last event
+            // we saw, catch up as best we can and keep going.
+            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) {
+                long eventTime = ev.getEventTime();
+                long lastHandledEventTime = mLastEventTime;
+                if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) {
+                    Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
+                            "Catching up.");
+                    runQueuedAndPreQueuedEvents();
+
+                    // Drop leftovers that we truly don't have.
+                    QueuedTouch qd = mTouchEventQueue;
+                    while (qd != null && qd.mSequence < sequence) {
+                        QueuedTouch recycleMe = qd;
+                        qd = qd.mNext;
+                        recycleQueuedTouch(recycleMe);
+                    }
+                    mTouchEventQueue = qd;
+                    mLastHandledTouchSequence = sequence - 1;
+                }
+            }
+
+            if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) {
+                QueuedTouch qd = mTouchEventQueue;
+                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
+                    QueuedTouch recycleMe = qd;
+                    qd = qd.mNext;
+                    recycleQueuedTouch(recycleMe);
+                }
+                mTouchEventQueue = qd;
+                mLastHandledTouchSequence = mIgnoreUntilSequence - 1;
+            }
+
+            if (mPreQueue != null) {
+                // Drop stale prequeued events
+                QueuedTouch qd = mPreQueue;
+                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
+                    QueuedTouch recycleMe = qd;
+                    qd = qd.mNext;
+                    recycleQueuedTouch(recycleMe);
+                }
+                mPreQueue = qd;
+            }
+
+            return sequence <= mLastHandledTouchSequence;
+        }
+
+        private void handleQueuedTouch(QueuedTouch qt) {
+            if (qt.mTed != null) {
+                handleQueuedTouchEventData(qt.mTed);
+            } else {
+                handleQueuedMotionEvent(qt.mEvent);
+                qt.mEvent.recycle();
+            }
+        }
+
+        private void handleQueuedMotionEvent(MotionEvent ev) {
+            mLastEventTime = ev.getEventTime();
+            int action = ev.getActionMasked();
+            if (ev.getPointerCount() > 1) {  // Multi-touch
+                handleMultiTouchInWebView(ev);
+            } else {
+                final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+                if (detector != null && mPreventDefault != PREVENT_DEFAULT_YES) {
+                    // ScaleGestureDetector needs a consistent event stream to operate properly.
+                    // It won't take any action with fewer than two pointers, but it needs to
+                    // update internal bookkeeping state.
+                    detector.onTouchEvent(ev);
+                }
+
+                handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
+            }
+        }
+
+        private void handleQueuedTouchEventData(TouchEventData ted) {
+            if (ted.mMotionEvent != null) {
+                mLastEventTime = ted.mMotionEvent.getEventTime();
+            }
+            if (!ted.mReprocess) {
+                if (ted.mAction == MotionEvent.ACTION_DOWN
+                        && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
+                    // if prevent default is called from WebCore, UI
+                    // will not handle the rest of the touch events any
+                    // more.
+                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
+                            : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
+                } else if (ted.mAction == MotionEvent.ACTION_MOVE
+                        && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
+                    // the return for the first ACTION_MOVE will decide
+                    // whether UI will handle touch or not. Currently no
+                    // support for alternating prevent default
+                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
+                            : PREVENT_DEFAULT_NO;
+                }
+                if (mPreventDefault == PREVENT_DEFAULT_YES) {
+                    mTouchHighlightRegion.setEmpty();
+                }
+            } else {
+                if (ted.mPoints.length > 1) {  // multi-touch
+                    if (!ted.mNativeResult && mPreventDefault != PREVENT_DEFAULT_YES) {
+                        mPreventDefault = PREVENT_DEFAULT_NO;
+                        handleMultiTouchInWebView(ted.mMotionEvent);
+                    } else {
+                        mPreventDefault = PREVENT_DEFAULT_YES;
+                    }
+                    return;
+                }
+
+                // prevent default is not called in WebCore, so the
+                // message needs to be reprocessed in UI
+                if (!ted.mNativeResult) {
+                    // Following is for single touch.
+                    switch (ted.mAction) {
+                        case MotionEvent.ACTION_DOWN:
+                            mLastDeferTouchX = ted.mPointsInView[0].x;
+                            mLastDeferTouchY = ted.mPointsInView[0].y;
+                            mDeferTouchMode = TOUCH_INIT_MODE;
+                            break;
+                        case MotionEvent.ACTION_MOVE: {
+                            // no snapping in defer process
+                            int x = ted.mPointsInView[0].x;
+                            int y = ted.mPointsInView[0].y;
+
+                            if (mDeferTouchMode != TOUCH_DRAG_MODE) {
+                                mDeferTouchMode = TOUCH_DRAG_MODE;
+                                mLastDeferTouchX = x;
+                                mLastDeferTouchY = y;
+                                startScrollingLayer(x, y);
+                                startDrag();
+                            }
+                            int deltaX = pinLocX((int) (getScrollX()
+                                    + mLastDeferTouchX - x))
+                                    - getScrollX();
+                            int deltaY = pinLocY((int) (getScrollY()
+                                    + mLastDeferTouchY - y))
+                                    - getScrollY();
+                            doDrag(deltaX, deltaY);
+                            if (deltaX != 0) mLastDeferTouchX = x;
+                            if (deltaY != 0) mLastDeferTouchY = y;
+                            break;
+                        }
+                        case MotionEvent.ACTION_UP:
+                        case MotionEvent.ACTION_CANCEL:
+                            if (mDeferTouchMode == TOUCH_DRAG_MODE) {
+                                // no fling in defer process
+                                mScroller.springBack(getScrollX(), getScrollY(), 0,
+                                        computeMaxScrollX(), 0,
+                                        computeMaxScrollY());
+                                invalidate();
+                                WebViewCore.resumePriority();
+                                WebViewCore.resumeUpdatePicture(mWebViewCore);
+                            }
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                        case WebViewCore.ACTION_DOUBLETAP:
+                            // doDoubleTap() needs mLastTouchX/Y as anchor
+                            mLastDeferTouchX = ted.mPointsInView[0].x;
+                            mLastDeferTouchY = ted.mPointsInView[0].y;
+                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                        case WebViewCore.ACTION_LONGPRESS:
+                            HitTestResult hitTest = getHitTestResult();
+                            if (hitTest != null && hitTest.getType()
+                                    != HitTestResult.UNKNOWN_TYPE) {
+                                performLongClick();
+                            }
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // Methods can be called from a separate thread, like WebViewCore
+    // If it needs to call the View system, it has to send message.
+    //-------------------------------------------------------------------------
+
+    /**
+     * General handler to receive message coming from webkit thread
+     */
+    class PrivateHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            // exclude INVAL_RECT_MSG_ID since it is frequently output
+            if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
+                if (msg.what >= FIRST_PRIVATE_MSG_ID
+                        && msg.what <= LAST_PRIVATE_MSG_ID) {
+                    Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
+                            - FIRST_PRIVATE_MSG_ID]);
+                } else if (msg.what >= FIRST_PACKAGE_MSG_ID
+                        && msg.what <= LAST_PACKAGE_MSG_ID) {
+                    Log.v(LOGTAG, HandlerPackageDebugString[msg.what
+                            - FIRST_PACKAGE_MSG_ID]);
+                } else {
+                    Log.v(LOGTAG, Integer.toString(msg.what));
+                }
+            }
+            if (mWebViewCore == null) {
+                // after WebView's destroy() is called, skip handling messages.
+                return;
+            }
+            if (mBlockWebkitViewMessages
+                    && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
+                // Blocking messages from webkit
+                return;
+            }
+            switch (msg.what) {
+                case REMEMBER_PASSWORD: {
+                    mDatabase.setUsernamePassword(
+                            msg.getData().getString("host"),
+                            msg.getData().getString("username"),
+                            msg.getData().getString("password"));
+                    ((Message) msg.obj).sendToTarget();
+                    break;
+                }
+                case NEVER_REMEMBER_PASSWORD: {
+                    mDatabase.setUsernamePassword(
+                            msg.getData().getString("host"), null, null);
+                    ((Message) msg.obj).sendToTarget();
+                    break;
+                }
+                case PREVENT_DEFAULT_TIMEOUT: {
+                    // if timeout happens, cancel it so that it won't block UI
+                    // to continue handling touch events
+                    if ((msg.arg1 == MotionEvent.ACTION_DOWN
+                            && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
+                            || (msg.arg1 == MotionEvent.ACTION_MOVE
+                            && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
+                        cancelWebCoreTouchEvent(
+                                viewToContentX(mLastTouchX + getScrollX()),
+                                viewToContentY(mLastTouchY + getScrollY()),
+                                true);
+                    }
+                    break;
+                }
+                case SCROLL_SELECT_TEXT: {
+                    if (mAutoScrollX == 0 && mAutoScrollY == 0) {
+                        mSentAutoScrollMessage = false;
+                        break;
+                    }
+                    if (mCurrentScrollingLayerId == 0) {
+                        pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
+                    } else {
+                        scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
+                                mScrollingLayerRect.top + mAutoScrollY);
+                    }
+                    sendEmptyMessageDelayed(
+                            SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
+                    break;
+                }
+                case UPDATE_SELECTION: {
+                    if (mTouchMode == TOUCH_INIT_MODE
+                            || mTouchMode == TOUCH_SHORTPRESS_MODE
+                            || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
+                        updateSelection();
+                    }
+                    break;
+                }
+                case SWITCH_TO_SHORTPRESS: {
+                    if (mTouchMode == TOUCH_INIT_MODE) {
+                        if (!sDisableNavcache
+                                && mPreventDefault != PREVENT_DEFAULT_YES) {
+                            mTouchMode = TOUCH_SHORTPRESS_START_MODE;
+                            updateSelection();
+                        } else {
+                            // set to TOUCH_SHORTPRESS_MODE so that it won't
+                            // trigger double tap any more
+                            mTouchMode = TOUCH_SHORTPRESS_MODE;
+                        }
+                    } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                    }
+                    break;
+                }
+                case SWITCH_TO_LONGPRESS: {
+                    if (sDisableNavcache) {
+                        removeTouchHighlight();
+                    }
+                    if (inFullScreenMode() || mDeferTouchProcess) {
+                        TouchEventData ted = new TouchEventData();
+                        ted.mAction = WebViewCore.ACTION_LONGPRESS;
+                        ted.mIds = new int[1];
+                        ted.mIds[0] = 0;
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(viewToContentX(mLastTouchX + getScrollX()),
+                                                   viewToContentY(mLastTouchY + getScrollY()));
+                        ted.mPointsInView = new Point[1];
+                        ted.mPointsInView[0] = new Point(mLastTouchX, mLastTouchY);
+                        // metaState for long press is tricky. Should it be the
+                        // state when the press started or when the press was
+                        // released? Or some intermediary key state? For
+                        // simplicity for now, we don't set it.
+                        ted.mMetaState = 0;
+                        ted.mReprocess = mDeferTouchProcess;
+                        ted.mNativeLayer = nativeScrollableLayer(
+                                ted.mPoints[0].x, ted.mPoints[0].y,
+                                ted.mNativeLayerRect, null);
+                        ted.mSequence = mTouchEventQueue.nextTouchSequence();
+                        mTouchEventQueue.preQueueTouchEventData(ted);
+                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                    } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                        performLongClick();
+                    }
+                    break;
+                }
+                case RELEASE_SINGLE_TAP: {
+                    doShortPress();
+                    break;
+                }
+                case SCROLL_TO_MSG_ID: {
+                    // arg1 = animate, arg2 = onlyIfImeIsShowing
+                    // obj = Point(x, y)
+                    if (msg.arg2 == 1) {
+                        // This scroll is intended to bring the textfield into
+                        // view, but is only necessary if the IME is showing
+                        InputMethodManager imm = InputMethodManager.peekInstance();
+                        if (imm == null || !imm.isAcceptingText()
+                                || (!imm.isActive(mWebView) && (!inEditingMode()
+                                || !imm.isActive(mWebTextView)))) {
+                            break;
+                        }
+                    }
+                    final Point p = (Point) msg.obj;
+                    if (msg.arg1 == 1) {
+                        spawnContentScrollTo(p.x, p.y);
+                    } else {
+                        setContentScrollTo(p.x, p.y);
+                    }
+                    break;
+                }
+                case UPDATE_ZOOM_RANGE: {
+                    WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
+                    // mScrollX contains the new minPrefWidth
+                    mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
+                    break;
+                }
+                case UPDATE_ZOOM_DENSITY: {
+                    final float density = (Float) msg.obj;
+                    mZoomManager.updateDefaultZoomDensity(density);
+                    break;
+                }
+                case REPLACE_BASE_CONTENT: {
+                    nativeReplaceBaseContent(msg.arg1);
+                    break;
+                }
+                case NEW_PICTURE_MSG_ID: {
+                    // called for new content
+                    final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
+                    setNewPicture(draw, true);
+                    break;
+                }
+                case WEBCORE_INITIALIZED_MSG_ID:
+                    // nativeCreate sets mNativeClass to a non-zero value
+                    String drawableDir = BrowserFrame.getRawResFilename(
+                            BrowserFrame.DRAWABLEDIR, mContext);
+                    WindowManager windowManager =
+                            (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+                    Display display = windowManager.getDefaultDisplay();
+                    nativeCreate(msg.arg1, drawableDir,
+                            ActivityManager.isHighEndGfx(display));
+                    if (mDelaySetPicture != null) {
+                        setNewPicture(mDelaySetPicture, true);
+                        mDelaySetPicture = null;
+                    }
+                    if (mIsPaused) {
+                        nativeSetPauseDrawing(mNativeClass, true);
+                    }
+                    break;
+                case UPDATE_TEXTFIELD_TEXT_MSG_ID:
+                    // Make sure that the textfield is currently focused
+                    // and representing the same node as the pointer.
+                    if (msg.arg2 == mTextGeneration) {
+                        String text = (String) msg.obj;
+                        if (null == text) {
+                            text = "";
+                        }
+                        if (inEditingMode() &&
+                                mWebTextView.isSameTextField(msg.arg1)) {
+                            mWebTextView.setTextAndKeepSelection(text);
+                        } else if (mInputConnection != null &&
+                                mFieldPointer == msg.arg1) {
+                            mInputConnection.setTextAndKeepSelection(text);
+                        }
+                    }
+                    break;
+                case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
+                    displaySoftKeyboard(true);
+                    // fall through to UPDATE_TEXT_SELECTION_MSG_ID
+                case UPDATE_TEXT_SELECTION_MSG_ID:
+                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
+                            (WebViewCore.TextSelectionData) msg.obj);
+                    break;
+                case FORM_DID_BLUR:
+                    if (inEditingMode()
+                            && mWebTextView.isSameTextField(msg.arg1)) {
+                        hideSoftKeyboard();
+                    }
+                    break;
+                case RETURN_LABEL:
+                    if (inEditingMode()
+                            && mWebTextView.isSameTextField(msg.arg1)) {
+                        mWebTextView.setHint((String) msg.obj);
+                        InputMethodManager imm
+                                = InputMethodManager.peekInstance();
+                        // The hint is propagated to the IME in
+                        // onCreateInputConnection.  If the IME is already
+                        // active, restart it so that its hint text is updated.
+                        if (imm != null && imm.isActive(mWebTextView)) {
+                            imm.restartInput(mWebTextView);
+                        }
+                    }
+                    break;
+                case UNHANDLED_NAV_KEY:
+                    navHandledKey(msg.arg1, 1, false, 0);
+                    break;
+                case UPDATE_TEXT_ENTRY_MSG_ID:
+                    // this is sent after finishing resize in WebViewCore. Make
+                    // sure the text edit box is still on the  screen.
+                    if (inEditingMode() && nativeCursorIsTextInput()) {
+                        updateWebTextViewPosition();
+                    }
+                    break;
+                case CLEAR_TEXT_ENTRY:
+                    clearTextEntry();
+                    break;
+                case INVAL_RECT_MSG_ID: {
+                    Rect r = (Rect)msg.obj;
+                    if (r == null) {
+                        invalidate();
+                    } else {
+                        // we need to scale r from content into view coords,
+                        // which viewInvalidate() does for us
+                        viewInvalidate(r.left, r.top, r.right, r.bottom);
+                    }
+                    break;
+                }
+                case REQUEST_FORM_DATA:
+                    AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
+                    if (mWebTextView.isSameTextField(msg.arg1)) {
+                        mWebTextView.setAdapterCustom(adapter);
+                    }
+                    break;
+
+                case LONG_PRESS_CENTER:
+                    // as this is shared by keydown and trackballdown, reset all
+                    // the states
+                    mGotCenterDown = false;
+                    mTrackballDown = false;
+                    performLongClick();
+                    break;
+
+                case WEBCORE_NEED_TOUCH_EVENTS:
+                    mForwardTouchEvents = (msg.arg1 != 0);
+                    break;
+
+                case PREVENT_TOUCH_ID:
+                    if (inFullScreenMode()) {
+                        break;
+                    }
+                    TouchEventData ted = (TouchEventData) msg.obj;
+
+                    if (mTouchEventQueue.enqueueTouchEvent(ted)) {
+                        // WebCore is responding to us; remove pending timeout.
+                        // It will be re-posted when needed.
+                        removeMessages(PREVENT_DEFAULT_TIMEOUT);
+                    }
+                    break;
+
+                case REQUEST_KEYBOARD:
+                    if (msg.arg1 == 0) {
+                        hideSoftKeyboard();
+                    } else {
+                        displaySoftKeyboard(false);
+                    }
+                    break;
+
+                case DRAG_HELD_MOTIONLESS:
+                    mHeldMotionless = MOTIONLESS_TRUE;
+                    invalidate();
+                    // fall through to keep scrollbars awake
+
+                case AWAKEN_SCROLL_BARS:
+                    if (mTouchMode == TOUCH_DRAG_MODE
+                            && mHeldMotionless == MOTIONLESS_TRUE) {
+                        mWebViewPrivate.awakenScrollBars(ViewConfiguration
+                                .getScrollDefaultDelay(), false);
+                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                                .obtainMessage(AWAKEN_SCROLL_BARS),
+                                ViewConfiguration.getScrollDefaultDelay());
+                    }
+                    break;
+
+                case DO_MOTION_UP:
+                    doMotionUp(msg.arg1, msg.arg2);
+                    break;
+
+                case SCREEN_ON:
+                    mWebView.setKeepScreenOn(msg.arg1 == 1);
+                    break;
+
+                case ENTER_FULLSCREEN_VIDEO:
+                    int layerId = msg.arg1;
+
+                    String url = (String) msg.obj;
+                    if (mHTML5VideoViewProxy != null) {
+                        mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url);
+                    }
+                    break;
+
+                case EXIT_FULLSCREEN_VIDEO:
+                    if (mHTML5VideoViewProxy != null) {
+                        mHTML5VideoViewProxy.exitFullScreenVideo();
+                    }
+                    break;
+
+                case SHOW_FULLSCREEN: {
+                    View view = (View) msg.obj;
+                    int orientation = msg.arg1;
+                    int npp = msg.arg2;
+
+                    if (inFullScreenMode()) {
+                        Log.w(LOGTAG, "Should not have another full screen.");
+                        dismissFullScreenMode();
+                    }
+                    mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp);
+                    mFullScreenHolder.setContentView(view);
+                    mFullScreenHolder.show();
+                    invalidate();
+
+                    break;
+                }
+                case HIDE_FULLSCREEN:
+                    dismissFullScreenMode();
+                    break;
+
+                case DOM_FOCUS_CHANGED:
+                    if (inEditingMode()) {
+                        nativeClearCursor();
+                        rebuildWebTextView();
+                    }
+                    break;
+
+                case SHOW_RECT_MSG_ID: {
+                    WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
+                    int x = getScrollX();
+                    int left = contentToViewX(data.mLeft);
+                    int width = contentToViewDimension(data.mWidth);
+                    int maxWidth = contentToViewDimension(data.mContentWidth);
+                    int viewWidth = getViewWidth();
+                    if (width < viewWidth) {
+                        // center align
+                        x += left + width / 2 - getScrollX() - viewWidth / 2;
+                    } else {
+                        x += (int) (left + data.mXPercentInDoc * width
+                                - getScrollX() - data.mXPercentInView * viewWidth);
+                    }
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
+                              width + ",maxWidth=" + maxWidth +
+                              ",viewWidth=" + viewWidth + ",x="
+                              + x + ",xPercentInDoc=" + data.mXPercentInDoc +
+                              ",xPercentInView=" + data.mXPercentInView+ ")");
+                    }
+                    // use the passing content width to cap x as the current
+                    // mContentWidth may not be updated yet
+                    x = Math.max(0,
+                            (Math.min(maxWidth, x + viewWidth)) - viewWidth);
+                    int top = contentToViewY(data.mTop);
+                    int height = contentToViewDimension(data.mHeight);
+                    int maxHeight = contentToViewDimension(data.mContentHeight);
+                    int viewHeight = getViewHeight();
+                    int y = (int) (top + data.mYPercentInDoc * height -
+                                   data.mYPercentInView * viewHeight);
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
+                              height + ",maxHeight=" + maxHeight +
+                              ",viewHeight=" + viewHeight + ",y="
+                              + y + ",yPercentInDoc=" + data.mYPercentInDoc +
+                              ",yPercentInView=" + data.mYPercentInView+ ")");
+                    }
+                    // use the passing content height to cap y as the current
+                    // mContentHeight may not be updated yet
+                    y = Math.max(0,
+                            (Math.min(maxHeight, y + viewHeight) - viewHeight));
+                    // We need to take into account the visible title height
+                    // when scrolling since y is an absolute view position.
+                    y = Math.max(0, y - getVisibleTitleHeightImpl());
+                    mWebView.scrollTo(x, y);
+                    }
+                    break;
+
+                case CENTER_FIT_RECT:
+                    centerFitRect((Rect)msg.obj);
+                    break;
+
+                case SET_SCROLLBAR_MODES:
+                    mHorizontalScrollBarMode = msg.arg1;
+                    mVerticalScrollBarMode = msg.arg2;
+                    break;
+
+                case SELECTION_STRING_CHANGED:
+                    if (mAccessibilityInjector != null) {
+                        String selectionString = (String) msg.obj;
+                        mAccessibilityInjector.onSelectionStringChange(selectionString);
+                    }
+                    break;
+
+                case HIT_TEST_RESULT:
+                    WebKitHitTest hit = (WebKitHitTest) msg.obj;
+                    mFocusedNode = hit;
+                    setTouchHighlightRects(hit);
+                    setHitTestResult(hit);
+                    break;
+
+                case SAVE_WEBARCHIVE_FINISHED:
+                    SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
+                    if (saveMessage.mCallback != null) {
+                        saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
+                    }
+                    break;
+
+                case SET_AUTOFILLABLE:
+                    mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
+                    if (mWebTextView != null) {
+                        mWebTextView.setAutoFillable(mAutoFillData.getQueryId());
+                        rebuildWebTextView();
+                    }
+                    break;
+
+                case AUTOFILL_COMPLETE:
+                    if (mWebTextView != null) {
+                        // Clear the WebTextView adapter when AutoFill finishes
+                        // so that the drop down gets cleared.
+                        mWebTextView.setAdapterCustom(null);
+                    }
+                    break;
+
+                case SELECT_AT:
+                    nativeSelectAt(msg.arg1, msg.arg2);
+                    break;
+
+                case COPY_TO_CLIPBOARD:
+                    copyToClipboard((String) msg.obj);
+                    break;
+
+                case INIT_EDIT_FIELD:
+                    if (mInputConnection != null) {
+                        TextFieldInitData initData = (TextFieldInitData) msg.obj;
+                        mTextGeneration = 0;
+                        mFieldPointer = initData.mFieldPointer;
+                        mInputConnection.initEditorInfo(initData);
+                        mInputConnection.setTextAndKeepSelection(initData.mText);
+                    }
+                    break;
+
+                case REPLACE_TEXT:{
+                    String text = (String)msg.obj;
+                    int start = msg.arg1;
+                    int end = msg.arg2;
+                    int cursorPosition = start + text.length();
+                    replaceTextfieldText(start, end, text,
+                            cursorPosition, cursorPosition);
+                    break;
+                }
+
+                case UPDATE_MATCH_COUNT: {
+                    if (mFindCallback != null) {
+                        mFindCallback.updateMatchCount(msg.arg1, msg.arg2,
+                            (String) msg.obj);
+                    }
+                    break;
+                }
+                case CLEAR_CARET_HANDLE:
+                    selectionDone();
+                    break;
+
+                case KEY_PRESS:
+                    mWebViewCore.sendMessage(EventHub.KEY_PRESS, msg.arg1);
+                    break;
+
+                default:
+                    super.handleMessage(msg);
+                    break;
+            }
+        }
+    }
+
+    private void setHitTestTypeFromUrl(String url) {
+        String substr = null;
+        if (url.startsWith(SCHEME_GEO)) {
+            mInitialHitTestResult.setType(HitTestResult.GEO_TYPE);
+            substr = url.substring(SCHEME_GEO.length());
+        } else if (url.startsWith(SCHEME_TEL)) {
+            mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE);
+            substr = url.substring(SCHEME_TEL.length());
+        } else if (url.startsWith(SCHEME_MAILTO)) {
+            mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE);
+            substr = url.substring(SCHEME_MAILTO.length());
+        } else {
+            mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE);
+            mInitialHitTestResult.setExtra(url);
+            return;
+        }
+        try {
+            mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8"));
+        } catch (Throwable e) {
+            Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
+            mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE);
+        }
+    }
+
+    private void setHitTestResult(WebKitHitTest hit) {
+        if (hit == null) {
+            mInitialHitTestResult = null;
+            return;
+        }
+        mInitialHitTestResult = new HitTestResult();
+        if (hit.mLinkUrl != null) {
+            setHitTestTypeFromUrl(hit.mLinkUrl);
+            if (hit.mImageUrl != null
+                    && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
+                mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+                mInitialHitTestResult.setExtra(hit.mImageUrl);
+            }
+        } else if (hit.mImageUrl != null) {
+            mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE);
+            mInitialHitTestResult.setExtra(hit.mImageUrl);
+        } else if (hit.mEditable) {
+            mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE);
+        } else if (hit.mIntentUrl != null) {
+            setHitTestTypeFromUrl(hit.mIntentUrl);
+        }
+    }
+
+    private boolean shouldDrawHighlightRect() {
+        if (mFocusedNode == null || mInitialHitTestResult == null) {
+            return false;
+        }
+        if (mTouchHighlightRegion.isEmpty()) {
+            return false;
+        }
+        if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
+            return !mFocusedNode.mEditable;
+        }
+        if (mInitialHitTestResult.getType() == HitTestResult.UNKNOWN_TYPE) {
+            return false;
+        }
+        long delay = System.currentTimeMillis() - mTouchHighlightRequested;
+        if (delay < ViewConfiguration.getTapTimeout()) {
+            Rect r = mTouchHighlightRegion.getBounds();
+            mWebView.postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
+            return false;
+        }
+        return true;
+    }
+
+
+    private FocusTransitionDrawable mFocusTransition = null;
+    static class FocusTransitionDrawable extends Drawable {
+        Region mPreviousRegion;
+        Region mNewRegion;
+        float mProgress = 0;
+        WebViewClassic mWebView;
+        Paint mPaint;
+        int mMaxAlpha;
+        Point mTranslate;
+
+        public FocusTransitionDrawable(WebViewClassic view) {
+            mWebView = view;
+            mPaint = new Paint(mWebView.mTouchHightlightPaint);
+            mMaxAlpha = mPaint.getAlpha();
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        public void setProgress(float p) {
+            mProgress = p;
+            if (mWebView.mFocusTransition == this) {
+                if (mProgress == 1f)
+                    mWebView.mFocusTransition = null;
+                mWebView.invalidate();
+            }
+        }
+
+        public float getProgress() {
+            return mProgress;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mTranslate == null) {
+                Rect bounds = mPreviousRegion.getBounds();
+                Point from = new Point(bounds.centerX(), bounds.centerY());
+                mNewRegion.getBounds(bounds);
+                Point to = new Point(bounds.centerX(), bounds.centerY());
+                mTranslate = new Point(from.x - to.x, from.y - to.y);
+            }
+            int alpha = (int) (mProgress * mMaxAlpha);
+            RegionIterator iter = new RegionIterator(mPreviousRegion);
+            Rect r = new Rect();
+            mPaint.setAlpha(mMaxAlpha - alpha);
+            float tx = mTranslate.x * mProgress;
+            float ty = mTranslate.y * mProgress;
+            int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            canvas.translate(-tx, -ty);
+            while (iter.next(r)) {
+                canvas.drawRect(r, mPaint);
+            }
+            canvas.restoreToCount(save);
+            iter = new RegionIterator(mNewRegion);
+            r = new Rect();
+            mPaint.setAlpha(alpha);
+            save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            tx = mTranslate.x - tx;
+            ty = mTranslate.y - ty;
+            canvas.translate(tx, ty);
+            while (iter.next(r)) {
+                canvas.drawRect(r, mPaint);
+            }
+            canvas.restoreToCount(save);
+        }
+    };
+
+    private boolean shouldAnimateTo(WebKitHitTest hit) {
+        // TODO: Don't be annoying or throw out the animation entirely
+        return false;
+    }
+
+    private void setTouchHighlightRects(WebKitHitTest hit) {
+        FocusTransitionDrawable transition = null;
+        if (shouldAnimateTo(hit)) {
+            transition = new FocusTransitionDrawable(this);
+        }
+        Rect[] rects = hit != null ? hit.mTouchRects : null;
+        if (!mTouchHighlightRegion.isEmpty()) {
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
+            if (transition != null) {
+                transition.mPreviousRegion = new Region(mTouchHighlightRegion);
+            }
+            mTouchHighlightRegion.setEmpty();
+        }
+        if (rects != null) {
+            mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
+            for (Rect rect : rects) {
+                Rect viewRect = contentToViewRect(rect);
+                // some sites, like stories in nytimes.com, set
+                // mouse event handler in the top div. It is not
+                // user friendly to highlight the div if it covers
+                // more than half of the screen.
+                if (viewRect.width() < getWidth() >> 1
+                        || viewRect.height() < getHeight() >> 1) {
+                    mTouchHighlightRegion.union(viewRect);
+                } else {
+                    Log.w(LOGTAG, "Skip the huge selection rect:"
+                            + viewRect);
+                }
+            }
+            mWebView.invalidate(mTouchHighlightRegion.getBounds());
+            if (transition != null && transition.mPreviousRegion != null) {
+                transition.mNewRegion = new Region(mTouchHighlightRegion);
+                mFocusTransition = transition;
+                ObjectAnimator animator = ObjectAnimator.ofFloat(
+                        mFocusTransition, "progress", 1f);
+                animator.start();
+            }
+        }
+    }
+
+    // Interface to allow the profiled WebView to hook the page swap notifications.
+    public interface PageSwapDelegate {
+        void onPageSwapOccurred(boolean notifyAnimationStarted);
+    }
+
+    /** @hide Called by JNI when pages are swapped (only occurs with hardware
+     * acceleration) */
+    protected void pageSwapCallback(boolean notifyAnimationStarted) {
+        mWebViewCore.resumeWebKitDraw();
+        if (inEditingMode()) {
+            didUpdateWebTextViewDimensions(ANYWHERE);
+        }
+        if (notifyAnimationStarted) {
+            mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
+        }
+        if (mWebView instanceof PageSwapDelegate) {
+            // This provides a hook for ProfiledWebView to observe the tile page swaps.
+            ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted);
+        }
+    }
+
+    void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
+        if (mNativeClass == 0) {
+            if (mDelaySetPicture != null) {
+                throw new IllegalStateException("Tried to setNewPicture with"
+                        + " a delay picture already set! (memory leak)");
+            }
+            // Not initialized yet, delay set
+            mDelaySetPicture = draw;
+            return;
+        }
+        WebViewCore.ViewState viewState = draw.mViewState;
+        boolean isPictureAfterFirstLayout = viewState != null;
+
+        if (updateBaseLayer) {
+            setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
+                    getSettings().getShowVisualIndicator(),
+                    isPictureAfterFirstLayout);
+        }
+        final Point viewSize = draw.mViewSize;
+        // We update the layout (i.e. request a layout from the
+        // view system) if the last view size that we sent to
+        // WebCore matches the view size of the picture we just
+        // received in the fixed dimension.
+        final boolean updateLayout = viewSize.x == mLastWidthSent
+                && viewSize.y == mLastHeightSent;
+        // Don't send scroll event for picture coming from webkit,
+        // since the new picture may cause a scroll event to override
+        // the saved history scroll position.
+        mSendScrollEvent = false;
+        recordNewContentSize(draw.mContentSize.x,
+                draw.mContentSize.y, updateLayout);
+        if (isPictureAfterFirstLayout) {
+            // Reset the last sent data here since dealing with new page.
+            mLastWidthSent = 0;
+            mZoomManager.onFirstLayout(draw);
+            int scrollX = viewState.mShouldStartScrolledRight
+                    ? getContentWidth() : viewState.mScrollX;
+            int scrollY = viewState.mScrollY;
+            setContentScrollTo(scrollX, scrollY);
+            if (!mDrawHistory) {
+                // As we are on a new page, remove the WebTextView. This
+                // is necessary for page loads driven by webkit, and in
+                // particular when the user was on a password field, so
+                // the WebTextView was visible.
+                clearTextEntry();
+            }
+        }
+        mSendScrollEvent = true;
+
+        if (DebugFlags.WEB_VIEW) {
+            Rect b = draw.mInvalRegion.getBounds();
+            Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
+                    b.left+","+b.top+","+b.right+","+b.bottom+"}");
+        }
+        invalidateContentRect(draw.mInvalRegion.getBounds());
+
+        if (mPictureListener != null) {
+            mPictureListener.onNewPicture(getWebView(), capturePicture());
+        }
+
+        // update the zoom information based on the new picture
+        mZoomManager.onNewPicture(draw);
+
+        if (draw.mFocusSizeChanged && inEditingMode()) {
+            mFocusSizeChanged = true;
+        }
+        if (isPictureAfterFirstLayout) {
+            mViewManager.postReadyToDrawAll();
+        }
+    }
+
+    /**
+     * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
+     * and UPDATE_TEXT_SELECTION_MSG_ID.  Update the selection of WebTextView.
+     */
+    private void updateTextSelectionFromMessage(int nodePointer,
+            int textGeneration, WebViewCore.TextSelectionData data) {
+        if (textGeneration == mTextGeneration) {
+            if (inEditingMode()
+                    && mWebTextView.isSameTextField(nodePointer)) {
+                mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
+            } else if (mInputConnection != null && mFieldPointer == nodePointer) {
+                mInputConnection.setSelection(data.mStart, data.mEnd);
+            }
+        }
+        nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
+
+        if (data.mSelectTextPtr != 0 &&
+                (data.mStart != data.mEnd ||
+                (mFieldPointer == nodePointer && mFieldPointer != 0))) {
+            mIsCaretSelection = (data.mStart == data.mEnd);
+            if (!mSelectingText) {
+                setupWebkitSelect();
+            } else if (!mSelectionStarted) {
+                syncSelectionCursors();
+            }
+            if (mIsCaretSelection) {
+                resetCaretTimer();
+            }
+        } else {
+            selectionDone();
+        }
+        invalidate();
+    }
+
+    // Class used to use a dropdown for a <select> element
+    private class InvokeListBox implements Runnable {
+        // Whether the listbox allows multiple selection.
+        private boolean     mMultiple;
+        // Passed in to a list with multiple selection to tell
+        // which items are selected.
+        private int[]       mSelectedArray;
+        // Passed in to a list with single selection to tell
+        // where the initial selection is.
+        private int         mSelection;
+
+        private Container[] mContainers;
+
+        // Need these to provide stable ids to my ArrayAdapter,
+        // which normally does not have stable ids. (Bug 1250098)
+        private class Container extends Object {
+            /**
+             * Possible values for mEnabled.  Keep in sync with OptionStatus in
+             * WebViewCore.cpp
+             */
+            final static int OPTGROUP = -1;
+            final static int OPTION_DISABLED = 0;
+            final static int OPTION_ENABLED = 1;
+
+            String  mString;
+            int     mEnabled;
+            int     mId;
+
+            @Override
+            public String toString() {
+                return mString;
+            }
+        }
+
+        /**
+         *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
+         *  and allow filtering.
+         */
+        private class MyArrayListAdapter extends ArrayAdapter<Container> {
+            public MyArrayListAdapter() {
+                super(WebViewClassic.this.mContext,
+                        mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
+                        com.android.internal.R.layout.webview_select_singlechoice,
+                        mContainers);
+            }
+
+            @Override
+            public View getView(int position, View convertView,
+                    ViewGroup parent) {
+                // Always pass in null so that we will get a new CheckedTextView
+                // Otherwise, an item which was previously used as an <optgroup>
+                // element (i.e. has no check), could get used as an <option>
+                // element, which needs a checkbox/radio, but it would not have
+                // one.
+                convertView = super.getView(position, null, parent);
+                Container c = item(position);
+                if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
+                    // ListView does not draw dividers between disabled and
+                    // enabled elements.  Use a LinearLayout to provide dividers
+                    LinearLayout layout = new LinearLayout(mContext);
+                    layout.setOrientation(LinearLayout.VERTICAL);
+                    if (position > 0) {
+                        View dividerTop = new View(mContext);
+                        dividerTop.setBackgroundResource(
+                                android.R.drawable.divider_horizontal_bright);
+                        layout.addView(dividerTop);
+                    }
+
+                    if (Container.OPTGROUP == c.mEnabled) {
+                        // Currently select_dialog_multichoice uses CheckedTextViews.
+                        // If that changes, the class cast will no longer be valid.
+                        if (mMultiple) {
+                            Assert.assertTrue(convertView instanceof CheckedTextView);
+                            ((CheckedTextView) convertView).setCheckMarkDrawable(null);
+                        }
+                    } else {
+                        // c.mEnabled == Container.OPTION_DISABLED
+                        // Draw the disabled element in a disabled state.
+                        convertView.setEnabled(false);
+                    }
+
+                    layout.addView(convertView);
+                    if (position < getCount() - 1) {
+                        View dividerBottom = new View(mContext);
+                        dividerBottom.setBackgroundResource(
+                                android.R.drawable.divider_horizontal_bright);
+                        layout.addView(dividerBottom);
+                    }
+                    return layout;
+                }
+                return convertView;
+            }
+
+            @Override
+            public boolean hasStableIds() {
+                // AdapterView's onChanged method uses this to determine whether
+                // to restore the old state.  Return false so that the old (out
+                // of date) state does not replace the new, valid state.
+                return false;
+            }
+
+            private Container item(int position) {
+                if (position < 0 || position >= getCount()) {
+                    return null;
+                }
+                return getItem(position);
+            }
+
+            @Override
+            public long getItemId(int position) {
+                Container item = item(position);
+                if (item == null) {
+                    return -1;
+                }
+                return item.mId;
+            }
+
+            @Override
+            public boolean areAllItemsEnabled() {
+                return false;
+            }
+
+            @Override
+            public boolean isEnabled(int position) {
+                Container item = item(position);
+                if (item == null) {
+                    return false;
+                }
+                return Container.OPTION_ENABLED == item.mEnabled;
+            }
+        }
+
+        private InvokeListBox(String[] array, int[] enabled, int[] selected) {
+            mMultiple = true;
+            mSelectedArray = selected;
+
+            int length = array.length;
+            mContainers = new Container[length];
+            for (int i = 0; i < length; i++) {
+                mContainers[i] = new Container();
+                mContainers[i].mString = array[i];
+                mContainers[i].mEnabled = enabled[i];
+                mContainers[i].mId = i;
+            }
+        }
+
+        private InvokeListBox(String[] array, int[] enabled, int selection) {
+            mSelection = selection;
+            mMultiple = false;
+
+            int length = array.length;
+            mContainers = new Container[length];
+            for (int i = 0; i < length; i++) {
+                mContainers[i] = new Container();
+                mContainers[i].mString = array[i];
+                mContainers[i].mEnabled = enabled[i];
+                mContainers[i].mId = i;
+            }
+        }
+
+        /*
+         * Whenever the data set changes due to filtering, this class ensures
+         * that the checked item remains checked.
+         */
+        private class SingleDataSetObserver extends DataSetObserver {
+            private long        mCheckedId;
+            private ListView    mListView;
+            private Adapter     mAdapter;
+
+            /*
+             * Create a new observer.
+             * @param id The ID of the item to keep checked.
+             * @param l ListView for getting and clearing the checked states
+             * @param a Adapter for getting the IDs
+             */
+            public SingleDataSetObserver(long id, ListView l, Adapter a) {
+                mCheckedId = id;
+                mListView = l;
+                mAdapter = a;
+            }
+
+            @Override
+            public void onChanged() {
+                // The filter may have changed which item is checked.  Find the
+                // item that the ListView thinks is checked.
+                int position = mListView.getCheckedItemPosition();
+                long id = mAdapter.getItemId(position);
+                if (mCheckedId != id) {
+                    // Clear the ListView's idea of the checked item, since
+                    // it is incorrect
+                    mListView.clearChoices();
+                    // Search for mCheckedId.  If it is in the filtered list,
+                    // mark it as checked
+                    int count = mAdapter.getCount();
+                    for (int i = 0; i < count; i++) {
+                        if (mAdapter.getItemId(i) == mCheckedId) {
+                            mListView.setItemChecked(i, true);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            final ListView listView = (ListView) LayoutInflater.from(mContext)
+                    .inflate(com.android.internal.R.layout.select_dialog, null);
+            final MyArrayListAdapter adapter = new MyArrayListAdapter();
+            AlertDialog.Builder b = new AlertDialog.Builder(mContext)
+                    .setView(listView).setCancelable(true)
+                    .setInverseBackgroundForced(true);
+
+            if (mMultiple) {
+                b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mWebViewCore.sendMessage(
+                                EventHub.LISTBOX_CHOICES,
+                                adapter.getCount(), 0,
+                                listView.getCheckedItemPositions());
+                    }});
+                b.setNegativeButton(android.R.string.cancel,
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mWebViewCore.sendMessage(
+                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+                }});
+            }
+            mListBoxDialog = b.create();
+            listView.setAdapter(adapter);
+            listView.setFocusableInTouchMode(true);
+            // There is a bug (1250103) where the checks in a ListView with
+            // multiple items selected are associated with the positions, not
+            // the ids, so the items do not properly retain their checks when
+            // filtered.  Do not allow filtering on multiple lists until
+            // that bug is fixed.
+
+            listView.setTextFilterEnabled(!mMultiple);
+            if (mMultiple) {
+                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+                int length = mSelectedArray.length;
+                for (int i = 0; i < length; i++) {
+                    listView.setItemChecked(mSelectedArray[i], true);
+                }
+            } else {
+                listView.setOnItemClickListener(new OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View v,
+                            int position, long id) {
+                        // Rather than sending the message right away, send it
+                        // after the page regains focus.
+                        mListBoxMessage = Message.obtain(null,
+                                EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
+                        mListBoxDialog.dismiss();
+                        mListBoxDialog = null;
+                    }
+                });
+                if (mSelection != -1) {
+                    listView.setSelection(mSelection);
+                    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+                    listView.setItemChecked(mSelection, true);
+                    DataSetObserver observer = new SingleDataSetObserver(
+                            adapter.getItemId(mSelection), listView, adapter);
+                    adapter.registerDataSetObserver(observer);
+                }
+            }
+            mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+                @Override
+                public void onCancel(DialogInterface dialog) {
+                    mWebViewCore.sendMessage(
+                                EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+                    mListBoxDialog = null;
+                }
+            });
+            mListBoxDialog.show();
+        }
+    }
+
+    private Message mListBoxMessage;
+
+    /*
+     * Request a dropdown menu for a listbox with multiple selection.
+     *
+     * @param array Labels for the listbox.
+     * @param enabledArray  State for each element in the list.  See static
+     *      integers in Container class.
+     * @param selectedArray Which positions are initally selected.
+     */
+    void requestListBox(String[] array, int[] enabledArray, int[]
+            selectedArray) {
+        mPrivateHandler.post(
+                new InvokeListBox(array, enabledArray, selectedArray));
+    }
+
+    /*
+     * Request a dropdown menu for a listbox with single selection or a single
+     * <select> element.
+     *
+     * @param array Labels for the listbox.
+     * @param enabledArray  State for each element in the list.  See static
+     *      integers in Container class.
+     * @param selection Which position is initally selected.
+     */
+    void requestListBox(String[] array, int[] enabledArray, int selection) {
+        mPrivateHandler.post(
+                new InvokeListBox(array, enabledArray, selection));
+    }
+
+    // called by JNI
+    private void sendMoveFocus(int frame, int node) {
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
+                new WebViewCore.CursorData(frame, node, 0, 0));
+    }
+
+    // called by JNI
+    private void sendMoveMouse(int frame, int node, int x, int y) {
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
+                new WebViewCore.CursorData(frame, node, x, y));
+    }
+
+    /*
+     * Send a mouse move event to the webcore thread.
+     *
+     * @param removeFocus Pass true to remove the WebTextView, if present.
+     * @param stopPaintingCaret Stop drawing the blinking caret if true.
+     * called by JNI
+     */
+    @SuppressWarnings("unused")
+    private void sendMoveMouseIfLatest(boolean removeFocus, boolean stopPaintingCaret) {
+        if (removeFocus) {
+            clearTextEntry();
+        }
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
+                stopPaintingCaret ? 1 : 0, 0,
+                cursorData());
+    }
+
+    /**
+     * Called by JNI to send a message to the webcore thread that the user
+     * touched the webpage.
+     * @param touchGeneration Generation number of the touch, to ignore touches
+     *      after a new one has been generated.
+     * @param frame Pointer to the frame holding the node that was touched.
+     * @param node Pointer to the node touched.
+     * @param x x-position of the touch.
+     * @param y y-position of the touch.
+     */
+    private void sendMotionUp(int touchGeneration,
+            int frame, int node, int x, int y) {
+        WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
+        touchUpData.mMoveGeneration = touchGeneration;
+        touchUpData.mFrame = frame;
+        touchUpData.mNode = node;
+        touchUpData.mX = x;
+        touchUpData.mY = y;
+        touchUpData.mNativeLayer = nativeScrollableLayer(
+                x, y, touchUpData.mNativeLayerRect, null);
+        mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
+    }
+
+
+    private int getScaledMaxXScroll() {
+        int width;
+        if (mHeightCanMeasure == false) {
+            width = getViewWidth() / 4;
+        } else {
+            Rect visRect = new Rect();
+            calcOurVisibleRect(visRect);
+            width = visRect.width() / 2;
+        }
+        // FIXME the divisor should be retrieved from somewhere
+        return viewToContentX(width);
+    }
+
+    private int getScaledMaxYScroll() {
+        int height;
+        if (mHeightCanMeasure == false) {
+            height = getViewHeight() / 4;
+        } else {
+            Rect visRect = new Rect();
+            calcOurVisibleRect(visRect);
+            height = visRect.height() / 2;
+        }
+        // FIXME the divisor should be retrieved from somewhere
+        // the closest thing today is hard-coded into ScrollView.java
+        // (from ScrollView.java, line 363)   int maxJump = height/2;
+        return Math.round(height * mZoomManager.getInvScale());
+    }
+
+    /**
+     * Called by JNI to invalidate view
+     */
+    private void viewInvalidate() {
+        invalidate();
+    }
+
+    /**
+     * Pass the key directly to the page.  This assumes that
+     * nativePageShouldHandleShiftAndArrows() returned true.
+     */
+    private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
+        int keyEventAction;
+        int eventHubAction;
+        if (down) {
+            keyEventAction = KeyEvent.ACTION_DOWN;
+            eventHubAction = EventHub.KEY_DOWN;
+            mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
+        } else {
+            keyEventAction = KeyEvent.ACTION_UP;
+            eventHubAction = EventHub.KEY_UP;
+        }
+
+        KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
+                1, (metaState & KeyEvent.META_SHIFT_ON)
+                | (metaState & KeyEvent.META_ALT_ON)
+                | (metaState & KeyEvent.META_SYM_ON)
+                , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
+        mWebViewCore.sendMessage(eventHubAction, event);
+    }
+
+    // return true if the key was handled
+    private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+            long time) {
+        if (mNativeClass == 0) {
+            return false;
+        }
+        mInitialHitTestResult = null;
+        mLastCursorTime = time;
+        mLastCursorBounds = cursorRingBounds();
+        boolean keyHandled
+                = nativeMoveCursor(keyCode, count, noScroll) == false;
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
+                    + " mLastCursorTime=" + mLastCursorTime
+                    + " handled=" + keyHandled);
+        }
+        if (keyHandled == false) {
+            return keyHandled;
+        }
+        Rect contentCursorRingBounds = cursorRingBounds();
+        if (contentCursorRingBounds.isEmpty()) return keyHandled;
+        Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
+        // set last touch so that context menu related functions will work
+        mLastTouchX = (viewCursorRingBounds.left + viewCursorRingBounds.right) / 2;
+        mLastTouchY = (viewCursorRingBounds.top + viewCursorRingBounds.bottom) / 2;
+        if (mHeightCanMeasure == false) {
+            return keyHandled;
+        }
+        Rect visRect = new Rect();
+        calcOurVisibleRect(visRect);
+        Rect outset = new Rect(visRect);
+        int maxXScroll = visRect.width() / 2;
+        int maxYScroll = visRect.height() / 2;
+        outset.inset(-maxXScroll, -maxYScroll);
+        if (Rect.intersects(outset, viewCursorRingBounds) == false) {
+            return keyHandled;
+        }
+        // FIXME: Necessary because ScrollView/ListView do not scroll left/right
+        int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
+                maxXScroll);
+        if (maxH > 0) {
+            pinScrollBy(maxH, 0, true, 0);
+        } else {
+            maxH = Math.max(viewCursorRingBounds.left - visRect.left,
+                    -maxXScroll);
+            if (maxH < 0) {
+                pinScrollBy(maxH, 0, true, 0);
+            }
+        }
+        if (mLastCursorBounds.isEmpty()) return keyHandled;
+        if (mLastCursorBounds.equals(contentCursorRingBounds)) {
+            return keyHandled;
+        }
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
+                    + contentCursorRingBounds);
+        }
+        mWebView.requestRectangleOnScreen(viewCursorRingBounds);
+        return keyHandled;
+    }
+
+    /**
+     * @return Whether accessibility script has been injected.
+     */
+    private boolean accessibilityScriptInjected() {
+        // TODO: Maybe the injected script should announce its presence in
+        // the page meta-tag so the nativePageShouldHandleShiftAndArrows
+        // will check that as one of the conditions it looks for
+        return mAccessibilityScriptInjected;
+    }
+
+    /**
+     * Set the background color. It's white by default. Pass
+     * zero to make the view transparent.
+     * @param color   the ARGB color described by Color.java
+     */
+    @Override
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
+    }
+
+    /**
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public void debugDump() {
+        checkThread();
+        nativeDebugDump();
+        mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
+    }
+
+    /**
+     * Draw the HTML page into the specified canvas. This call ignores any
+     * view-specific zoom, scroll offset, or other changes. It does not draw
+     * any view-specific chrome, such as progress or URL bars.
+     *
+     * @hide only needs to be accessible to Browser and testing
+     */
+    public void drawPage(Canvas canvas) {
+        calcOurContentVisibleRectF(mVisibleContentRect);
+        nativeDraw(canvas, mVisibleContentRect, 0, 0, false);
+    }
+
+    /**
+     * Enable the communication b/t the webView and VideoViewProxy
+     *
+     * @hide only used by the Browser
+     */
+    public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
+        mHTML5VideoViewProxy = proxy;
+    }
+
+    /**
+     * Set the time to wait between passing touches to WebCore. See also the
+     * TOUCH_SENT_INTERVAL member for further discussion.
+     *
+     * @hide This is only used by the DRT test application.
+     */
+    public void setTouchInterval(int interval) {
+        mCurrentTouchInterval = interval;
+    }
+
+    /**
+     * Copy text into the clipboard. This is called indirectly from
+     * WebViewCore.
+     * @param text The text to put into the clipboard.
+     */
+    private void copyToClipboard(String text) {
+        ClipboardManager cm = (ClipboardManager)mContext
+                .getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipData clip = ClipData.newPlainText(getTitle(), text);
+        cm.setPrimaryClip(clip);
+    }
+
+    /**
+     *  Update our cache with updatedText.
+     *  @param updatedText  The new text to put in our cache.
+     *  @hide
+     */
+    protected void updateCachedTextfield(String updatedText) {
+        // Also place our generation number so that when we look at the cache
+        // we recognize that it is up to date.
+        nativeUpdateCachedTextfield(updatedText, mTextGeneration);
+    }
+
+    /*package*/ void autoFillForm(int autoFillQueryId) {
+        mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
+    }
+
+    /* package */ ViewManager getViewManager() {
+        return mViewManager;
+    }
+
+    private static void checkThread() {
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            Throwable throwable = new Throwable(
+                    "Warning: A WebView method was called on thread '" +
+                    Thread.currentThread().getName() + "'. " +
+                    "All WebView methods must be called on the UI thread. " +
+                    "Future versions of WebView may not support use on other threads.");
+            Log.w(LOGTAG, Log.getStackTraceString(throwable));
+            StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
+        }
+    }
+
+    /** @hide send content invalidate */
+    protected void contentInvalidateAll() {
+        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
+            mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
+        }
+    }
+
+    /** @hide discard all textures from tiles. Used in Profiled WebView */
+    public void discardAllTextures() {
+        nativeDiscardAllTextures();
+    }
+
+    /**
+     * Begin collecting per-tile profiling data
+     *
+     * @hide only used by profiling tests
+     */
+    public void tileProfilingStart() {
+        nativeTileProfilingStart();
+    }
+    /**
+     * Return per-tile profiling data
+     *
+     * @hide only used by profiling tests
+     */
+    public float tileProfilingStop() {
+        return nativeTileProfilingStop();
+    }
+
+    /** @hide only used by profiling tests */
+    public void tileProfilingClear() {
+        nativeTileProfilingClear();
+    }
+    /** @hide only used by profiling tests */
+    public int tileProfilingNumFrames() {
+        return nativeTileProfilingNumFrames();
+    }
+    /** @hide only used by profiling tests */
+    public int tileProfilingNumTilesInFrame(int frame) {
+        return nativeTileProfilingNumTilesInFrame(frame);
+    }
+    /** @hide only used by profiling tests */
+    public int tileProfilingGetInt(int frame, int tile, String key) {
+        return nativeTileProfilingGetInt(frame, tile, key);
+    }
+    /** @hide only used by profiling tests */
+    public float tileProfilingGetFloat(int frame, int tile, String key) {
+        return nativeTileProfilingGetFloat(frame, tile, key);
+    }
+
+    /**
+     * Checks the focused content for an editable text field. This can be
+     * text input or ContentEditable.
+     * @return true if the focused item is an editable text field.
+     */
+    boolean focusCandidateIsEditableText() {
+        boolean isEditable = false;
+        // TODO: reverse sDisableNavcache so that its name is positive
+        boolean isNavcacheEnabled = !sDisableNavcache;
+        if (isNavcacheEnabled) {
+            isEditable = nativeFocusCandidateIsEditableText(mNativeClass);
+        } else if (mFocusedNode != null) {
+            isEditable = mFocusedNode.mEditable;
+        }
+        return isEditable;
+    }
+
+    // TODO: Remove this
+    Rect cursorRingBounds() {
+        if (sDisableNavcache) {
+            return new Rect();
+        }
+        return nativeGetCursorRingBounds();
+    }
+
+    // Called via JNI
+    private void postInvalidate() {
+        mWebView.postInvalidate();
+    }
+
+    private native int nativeCacheHitFramePointer();
+    private native boolean  nativeCacheHitIsPlugin();
+    private native Rect nativeCacheHitNodeBounds();
+    private native int nativeCacheHitNodePointer();
+    /* package */ native void nativeClearCursor();
+    private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
+    private native int      nativeCursorFramePointer();
+    private native Rect     nativeCursorNodeBounds();
+    private native int nativeCursorNodePointer();
+    private native boolean  nativeCursorIntersects(Rect visibleRect);
+    private native boolean  nativeCursorIsAnchor();
+    private native boolean  nativeCursorIsTextInput();
+    private native Point    nativeCursorPosition();
+    private native String   nativeCursorText();
+    /**
+     * Returns true if the native cursor node says it wants to handle key events
+     * (ala plugins). This can only be called if mNativeClass is non-zero!
+     */
+    private native boolean  nativeCursorWantsKeyEvents();
+    private native void     nativeDebugDump();
+    private native void     nativeDestroy();
+
+    /**
+     * Draw the picture set with a background color and extra. If
+     * "splitIfNeeded" is true and the return value is not 0, the return value
+     * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
+     * native allocation can be freed.
+     */
+    private native int nativeDraw(Canvas canvas, RectF visibleRect,
+            int color, int extra, boolean splitIfNeeded);
+    private native void     nativeDumpDisplayTree(String urlOrNull);
+    private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
+    private native int      nativeGetDrawGLFunction(int nativeInstance, Rect rect,
+            Rect viewRect, RectF visibleRect, float scale, int extras);
+    private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
+            RectF visibleRect, float scale);
+    private native void     nativeExtendSelection(int x, int y);
+    /* package */ native int      nativeFocusCandidateFramePointer();
+    /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
+    /* package */ native boolean  nativeFocusCandidateIsPassword();
+    private native boolean  nativeFocusCandidateIsRtlText();
+    private native boolean  nativeFocusCandidateIsTextInput();
+    private native boolean nativeFocusCandidateIsEditableText(int nativeClass);
+    /* package */ native int      nativeFocusCandidateMaxLength();
+    /* package */ native boolean  nativeFocusCandidateIsAutoComplete();
+    /* package */ native boolean  nativeFocusCandidateIsSpellcheck();
+    /* package */ native String   nativeFocusCandidateName();
+    private native Rect     nativeFocusCandidateNodeBounds();
+    /**
+     * @return A Rect with left, top, right, bottom set to the corresponding
+     * padding values in the focus candidate, if it is a textfield/textarea with
+     * a style.  Otherwise return null.  This is not actually a rectangle; Rect
+     * is being used to pass four integers.
+     */
+    private native Rect     nativeFocusCandidatePaddingRect();
+    /* package */ native int      nativeFocusCandidatePointer();
+    private native String   nativeFocusCandidateText();
+    /* package */ native float    nativeFocusCandidateTextSize();
+    /* package */ native int nativeFocusCandidateLineHeight();
+    /**
+     * Returns an integer corresponding to WebView.cpp::type.
+     * See WebTextView.setType()
+     */
+    private native int      nativeFocusCandidateType();
+    private native int      nativeFocusCandidateLayerId();
+    private native boolean  nativeFocusIsPlugin();
+    private native Rect     nativeFocusNodeBounds();
+    /* package */ native int nativeFocusNodePointer();
+    private native Rect     nativeGetCursorRingBounds();
+    private native String   nativeGetSelection();
+    private native boolean  nativeHasCursorNode();
+    private native boolean  nativeHasFocusNode();
+    private native void     nativeHideCursor();
+    private native boolean  nativeHitSelection(int x, int y);
+    private native String   nativeImageURI(int x, int y);
+    private native Rect     nativeLayerBounds(int layer);
+    /* package */ native boolean nativeMoveCursorToNextTextInput();
+    // return true if the page has been scrolled
+    private native boolean  nativeMotionUp(int x, int y, int slop);
+    // returns false if it handled the key
+    private native boolean  nativeMoveCursor(int keyCode, int count,
+            boolean noScroll);
+    private native int      nativeMoveGeneration();
+    /**
+     * @return true if the page should get the shift and arrow keys, rather
+     * than select text/navigation.
+     *
+     * If the focus is a plugin, or if the focus and cursor match and are
+     * a contentEditable element, then the page should handle these keys.
+     */
+    private native boolean  nativePageShouldHandleShiftAndArrows();
+    private native boolean  nativePointInNavCache(int x, int y, int slop);
+    private native void     nativeSelectBestAt(Rect rect);
+    private native void     nativeSelectAt(int x, int y);
+    private native void     nativeSetExtendSelection();
+    private native void     nativeSetFindIsUp(boolean isUp);
+    private native void     nativeSetHeightCanMeasure(boolean measure);
+    private native boolean  nativeSetBaseLayer(int nativeInstance,
+            int layer, Region invalRegion,
+            boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
+    private native int      nativeGetBaseLayer();
+    private native void     nativeShowCursorTimed();
+    private native void     nativeReplaceBaseContent(int content);
+    private native void     nativeCopyBaseContentToPicture(Picture pict);
+    private native boolean  nativeHasContent();
+    private native void     nativeSetSelectionPointer(int nativeInstance,
+            boolean set, float scale, int x, int y);
+    private native boolean  nativeStartSelection(int x, int y);
+    private native void     nativeStopGL();
+    private native Rect     nativeSubtractLayers(Rect content);
+    private native int      nativeTextGeneration();
+    private native void     nativeDiscardAllTextures();
+    private native void     nativeTileProfilingStart();
+    private native float    nativeTileProfilingStop();
+    private native void     nativeTileProfilingClear();
+    private native int      nativeTileProfilingNumFrames();
+    private native int      nativeTileProfilingNumTilesInFrame(int frame);
+    private native int      nativeTileProfilingGetInt(int frame, int tile, String key);
+    private native float    nativeTileProfilingGetFloat(int frame, int tile, String key);
+    // Never call this version except by updateCachedTextfield(String) -
+    // we always want to pass in our generation number.
+    private native void     nativeUpdateCachedTextfield(String updatedText,
+            int generation);
+    private native boolean  nativeWordSelection(int x, int y);
+    // return NO_LEFTEDGE means failure.
+    static final int NO_LEFTEDGE = -1;
+    native int nativeGetBlockLeftEdge(int x, int y, float scale);
+
+    private native void     nativeUseHardwareAccelSkia(boolean enabled);
+
+    // Returns a pointer to the scrollable LayerAndroid at the given point.
+    private native int      nativeScrollableLayer(int x, int y, Rect scrollRect,
+            Rect scrollBounds);
+    /**
+     * Scroll the specified layer.
+     * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
+     * @param newX Destination x position to which to scroll.
+     * @param newY Destination y position to which to scroll.
+     * @return True if the layer is successfully scrolled.
+     */
+    private native boolean  nativeScrollLayer(int layer, int newX, int newY);
+    private native void     nativeSetIsScrolling(boolean isScrolling);
+    private native int      nativeGetBackgroundColor();
+    native boolean  nativeSetProperty(String key, String value);
+    native String   nativeGetProperty(String key);
+    /**
+     * See {@link ComponentCallbacks2} for the trim levels and descriptions
+     */
+    private static native void     nativeOnTrimMemory(int level);
+    private static native void nativeSetPauseDrawing(int instance, boolean pause);
+    private static native boolean nativeDisableNavcache();
+    private static native void nativeSetTextSelection(int instance, int selection);
+    private static native int nativeGetHandleLayerId(int instance, int handle,
+            Rect cursorLocation);
+    private static native boolean nativeIsBaseFirst(int instance);
+}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e7da1a8..4bda5ef 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -39,7 +39,7 @@
 import android.view.MotionEvent;
 import android.view.SurfaceView;
 import android.view.View;
-import android.webkit.WebView.FocusNodeHref;
+import android.webkit.WebViewClassic.FocusNodeHref;
 
 import junit.framework.Assert;
 
@@ -72,11 +72,12 @@
      */
 
     // The WebView that corresponds to this WebViewCore.
-    private WebView mWebView;
+    // TODO: rename this field (and its getter) to mWebViewClassic or  mWebViewImpl.
+    private WebViewClassic mWebView;
     // Proxy for handling callbacks from native code
     private final CallbackProxy mCallbackProxy;
     // Settings object for maintaining all settings
-    private final WebSettings mSettings;
+    private final WebSettingsClassic mSettings;
     // Context for initializing the BrowserFrame with the proper assets.
     private final Context mContext;
     // The pointer to a native view object.
@@ -142,7 +143,7 @@
     // debugging other classes that require operation within the WebCore thread.
     /* package */ static final String THREAD_NAME = "WebViewCoreThread";
 
-    public WebViewCore(Context context, WebView w, CallbackProxy proxy,
+    public WebViewCore(Context context, WebViewClassic w, CallbackProxy proxy,
             Map<String, Object> javascriptInterfaces) {
         // No need to assign this in the WebCore thread.
         mCallbackProxy = proxy;
@@ -179,7 +180,7 @@
         // ready.
         mEventHub = new EventHub();
         // Create a WebSettings object for maintaining all settings
-        mSettings = new WebSettings(mContext, mWebView);
+        mSettings = new WebSettingsClassic(mContext, mWebView);
         // The WebIconDatabase needs to be initialized within the UI thread so
         // just request the instance here.
         WebIconDatabase.getInstance();
@@ -234,7 +235,7 @@
         // WebCore thread.
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.WEBCORE_INITIALIZED_MSG_ID,
+                    WebViewClassic.WEBCORE_INITIALIZED_MSG_ID,
                     mNativeClass, 0).sendToTarget();
         }
 
@@ -284,7 +285,7 @@
         BrowserFrame.sJavaBridge.resume();
     }
 
-    public WebSettings getSettings() {
+    public WebSettingsClassic getSettings() {
         return mSettings;
     }
 
@@ -329,7 +330,7 @@
      */
     private void formDidBlur(int nodePointer) {
         if (mWebView == null) return;
-        Message.obtain(mWebView.mPrivateHandler, WebView.FORM_DID_BLUR,
+        Message.obtain(mWebView.mPrivateHandler, WebViewClassic.FORM_DID_BLUR,
                 nodePointer, 0).sendToTarget();
     }
 
@@ -338,7 +339,7 @@
      */
     private void focusNodeChanged(WebKitHitTest hitTest) {
         if (mWebView == null) return;
-        mWebView.mPrivateHandler.obtainMessage(WebView.HIT_TEST_RESULT, hitTest)
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIT_TEST_RESULT, hitTest)
                 .sendToTarget();
     }
 
@@ -508,7 +509,7 @@
     protected void enterFullscreenForVideoLayer(int layerId, String url) {
         if (mWebView == null) return;
         Message message = Message.obtain(mWebView.mPrivateHandler,
-                       WebView.ENTER_FULLSCREEN_VIDEO, layerId, 0);
+                       WebViewClassic.ENTER_FULLSCREEN_VIDEO, layerId, 0);
         message.obj = url;
         message.sendToTarget();
     }
@@ -520,7 +521,7 @@
     protected void exitFullscreenVideo() {
         if (mWebView == null) return;
         Message message = Message.obtain(mWebView.mPrivateHandler,
-                       WebView.EXIT_FULLSCREEN_VIDEO);
+                       WebViewClassic.EXIT_FULLSCREEN_VIDEO);
         message.sendToTarget();
     }
 
@@ -886,7 +887,7 @@
         String mTitle;
         Rect[] mTouchRects;
         boolean mEditable;
-        int mTapHighlightColor = WebView.HIGHLIGHT_COLOR;
+        int mTapHighlightColor = WebViewClassic.HIGHLIGHT_COLOR;
         Rect[] mEnclosingParentRects;
         boolean mHasFocus;
 
@@ -923,13 +924,14 @@
     static class TextFieldInitData {
         public TextFieldInitData(int fieldPointer,
                 String text, int type, boolean isSpellCheckEnabled,
-                boolean isTextFieldNext, String label) {
+                boolean isTextFieldNext, String label, int maxLength) {
             mFieldPointer = fieldPointer;
             mText = text;
             mType = type;
             mIsSpellCheckEnabled = isSpellCheckEnabled;
             mIsTextFieldNext = isTextFieldNext;
             mLabel = label;
+            mMaxLength = maxLength;
         }
         int mFieldPointer;
         String mText;
@@ -937,6 +939,7 @@
         boolean mIsSpellCheckEnabled;
         boolean mIsTextFieldNext;
         String mLabel;
+        int mMaxLength;
     }
 
     // mAction of TouchEventData can be MotionEvent.getAction() which uses the
@@ -1268,7 +1271,7 @@
                                         msg.arg1, nodePointer);
                                 if (label != null && label.length() > 0) {
                                     Message.obtain(mWebView.mPrivateHandler,
-                                            WebView.RETURN_LABEL, nodePointer,
+                                            WebViewClassic.RETURN_LABEL, nodePointer,
                                             0, label).sendToTarget();
                                 }
                             }
@@ -1371,7 +1374,7 @@
                             break;
 
                         case VIEW_SIZE_CHANGED: {
-                            viewSizeChanged((WebView.ViewSizeData) msg.obj);
+                            viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj);
                             break;
                         }
                         case SET_SCROLL_OFFSET:
@@ -1527,7 +1530,7 @@
                                     ted.mMetaState);
                             Message.obtain(
                                     mWebView.mPrivateHandler,
-                                    WebView.PREVENT_TOUCH_ID,
+                                    WebViewClassic.PREVENT_TOUCH_ID,
                                     ted.mAction,
                                     ted.mNativeResult ? 1 : 0,
                                     ted).sendToTarget();
@@ -1592,7 +1595,7 @@
                             nativeUpdateFrameCache(mNativeClass);
                             // FIXME: this should provide a minimal rectangle
                             if (mWebView != null) {
-                                mWebView.postInvalidate();
+                                mWebView.getWebView().postInvalidate();
                             }
                             sendUpdateTextEntry();
                             break;
@@ -1619,7 +1622,8 @@
                             String modifiedSelectionString =
                                 nativeModifySelection(mNativeClass, msg.arg1,
                                         msg.arg2);
-                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
+                            mWebView.mPrivateHandler.obtainMessage(
+                                    WebViewClassic.SELECTION_STRING_CHANGED,
                                     modifiedSelectionString).sendToTarget();
                             break;
 
@@ -1664,12 +1668,12 @@
                             break;
 
                         case SAVE_WEBARCHIVE:
-                            WebView.SaveWebArchiveMessage saveMessage =
-                                (WebView.SaveWebArchiveMessage)msg.obj;
+                            WebViewClassic.SaveWebArchiveMessage saveMessage =
+                                (WebViewClassic.SaveWebArchiveMessage)msg.obj;
                             saveMessage.mResultFile =
                                 saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
                             mWebView.mPrivateHandler.obtainMessage(
-                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
+                                WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
                             break;
 
                         case GEOLOCATION_PERMISSIONS_PROVIDE:
@@ -1682,7 +1686,7 @@
                         case SPLIT_PICTURE_SET:
                             nativeSplitContent(mNativeClass, msg.arg1);
                             mWebView.mPrivateHandler.obtainMessage(
-                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
+                                    WebViewClassic.REPLACE_BASE_CONTENT, msg.arg1, 0);
                             mSplitPictureIsScheduled = false;
                             break;
 
@@ -1709,7 +1713,7 @@
                                 nativeUpdateFrameCache(mNativeClass);
                             }
                             Message message = mWebView.mPrivateHandler
-                                    .obtainMessage(WebView.DO_MOTION_UP,
+                                    .obtainMessage(WebViewClassic.DO_MOTION_UP,
                                     motionUpData.mX, motionUpData.mY);
                             mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
                                     message);
@@ -1745,7 +1749,7 @@
                             }
                             WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true);
                             mWebView.mPrivateHandler.obtainMessage(
-                                    WebView.HIT_TEST_RESULT, hit)
+                                    WebViewClassic.HIT_TEST_RESULT, hit)
                                     .sendToTarget();
                             break;
 
@@ -1755,7 +1759,7 @@
 
                         case AUTOFILL_FORM:
                             nativeAutoFillForm(mNativeClass, msg.arg1);
-                            mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null)
+                            mWebView.mPrivateHandler.obtainMessage(WebViewClassic.AUTOFILL_COMPLETE, null)
                                     .sendToTarget();
                             break;
 
@@ -1786,7 +1790,7 @@
                                     handles[0], handles[1], handles[2],
                                     handles[3]);
                             if (copiedText != null) {
-                                mWebView.mPrivateHandler.obtainMessage(WebView.COPY_TO_CLIPBOARD, copiedText)
+                                mWebView.mPrivateHandler.obtainMessage(WebViewClassic.COPY_TO_CLIPBOARD, copiedText)
                                         .sendToTarget();
                             }
                             break;
@@ -2056,7 +2060,7 @@
                 }
                 if (mWebView != null && evt.isDown()) {
                     Message.obtain(mWebView.mPrivateHandler,
-                            WebView.UNHANDLED_NAV_KEY, keyCode,
+                            WebViewClassic.UNHANDLED_NAV_KEY, keyCode,
                             0).sendToTarget();
                 }
                 return;
@@ -2079,7 +2083,7 @@
     private float mCurrentViewScale = 1.0f;
 
     // notify webkit that our virtual view size changed size (after inv-zoom)
-    private void viewSizeChanged(WebView.ViewSizeData data) {
+    private void viewSizeChanged(WebViewClassic.ViewSizeData data) {
         int w = data.mWidth;
         int h = data.mHeight;
         int textwrapWidth = data.mTextWrapWidth;
@@ -2123,7 +2127,7 @@
         if (mSettings.getUseWideViewPort()) {
             if (mViewportWidth == -1) {
                 // Fixed viewport width.
-                width = WebView.DEFAULT_VIEWPORT_WIDTH;
+                width = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
             } else if (mViewportWidth > 0) {
                 // Use website specified or desired fixed viewport width.
                 width = mViewportWidth;
@@ -2138,7 +2142,7 @@
     private void sendUpdateTextEntry() {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
+                    WebViewClassic.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
         }
     }
 
@@ -2228,9 +2232,9 @@
             // If anything more complex than position has been touched, let's do a full draw
             webkitDraw();
         }
-        mWebView.mPrivateHandler.removeMessages(WebView.INVAL_RECT_MSG_ID);
+        mWebView.mPrivateHandler.removeMessages(WebViewClassic.INVAL_RECT_MSG_ID);
         mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(mWebView.mPrivateHandler
-                .obtainMessage(WebView.INVAL_RECT_MSG_ID));
+                .obtainMessage(WebViewClassic.INVAL_RECT_MSG_ID));
     }
 
     private Boolean m_skipDrawFlag = false;
@@ -2287,7 +2291,7 @@
             draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
             if (mSettings.getUseWideViewPort()) {
                 draw.mMinPrefWidth = Math.max(
-                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
+                        mViewportWidth == -1 ? WebViewClassic.DEFAULT_VIEWPORT_WIDTH
                                 : (mViewportWidth == 0 ? mCurrentViewWidth
                                         : mViewportWidth),
                         nativeGetContentMinPrefWidth(mNativeClass));
@@ -2302,7 +2306,7 @@
             }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
+                    WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
         }
     }
 
@@ -2431,7 +2435,7 @@
         }
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0,
+                    WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0,
                     onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
             if (mDrawIsScheduled) {
                 mEventHub.sendMessage(Message.obtain(null,
@@ -2455,7 +2459,7 @@
     private void sendViewInvalidate(int left, int top, int right, int bottom) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                           WebView.INVAL_RECT_MSG_ID,
+                           WebViewClassic.INVAL_RECT_MSG_ID,
                            new Rect(left, top, right, bottom)).sendToTarget();
         }
     }
@@ -2471,7 +2475,7 @@
 
     // Gets the WebView corresponding to this WebViewCore. Note that the
     // WebView object must only be used on the UI thread.
-    /* package */ WebView getWebView() {
+    /* package */ WebViewClassic getWebView() {
         return mWebView;
     }
 
@@ -2497,9 +2501,9 @@
         }
 
         // remove the touch highlight when moving to a new page
-        if (WebView.sDisableNavcache) {
+        if (WebViewClassic.sDisableNavcache) {
             mWebView.mPrivateHandler.sendEmptyMessage(
-                    WebView.HIT_TEST_RESULT);
+                    WebViewClassic.HIT_TEST_RESULT);
         }
 
         // reset the scroll position, the restored offset and scales
@@ -2565,7 +2569,7 @@
         }
         if (adjust != mWebView.getDefaultZoomScale()) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
+                    WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
         }
         int defaultScale = (int) (adjust * 100);
 
@@ -2616,7 +2620,7 @@
             viewState.mScrollX = 0;
             viewState.mShouldStartScrolledRight = false;
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
+                    WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
             return;
         }
 
@@ -2687,7 +2691,7 @@
             mWebView.mLastHeightSent = 0;
             // Send a negative scale to indicate that WebCore should reuse
             // the current scale
-            WebView.ViewSizeData data = new WebView.ViewSizeData();
+            WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
             data.mWidth = mWebView.mLastWidthSent;
             data.mHeight = 0;
             // if mHeightCanMeasure is true, getUseWideViewPort() can't be
@@ -2711,7 +2715,7 @@
                 // to WebViewCore
                 mWebView.mLastWidthSent = 0;
             } else {
-                WebView.ViewSizeData data = new WebView.ViewSizeData();
+                WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
                 // mViewScale as 0 means it is in zoom overview mode. So we don't
                 // know the exact scale. If mRestoredScale is non-zero, use it;
                 // otherwise just use mTextWrapScale as the initial scale.
@@ -2789,7 +2793,7 @@
     private void needTouchEvents(boolean need) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
+                    WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
                     .sendToTarget();
         }
     }
@@ -2799,7 +2803,7 @@
             String text, int textGeneration) {
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
+                    WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
                     textGeneration, text);
             msg.getData().putBoolean("password", changeToPassword);
             msg.sendToTarget();
@@ -2811,7 +2815,7 @@
             int textGeneration, int selectionPtr) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
+                WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
                 new TextSelectionData(start, end, selectionPtr)).sendToTarget();
         }
     }
@@ -2820,22 +2824,23 @@
     private void clearTextEntry() {
         if (mWebView == null) return;
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+                WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget();
     }
 
     // called by JNI
     private void initEditField(int pointer, String text, int inputType,
             boolean isSpellCheckEnabled, boolean nextFieldIsText,
-            String label, int start, int end, int selectionPtr) {
+            String label, int start, int end, int selectionPtr, int maxLength) {
         if (mWebView == null) {
             return;
         }
         TextFieldInitData initData = new TextFieldInitData(pointer,
-                text, inputType, isSpellCheckEnabled, nextFieldIsText, label);
+                text, inputType, isSpellCheckEnabled, nextFieldIsText, label,
+                maxLength);
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.INIT_EDIT_FIELD, initData).sendToTarget();
+                WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget();
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+                WebViewClassic.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
                 0, new TextSelectionData(start, end, selectionPtr))
                 .sendToTarget();
     }
@@ -2847,7 +2852,7 @@
             return;
         }
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.UPDATE_MATCH_COUNT, matchIndex, matchCount,
+                WebViewClassic.UPDATE_MATCH_COUNT, matchIndex, matchCount,
                 findText).sendToTarget();
     }
 
@@ -2889,14 +2894,14 @@
     private void requestKeyboard(boolean showKeyboard) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
+                    WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
                     .sendToTarget();
         }
     }
 
     private void setWebTextViewAutoFillable(int queryId, String preview) {
         if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
+            Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE,
                     new AutoFillData(queryId, preview))
                     .sendToTarget();
         }
@@ -2909,7 +2914,7 @@
     // called by JNI
     private void keepScreenOn(boolean screenOn) {
         if (mWebView != null) {
-            Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SCREEN_ON);
+            Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SCREEN_ON);
             message.arg1 = screenOn ? 1 : 0;
             message.sendToTarget();
         }
@@ -2949,7 +2954,7 @@
             return;
         }
 
-        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
+        Message message = mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_FULLSCREEN);
         message.obj = childView.mView;
         message.arg1 = orientation;
         message.arg2 = npp;
@@ -2961,7 +2966,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
                 .sendToTarget();
     }
 
@@ -3033,7 +3038,7 @@
             data.mXPercentInView = xPercentInView;
             data.mYPercentInDoc = yPercentInDoc;
             data.mYPercentInView = yPercentInView;
-            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
+            Message.obtain(mWebView.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID,
                     data).sendToTarget();
         }
     }
@@ -3043,7 +3048,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT,
                 new Rect(x, y, x + width, y + height)).sendToTarget();
     }
 
@@ -3052,7 +3057,7 @@
         if (mWebView == null) {
             return;
         }
-        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
+        mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES,
                 hMode, vMode).sendToTarget();
     }
 
@@ -3060,7 +3065,7 @@
     @SuppressWarnings("unused")
     private void selectAt(int x, int y) {
         if (mWebView != null) {
-            mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget();
+            mWebView.mPrivateHandler.obtainMessage(WebViewClassic.SELECT_AT, x, y).sendToTarget();
         }
     }
 
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
new file mode 100644
index 0000000..22bf0bf
--- /dev/null
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This is the main entry-point into the WebView back end implementations, which the WebView
+ * proxy class uses to instantiate all the other objects as needed. The backend must provide an
+ * implementation of this interface, and make it available to the WebView via mechanism TBD.
+ * @hide
+ */
+public interface WebViewFactoryProvider {
+
+    /**
+     * Construct a new WebView provider.
+     * @param webView the WebView instance bound to this implementation instance. Note it will not
+     * necessarily be fully constructed at the point of this call: defer real initialization to
+     * WebViewProvider.init().
+     * @param privateAccess provides access into WebView internal methods.
+     */
+    WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess);
+
+    Statics getStatics();
+
+    /**
+     * This Interface provides glue for implementing the backend of WebView static methods which
+     * cannot be implemented in-situ in the proxy class.
+     */
+    interface Statics {
+        /**
+         * Implements the API method:
+         * {@link android.webkit.WebView#findAddress(String)}
+         */
+        String findAddress(String addr);
+
+        /**
+         * Implements the API methods:
+         * {@link android.webkit.WebView#enablePlatformNotifications()}
+         * {@link android.webkit.WebView#disablePlatformNotifications()}
+         */
+        void setPlatformNotificationsEnabled(boolean enable);
+    }
+}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
new file mode 100644
index 0000000..2e8ad6d
--- /dev/null
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.webkit.WebView.HitTestResult;
+import android.webkit.WebView.PictureListener;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * WebView backend provider interface: this interface is the abstract backend to a WebView
+ * instance; each WebView object is bound to exactly one WebViewProvider object which implements
+ * the runtime behavior of that WebView.
+ *
+ * All methods must behave as per their namesake in {@link WebView}, unless otherwise noted.
+ *
+ * @hide Not part of the public API; only required by system implementors.
+ */
+public interface WebViewProvider {
+    //-------------------------------------------------------------------------
+    // Main interface for backend provider of the WebView class.
+    //-------------------------------------------------------------------------
+    /**
+     * Initialize this WebViewProvider instance. Called after the WebView has fully constructed.
+     * @param javaScriptInterfaces is a Map of interface names, as keys, and
+     * object implementing those interfaces, as values.
+     * @param privateBrowsing If true the web view will be initialized in private / incognito mode.
+     */
+    public void init(Map<String, Object> javaScriptInterfaces,
+            boolean privateBrowsing);
+
+    public void setHorizontalScrollbarOverlay(boolean overlay);
+
+    public void setVerticalScrollbarOverlay(boolean overlay);
+
+    public boolean overlayHorizontalScrollbar();
+
+    public boolean overlayVerticalScrollbar();
+
+    public int getVisibleTitleHeight();
+
+    public SslCertificate getCertificate();
+
+    public void setCertificate(SslCertificate certificate);
+
+    public void savePassword(String host, String username, String password);
+
+    public void setHttpAuthUsernamePassword(String host, String realm,
+            String username, String password);
+
+    public String[] getHttpAuthUsernamePassword(String host, String realm);
+
+    /**
+     * See {@link WebView#destroy()}.
+     * As well as releasing the internal state and resources held by the implementation,
+     * the provider should null all references it holds on the WebView proxy class, and ensure
+     * no further method calls are made to it.
+     */
+    public void destroy();
+
+    public void setNetworkAvailable(boolean networkUp);
+
+    public WebBackForwardList saveState(Bundle outState);
+
+    public boolean savePicture(Bundle b, final File dest);
+
+    public boolean restorePicture(Bundle b, File src);
+
+    public WebBackForwardList restoreState(Bundle inState);
+
+    public void loadUrl(String url, Map<String, String> additionalHttpHeaders);
+
+    public void loadUrl(String url);
+
+    public void postUrl(String url, byte[] postData);
+
+    public void loadData(String data, String mimeType, String encoding);
+
+    public void loadDataWithBaseURL(String baseUrl, String data,
+            String mimeType, String encoding, String historyUrl);
+
+    public void saveWebArchive(String filename);
+
+    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback);
+
+    public void stopLoading();
+
+    public void reload();
+
+    public boolean canGoBack();
+
+    public void goBack();
+
+    public boolean canGoForward();
+
+    public void goForward();
+
+    public boolean canGoBackOrForward(int steps);
+
+    public void goBackOrForward(int steps);
+
+    public boolean isPrivateBrowsingEnabled();
+
+    public boolean pageUp(boolean top);
+
+    public boolean pageDown(boolean bottom);
+
+    public void clearView();
+
+    public Picture capturePicture();
+
+    public float getScale();
+
+    public void setInitialScale(int scaleInPercent);
+
+    public void invokeZoomPicker();
+
+    public HitTestResult getHitTestResult();
+
+    public void requestFocusNodeHref(Message hrefMsg);
+
+    public void requestImageRef(Message msg);
+
+    public String getUrl();
+
+    public String getOriginalUrl();
+
+    public String getTitle();
+
+    public Bitmap getFavicon();
+
+    public String getTouchIconUrl();
+
+    public int getProgress();
+
+    public int getContentHeight();
+
+    public int getContentWidth();
+
+    public void pauseTimers();
+
+    public void resumeTimers();
+
+    public void onPause();
+
+    public void onResume();
+
+    public boolean isPaused();
+
+    public void freeMemory();
+
+    public void clearCache(boolean includeDiskFiles);
+
+    public void clearFormData();
+
+    public void clearHistory();
+
+    public void clearSslPreferences();
+
+    public WebBackForwardList copyBackForwardList();
+
+    public void findNext(boolean forward);
+
+    public int findAll(String find);
+
+    public boolean showFindDialog(String text, boolean showIme);
+
+    public void clearMatches();
+
+    public void documentHasImages(Message response);
+
+    public void setWebViewClient(WebViewClient client);
+
+    public void setDownloadListener(DownloadListener listener);
+
+    public void setWebChromeClient(WebChromeClient client);
+
+    public void setPictureListener(PictureListener listener);
+
+    public void addJavascriptInterface(Object obj, String interfaceName);
+
+    public void removeJavascriptInterface(String interfaceName);
+
+    public WebSettings getSettings();
+
+    public void emulateShiftHeld();
+
+    public void setMapTrackballToArrowKeys(boolean setMap);
+
+    public void flingScroll(int vx, int vy);
+
+    public View getZoomControls();
+
+    public boolean canZoomIn();
+
+    public boolean canZoomOut();
+
+    public boolean zoomIn();
+
+    public boolean zoomOut();
+
+    public void debugDump();
+
+    //-------------------------------------------------------------------------
+    // Provider glue methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * @return the ViewDelegate implementation. This provides the functionality to back all of
+     * the name-sake functions from the View and ViewGroup base classes of WebView.
+     */
+    /* package */ ViewDelegate getViewDelegate();
+
+    /**
+     * @return a ScrollDelegate implementation. Normally this would be same object as is
+     * returned by getViewDelegate().
+     */
+    /* package */ ScrollDelegate getScrollDelegate();
+
+    //-------------------------------------------------------------------------
+    // View / ViewGroup delegation methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated
+     * into the WebViewProvider instance.
+     * NOTE For many of these methods, the WebView will provide a super.Foo() call before or after
+     * making the call into the provider instance. This is done for convenience in the common case
+     * of maintaining backward compatibility. For remaining super class calls (e.g. where the
+     * provider may need to only conditionally make the call based on some internal state) see the
+     * {@link WebView.PrivateAccess} callback class.
+     */
+    // TODO: See if the pattern of the super-class calls can be rationalized at all, and document
+    // the remainder on the methods below.
+    interface ViewDelegate {
+        public boolean shouldDelayChildPressedState();
+
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
+
+        public void onInitializeAccessibilityEvent(AccessibilityEvent event);
+
+        public void setOverScrollMode(int mode);
+
+        public void setScrollBarStyle(int style);
+
+        public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t,
+                int r, int b);
+
+        public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY);
+
+        public void onWindowVisibilityChanged(int visibility);
+
+        public boolean drawChild(Canvas canvas, View child, long drawingTime);
+
+        public void onDraw(Canvas canvas);
+
+        public void setLayoutParams(LayoutParams layoutParams);
+
+        public boolean performLongClick();
+
+        public void onConfigurationChanged(Configuration newConfig);
+
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs);
+
+        public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
+
+        public boolean onKeyDown(int keyCode, KeyEvent event);
+
+        public boolean onKeyUp(int keyCode, KeyEvent event);
+
+        public void onAttachedToWindow();
+
+        public void onDetachedFromWindow();
+
+        public void onVisibilityChanged(View changedView, int visibility);
+
+        public void onWindowFocusChanged(boolean hasWindowFocus);
+
+        public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect);
+
+        public boolean setFrame(int left, int top, int right, int bottom);
+
+        public void onSizeChanged(int w, int h, int ow, int oh);
+
+        public void onScrollChanged(int l, int t, int oldl, int oldt);
+
+        public boolean dispatchKeyEvent(KeyEvent event);
+
+        public boolean onTouchEvent(MotionEvent ev);
+
+        public boolean onHoverEvent(MotionEvent event);
+
+        public boolean onGenericMotionEvent(MotionEvent event);
+
+        public boolean onTrackballEvent(MotionEvent ev);
+
+        public boolean requestFocus(int direction, Rect previouslyFocusedRect);
+
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
+
+        public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate);
+
+        public void setBackgroundColor(int color);
+    }
+
+    interface ScrollDelegate {
+        // These methods are declared protected in the ViewGroup base class. This interface
+        // exists to promote them to public so they may be called by the WebView proxy class.
+        // TODO: Combine into ViewDelegate?
+        /**
+         * See {@link android.webkit.WebView#computeHorizontalScrollRange}
+         */
+        public int computeHorizontalScrollRange();
+
+        /**
+         * See {@link android.webkit.WebView#computeHorizontalScrollOffset}
+         */
+        public int computeHorizontalScrollOffset();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollRange}
+         */
+        public int computeVerticalScrollRange();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollOffset}
+         */
+        public int computeVerticalScrollOffset();
+
+        /**
+         * See {@link android.webkit.WebView#computeVerticalScrollExtent}
+         */
+        public int computeVerticalScrollExtent();
+
+        /**
+         * See {@link android.webkit.WebView#computeScroll}
+         */
+        public void computeScroll();
+    }
+}
diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java
index e505614..d2a0561 100644
--- a/core/java/android/webkit/ZoomControlEmbedded.java
+++ b/core/java/android/webkit/ZoomControlEmbedded.java
@@ -25,12 +25,12 @@
 class ZoomControlEmbedded implements ZoomControlBase {
 
     private final ZoomManager mZoomManager;
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
 
     // The controller is lazily initialized in getControls() for performance.
     private ZoomButtonsController mZoomButtonsController;
 
-    public ZoomControlEmbedded(ZoomManager zoomManager, WebView webView) {
+    public ZoomControlEmbedded(ZoomManager zoomManager, WebViewClassic webView) {
         mZoomManager = zoomManager;
         mWebView = webView;
     }
@@ -41,7 +41,7 @@
             mZoomButtonsController.setVisible(true);
 
             if (mZoomManager.isDoubleTapEnabled()) {
-                WebSettings settings = mWebView.getSettings();
+                WebSettingsClassic settings = mWebView.getSettings();
                 int count = settings.getDoubleTapToastCount();
                 if (mZoomManager.isInZoomOverview() && count > 0) {
                     settings.setDoubleTapToastCount(--count);
@@ -82,7 +82,7 @@
 
     private ZoomButtonsController getControls() {
         if (mZoomButtonsController == null) {
-            mZoomButtonsController = new ZoomButtonsController(mWebView);
+            mZoomButtonsController = new ZoomButtonsController(mWebView.getWebView());
             mZoomButtonsController.setOnZoomListener(new ZoomListener());
             // ZoomButtonsController positions the buttons at the bottom, but in
             // the middle. Change their layout parameters so they appear on the
diff --git a/core/java/android/webkit/ZoomControlExternal.java b/core/java/android/webkit/ZoomControlExternal.java
index d75313e..f5bfc05 100644
--- a/core/java/android/webkit/ZoomControlExternal.java
+++ b/core/java/android/webkit/ZoomControlExternal.java
@@ -35,9 +35,9 @@
     private Runnable mZoomControlRunnable;
     private final Handler mPrivateHandler = new Handler();
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
 
-    public ZoomControlExternal(WebView webView) {
+    public ZoomControlExternal(WebViewClassic webView) {
         mWebView = webView;
     }
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 369e883..e7b049e 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -50,7 +50,7 @@
 
     static final String LOGTAG = "webviewZoom";
 
-    private final WebView mWebView;
+    private final WebViewClassic mWebView;
     private final CallbackProxy mCallbackProxy;
 
     // Widgets responsible for the on-screen zoom functions of the WebView.
@@ -211,7 +211,7 @@
     private boolean mHardwareAccelerated = false;
     private boolean mInHWAcceleratedZoom = false;
 
-    public ZoomManager(WebView webView, CallbackProxy callbackProxy) {
+    public ZoomManager(WebViewClassic webView, CallbackProxy callbackProxy) {
         mWebView = webView;
         mCallbackProxy = callbackProxy;
 
@@ -220,7 +220,7 @@
          * ESPN and Engadget always have wider mContentWidth no matter what the
          * viewport size is.
          */
-        setZoomOverviewWidth(WebView.DEFAULT_VIEWPORT_WIDTH);
+        setZoomOverviewWidth(WebViewClassic.DEFAULT_VIEWPORT_WIDTH);
 
         mFocusMovementQueue = new FocusMovementQueue();
     }
@@ -487,13 +487,13 @@
         // zoomScale, we can't use the WebView's pinLocX/Y functions directly.
         float scale = zoomScale * mInvInitialZoomScale;
         int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX) - mZoomCenterX);
-        tx = -WebView.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth()
+        tx = -WebViewClassic.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth()
                 * zoomScale)) + mWebView.getScrollX();
         int titleHeight = mWebView.getTitleHeight();
         int ty = Math.round(scale
                 * (mInitialScrollY + mZoomCenterY - titleHeight)
                 - (mZoomCenterY - titleHeight));
-        ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebView.pinLoc(ty
+        ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebViewClassic.pinLoc(ty
                 - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight()
                 * zoomScale)) + titleHeight) + mWebView.getScrollY();
 
@@ -630,7 +630,7 @@
     public void handleDoubleTap(float lastTouchX, float lastTouchY) {
         // User takes action, set initial zoom overview to false.
         mInitialZoomOverview = false;
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (!isDoubleTapEnabled()) {
             return;
         }
@@ -690,7 +690,7 @@
 
     private void setZoomOverviewWidth(int width) {
         if (width == 0) {
-            mZoomOverviewWidth = WebView.DEFAULT_VIEWPORT_WIDTH;
+            mZoomOverviewWidth = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
         } else {
             mZoomOverviewWidth = width;
         }
@@ -719,7 +719,7 @@
         final float readingScale = getReadingLevelScale();
 
         int left = mWebView.getBlockLeftEdge(mAnchorX, mAnchorY, readingScale);
-        if (left != WebView.NO_LEFTEDGE) {
+        if (left != WebViewClassic.NO_LEFTEDGE) {
             // add a 5pt padding to the left edge.
             int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5))
                     - mWebView.getScrollX();
@@ -728,7 +728,7 @@
             if (viewLeft > 0) {
                 mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
             } else {
-                mWebView.scrollBy(viewLeft, 0);
+                mWebView.getWebView().scrollBy(viewLeft, 0);
                 mZoomCenterX = 0;
             }
         }
@@ -955,7 +955,7 @@
         // cause its child View to reposition itself through ViewManager's
         // scaleAll(), we need to post a Runnable to ensure requestLayout().
         // Additionally, only update the text wrap scale if the width changed.
-        mWebView.post(new PostScale(w != ow &&
+        mWebView.getWebView().post(new PostScale(w != ow &&
             !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview, w < ow));
     }
 
@@ -1027,7 +1027,7 @@
         final int viewWidth = mWebView.getViewWidth();
         final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
         final float newZoomOverviewScale = getZoomOverviewScale();
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() &&
             settings.getUseFixedViewport() &&
             (mInitialZoomOverview || mInZoomOverview)) {
@@ -1085,7 +1085,7 @@
             if (drawData.mContentSize.x > 0) {
                 // The webkitDraw for layers will not populate contentSize, and it'll be
                 // ignored for zoom overview width update.
-                newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
+                newZoomOverviewWidth = Math.min(WebViewClassic.sMaxViewportWidth,
                     drawData.mContentSize.x);
             }
         } else {
@@ -1117,7 +1117,7 @@
         updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
         setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
         final float overviewScale = getZoomOverviewScale();
-        WebSettings settings = mWebView.getSettings();
+        WebSettingsClassic settings = mWebView.getSettings();
         if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) {
             mMinZoomScale = (mInitialScale > 0) ?
                     Math.min(mInitialScale, overviewScale) : overviewScale;
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 85252af..2a74f6a 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -154,21 +154,25 @@
 
     private final int mWeekSeperatorLineWidth;
 
-    private final int mDateTextSize;
+    private int mDateTextSize;
 
-    private final Drawable mSelectedDateVerticalBar;
+    private Drawable mSelectedDateVerticalBar;
 
     private final int mSelectedDateVerticalBarWidth;
 
-    private final int mSelectedWeekBackgroundColor;
+    private int mSelectedWeekBackgroundColor;
 
-    private final int mFocusedMonthDateColor;
+    private int mFocusedMonthDateColor;
 
-    private final int mUnfocusedMonthDateColor;
+    private int mUnfocusedMonthDateColor;
 
-    private final int mWeekSeparatorLineColor;
+    private int mWeekSeparatorLineColor;
 
-    private final int mWeekNumberColor;
+    private int mWeekNumberColor;
+
+    private int mWeekDayTextAppearanceResId;
+
+    private int mDateTextAppearanceResId;
 
     /**
      * The top offset of the weeks list.
@@ -366,15 +370,11 @@
         mSelectedDateVerticalBar = attributesArray.getDrawable(
                 R.styleable.CalendarView_selectedDateVerticalBar);
 
-        int dateTextAppearanceResId= attributesArray.getResourceId(
+        mDateTextAppearanceResId = attributesArray.getResourceId(
                 R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
-        TypedArray dateTextAppearance = context.obtainStyledAttributes(dateTextAppearanceResId,
-                com.android.internal.R.styleable.TextAppearance);
-        mDateTextSize = dateTextAppearance.getDimensionPixelSize(
-                R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
-        dateTextAppearance.recycle();
+        updateDateTextSize();
 
-        int weekDayTextAppearanceResId = attributesArray.getResourceId(
+        mWeekDayTextAppearanceResId = attributesArray.getResourceId(
                 R.styleable.CalendarView_weekDayTextAppearance,
                 DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
         attributesArray.recycle();
@@ -400,7 +400,7 @@
         mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
         mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
 
-        setUpHeader(weekDayTextAppearanceResId);
+        setUpHeader();
         setUpListView();
         setUpAdapter();
 
@@ -417,6 +417,235 @@
         invalidate();
     }
 
+    /**
+     * Sets the number of weeks to be shown.
+     *
+     * @param count The shown week count.
+     */
+    public void setShownWeekCount(int count) {
+        if (mShownWeekCount != count) {
+            mShownWeekCount = count;
+            invalidate();
+        }
+    }
+
+    /**
+     * Gets the number of weeks to be shown.
+     *
+     * @return The shown week count.
+     */
+    public int getShownWeekCount() {
+        return mShownWeekCount;
+    }
+
+    /**
+     * Sets the background color for the selected week.
+     *
+     * @param color The week background color.
+     */
+    public void setSelectedWeekBackgroundColor(int color) {
+        if (mSelectedWeekBackgroundColor != color) {
+            mSelectedWeekBackgroundColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasSelectedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the background color for the selected week.
+     *
+     * @return The week background color.
+     */
+    public int getSelectedWeekBackgroundColor() {
+        return mSelectedWeekBackgroundColor;
+    }
+
+    /**
+     * Sets the color for the dates of the focused month.
+     *
+     * @param color The focused month date color.
+     */
+    public void setFocusedMonthDateColor(int color) {
+        if (mFocusedMonthDateColor != color) {
+            mFocusedMonthDateColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasFocusedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the color for the dates in the focused month.
+     *
+     * @return The focused month date color.
+     */
+    public int getFocusedMonthDateColor() {
+        return mFocusedMonthDateColor;
+    }
+
+    /**
+     * Sets the color for the dates of a not focused month.
+     *
+     * @param color A not focused month date color.
+     */
+    public void setUnfocusedMonthDateColor(int color) {
+        if (mUnfocusedMonthDateColor != color) {
+            mUnfocusedMonthDateColor = color;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasUnfocusedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the color for the dates in a not focused month.
+     *
+     * @return A not focused month date color.
+     */
+    public int getUnfocusedMonthDateColor() {
+        return mFocusedMonthDateColor;
+    }
+
+    /**
+     * Sets the color for the week numbers.
+     *
+     * @param color The week number color.
+     */
+    public void setWeekNumberColor(int color) {
+        if (mWeekNumberColor != color) {
+            mWeekNumberColor = color;
+            if (mShowWeekNumber) {
+                invalidateAllWeekViews();
+            }
+        }
+    }
+
+    /**
+     * Gets the color for the week numbers.
+     *
+     * @return The week number color.
+     */
+    public int getWeekNumberColor() {
+        return mWeekNumberColor;
+    }
+
+    /**
+     * Sets the color for the separator line between weeks.
+     *
+     * @param color The week separator color.
+     */
+    public void setWeekSeparatorLineColor(int color) {
+        if (mWeekSeparatorLineColor != color) {
+            mWeekSeparatorLineColor = color;
+            invalidateAllWeekViews();
+        }
+    }
+
+    /**
+     * Gets the color for the separator line between weeks.
+     *
+     * @return The week separator color.
+     */
+    public int getWeekSeparatorLineColor() {
+        return mWeekSeparatorLineColor;
+    }
+
+    /**
+     * Sets the drawable for the vertical bar shown at the beginning and at
+     * the end of the selected date.
+     *
+     * @param resourceId The vertical bar drawable resource id.
+     */
+    public void setSelectedDateVerticalBar(int resourceId) {
+        Drawable drawable = getResources().getDrawable(resourceId);
+        setSelectedDateVerticalBar(drawable);
+    }
+
+    /**
+     * Sets the drawable for the vertical bar shown at the beginning and at
+     * the end of the selected date.
+     *
+     * @param drawable The vertical bar drawable.
+     */
+    public void setSelectedDateVerticalBar(Drawable drawable) {
+        if (mSelectedDateVerticalBar != drawable) {
+            mSelectedDateVerticalBar = drawable;
+            final int childCount = mListView.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                WeekView weekView = (WeekView) mListView.getChildAt(i);
+                if (weekView.mHasSelectedDay) {
+                    weekView.invalidate();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets the drawable for the vertical bar shown at the beginning and at
+     * the end of the selected date.
+     *
+     * @return The vertical bar drawable.
+     */
+    public Drawable getSelectedDateVerticalBar() {
+        return mSelectedDateVerticalBar;
+    }
+
+    /**
+     * Sets the text appearance for the week day abbreviation of the calendar header.
+     *
+     * @param resourceId The text appearance resource id.
+     */
+    public void setWeekDayTextAppearance(int resourceId) {
+        if (mWeekDayTextAppearanceResId != resourceId) {
+            mWeekDayTextAppearanceResId = resourceId;
+            setUpHeader();
+        }
+    }
+
+    /**
+     * Gets the text appearance for the week day abbreviation of the calendar header.
+     *
+     * @return The text appearance resource id.
+     */
+    public int getWeekDayTextAppearance() {
+        return mWeekDayTextAppearanceResId;
+    }
+
+    /**
+     * Sets the text appearance for the calendar dates.
+     *
+     * @param resourceId The text appearance resource id.
+     */
+    public void setDateTextAppearance(int resourceId) {
+        if (mDateTextAppearanceResId != resourceId) {
+            mDateTextAppearanceResId = resourceId;
+            updateDateTextSize();
+            invalidateAllWeekViews();
+        }
+    }
+
+    /**
+     * Gets the text appearance for the calendar dates.
+     *
+     * @return The text appearance resource id.
+     */
+    public int getDateTextAppearance() {
+        return mDateTextAppearanceResId;
+    }
+
     @Override
     public void setEnabled(boolean enabled) {
         mListView.setEnabled(enabled);
@@ -545,7 +774,7 @@
         }
         mShowWeekNumber = showWeekNumber;
         mAdapter.notifyDataSetChanged();
-        setUpHeader(DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+        setUpHeader();
     }
 
     /**
@@ -594,7 +823,7 @@
         mFirstDayOfWeek = firstDayOfWeek;
         mAdapter.init();
         mAdapter.notifyDataSetChanged();
-        setUpHeader(DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+        setUpHeader();
     }
 
     /**
@@ -655,6 +884,25 @@
         goTo(mTempDate, animate, true, center);
     }
 
+    private void updateDateTextSize() {
+        TypedArray dateTextAppearance = getContext().obtainStyledAttributes(
+                mDateTextAppearanceResId, R.styleable.TextAppearance);
+        mDateTextSize = dateTextAppearance.getDimensionPixelSize(
+                R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+        dateTextAppearance.recycle();
+    }
+
+    /**
+     * Invalidates all week views.
+     */
+    private void invalidateAllWeekViews() {
+        final int childCount = mListView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mListView.getChildAt(i);
+            view.invalidate();
+        }
+    }
+
     /**
      * Sets the current locale.
      *
@@ -727,7 +975,7 @@
     /**
      * Sets up the strings to be used by the header.
      */
-    private void setUpHeader(int weekDayTextAppearanceResId) {
+    private void setUpHeader() {
         mDayLabels = new String[mDaysPerWeek];
         for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
             int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
@@ -743,8 +991,8 @@
         }
         for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
             label = (TextView) mDayNamesHeader.getChildAt(i);
-            if (weekDayTextAppearanceResId > -1) {
-                label.setTextAppearance(mContext, weekDayTextAppearanceResId);
+            if (mWeekDayTextAppearanceResId > -1) {
+                label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
             }
             if (i < mDaysPerWeek + 1) {
                 label.setText(mDayLabels[i - 1]);
@@ -1198,6 +1446,12 @@
         // Quick lookup for checking which days are in the focus month
         private boolean[] mFocusDay;
 
+        // Whether this view has a focused day.
+        private boolean mHasFocusedDay;
+
+        // Whether this view has only focused days.
+        private boolean mHasUnfocusedDay;
+
         // The first day displayed by this item
         private Calendar mFirstDay;
 
@@ -1235,11 +1489,8 @@
         public WeekView(Context context) {
             super(context);
 
-            mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
-                    .getPaddingBottom()) / mShownWeekCount;
-
             // Sets up any standard paints that will be used
-            setPaintProperties();
+            initilaizePaints();
         }
 
         /**
@@ -1281,8 +1532,12 @@
             mFirstDay = (Calendar) mTempDate.clone();
             mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
 
+            mHasUnfocusedDay = true;
             for (; i < mNumCells; i++) {
-                mFocusDay[i] = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+                final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+                mFocusDay[i] = isFocusedDay;
+                mHasFocusedDay |= isFocusedDay;
+                mHasUnfocusedDay &= !isFocusedDay;
                 // do not draw dates outside the valid range to avoid user confusion
                 if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
                     mDayNumbers[i] = "";
@@ -1302,18 +1557,15 @@
         }
 
         /**
-         * Sets up the text and style properties for painting.
+         * Initialize the paint isntances.
          */
-        private void setPaintProperties() {
+        private void initilaizePaints() {
             mDrawPaint.setFakeBoldText(false);
             mDrawPaint.setAntiAlias(true);
-            mDrawPaint.setTextSize(mDateTextSize);
             mDrawPaint.setStyle(Style.FILL);
 
             mMonthNumDrawPaint.setFakeBoldText(true);
             mMonthNumDrawPaint.setAntiAlias(true);
-            mMonthNumDrawPaint.setTextSize(mDateTextSize);
-            mMonthNumDrawPaint.setColor(mFocusedMonthDateColor);
             mMonthNumDrawPaint.setStyle(Style.FILL);
             mMonthNumDrawPaint.setTextAlign(Align.CENTER);
         }
@@ -1369,7 +1621,7 @@
         @Override
         protected void onDraw(Canvas canvas) {
             drawBackground(canvas);
-            drawWeekNumbers(canvas);
+            drawWeekNumbersAndDates(canvas);
             drawWeekSeparators(canvas);
             drawSelectedDateVerticalBars(canvas);
         }
@@ -1401,12 +1653,13 @@
          *
          * @param canvas The canvas to draw on
          */
-        private void drawWeekNumbers(Canvas canvas) {
+        private void drawWeekNumbersAndDates(Canvas canvas) {
             float textHeight = mDrawPaint.getTextSize();
             int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
             int nDays = mNumCells;
 
             mDrawPaint.setTextAlign(Align.CENTER);
+            mDrawPaint.setTextSize(mDateTextSize);
             int i = 0;
             int divisor = 2 * nDays;
             if (mShowWeekNumber) {
@@ -1487,6 +1740,8 @@
 
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
+                    .getPaddingBottom()) / mShownWeekCount;
             setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
         }
     }
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index dd53325..603cea1 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -143,7 +143,7 @@
     }
 
     @Override
-    public void onResolvePadding(int layoutDirection) {
+    public void onPaddingChanged(int layoutDirection) {
         int newPadding = (mCheckMarkDrawable != null) ?
                 mCheckMarkWidth + mBasePadding : mBasePadding;
         mNeedRequestlayout |= (mPaddingRight != newPadding);
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 110c8f3..fd93980 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -162,7 +162,7 @@
         int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
         String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
         String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
-        int layoutResourceId = attributesArray.getResourceId(R.styleable.DatePicker_layout,
+        int layoutResourceId = attributesArray.getResourceId(R.styleable.DatePicker_internalLayout,
                 R.layout.date_picker);
         attributesArray.recycle();
 
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index b5deec7..a1bea43 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -309,7 +309,7 @@
             if (child != null && child.getVisibility() != GONE) {
                 if (hasDividerBeforeChildAt(i)) {
                     final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                    final int top = child.getTop() - lp.topMargin;
+                    final int top = child.getTop() - lp.topMargin - mDividerHeight;
                     drawHorizontalDivider(canvas, top);
                 }
             }
@@ -336,7 +336,7 @@
             if (child != null && child.getVisibility() != GONE) {
                 if (hasDividerBeforeChildAt(i)) {
                     final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                    final int left = child.getLeft() - lp.leftMargin;
+                    final int left = child.getLeft() - lp.leftMargin - mDividerWidth;
                     drawVerticalDivider(canvas, left);
                 }
             }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 84e86af..3335da0 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -49,6 +49,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
@@ -373,9 +374,9 @@
     private float mLastMotionEventY;
 
     /**
-     * Flag if to begin edit on next up event.
+     * Flag if to check for double tap and potentially start edit.
      */
-    private boolean mBeginEditOnUpEvent;
+    private boolean mCheckBeginEditOnUpEvent;
 
     /**
      * Flag if to adjust the selector wheel on next up event.
@@ -453,6 +454,11 @@
     private boolean mScrollWheelAndFadingEdgesInitialized;
 
     /**
+     * The time of the last up event.
+     */
+    private long mLastUpEventTimeMillis;
+
+    /**
      * Interface to listen for changes of the current value.
      */
     public interface OnValueChangeListener {
@@ -553,17 +559,17 @@
                 getResources().getDisplayMetrics());
         mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
                 R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
-        mMinHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minHeight,
-                SIZE_UNSPECIFIED);
-        mMaxHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxHeight,
-                SIZE_UNSPECIFIED);
+        mMinHeight = attributesArray.getDimensionPixelSize(
+                R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
+        mMaxHeight = attributesArray.getDimensionPixelSize(
+                R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
         if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
                 && mMinHeight > mMaxHeight) {
             throw new IllegalArgumentException("minHeight > maxHeight");
         }
-        mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minWidth,
+        mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_internalMinWidth,
                 SIZE_UNSPECIFIED);
-        mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxWidth,
+        mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_internalMaxWidth,
                 SIZE_UNSPECIFIED);
         if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
                 && mMinWidth > mMaxWidth) {
@@ -628,10 +634,6 @@
             public void onFocusChange(View v, boolean hasFocus) {
                 if (hasFocus) {
                     mInputText.selectAll();
-                    InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
-                    if (inputMethodManager != null) {
-                        inputMethodManager.showSoftInput(mInputText, 0);
-                    }
                 } else {
                     mInputText.setSelection(0, 0);
                     validateInputTextView(v);
@@ -643,6 +645,7 @@
         });
 
         mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
+        mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
 
         // initialize constants
         mTouchSlop = ViewConfiguration.getTapTimeout();
@@ -777,7 +780,7 @@
                 removeAllCallbacks();
                 mShowInputControlsAnimator.cancel();
                 mDimSelectorWheelAnimator.cancel();
-                mBeginEditOnUpEvent = false;
+                mCheckBeginEditOnUpEvent = false;
                 mAdjustScrollerOnUpEvent = true;
                 if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
                     mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
@@ -788,7 +791,7 @@
                         mAdjustScroller.forceFinished(true);
                         onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     }
-                    mBeginEditOnUpEvent = scrollersFinished;
+                    mCheckBeginEditOnUpEvent = scrollersFinished;
                     mAdjustScrollerOnUpEvent = true;
                     hideSoftInput();
                     hideInputControls();
@@ -807,7 +810,7 @@
                 float currentMoveY = event.getY();
                 int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
                 if (deltaDownY > mTouchSlop) {
-                    mBeginEditOnUpEvent = false;
+                    mCheckBeginEditOnUpEvent = false;
                     onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                     setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
                     hideSoftInput();
@@ -832,11 +835,11 @@
         switch (action) {
             case MotionEvent.ACTION_MOVE:
                 float currentMoveY = ev.getY();
-                if (mBeginEditOnUpEvent
+                if (mCheckBeginEditOnUpEvent
                         || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                     int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
                     if (deltaDownY > mTouchSlop) {
-                        mBeginEditOnUpEvent = false;
+                        mCheckBeginEditOnUpEvent = false;
                         onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                     }
                 }
@@ -846,11 +849,20 @@
                 mLastMotionEventY = currentMoveY;
                 break;
             case MotionEvent.ACTION_UP:
-                if (mBeginEditOnUpEvent) {
-                    setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
-                    showInputControls(mShowInputControlsAnimimationDuration);
-                    mInputText.requestFocus();
-                    return true;
+                if (mCheckBeginEditOnUpEvent) {
+                    mCheckBeginEditOnUpEvent = false;
+                    final long deltaTapTimeMillis = ev.getEventTime() - mLastUpEventTimeMillis;
+                    if (deltaTapTimeMillis < ViewConfiguration.getDoubleTapTimeout()) {
+                        setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
+                        showInputControls(mShowInputControlsAnimimationDuration);
+                        mInputText.requestFocus();
+                        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+                        if (inputMethodManager != null) {
+                            inputMethodManager.showSoftInput(mInputText, 0);
+                        }
+                        mLastUpEventTimeMillis = ev.getEventTime();
+                        return true;
+                    }
                 }
                 VelocityTracker velocityTracker = mVelocityTracker;
                 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
@@ -869,6 +881,7 @@
                 }
                 mVelocityTracker.recycle();
                 mVelocityTracker = null;
+                mLastUpEventTimeMillis = ev.getEventTime();
                 break;
         }
         return true;
@@ -2017,4 +2030,22 @@
             postDelayed(this, mLongPressUpdateInterval);
         }
     }
+
+    /**
+     * @hide
+     */
+    public static class CustomEditText extends EditText {
+
+        public CustomEditText(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public void onEditorAction(int actionCode) {
+            super.onEditorAction(actionCode);
+            if (actionCode == EditorInfo.IME_ACTION_DONE) {
+                clearFocus();
+            }
+        }
+    }
 }
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index a1cf205..df2996c 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -57,9 +57,14 @@
     // Pause between each spell check to keep the UI smooth
     private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
 
+    private static final int USE_SPAN_RANGE = -1;
+
     private final TextView mTextView;
 
     SpellCheckerSession mSpellCheckerSession;
+    // We assume that the sentence level spell check will always provide better results than words.
+    // Although word SC has a sequential option.
+    private boolean mIsSentenceSpellCheckSupported;
     final int mCookie;
 
     // Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated
@@ -111,6 +116,7 @@
                     null /* Bundle not currently used by the textServicesManager */,
                     mCurrentLocale, this,
                     false /* means any available languages from current spell checker */);
+            mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported();
         }
 
         // Restore SpellCheckSpans in pool
@@ -272,46 +278,80 @@
                 textInfos = textInfosCopy;
             }
 
-            mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
-                    false /* TODO Set sequentialWords to true for initial spell check */);
+            if (mIsSentenceSpellCheckSupported) {
+                mSpellCheckerSession.getSentenceSuggestions(
+                        textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE);
+            } else {
+                mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
+                        false /* TODO Set sequentialWords to true for initial spell check */);
+            }
         }
     }
 
-    @Override
-    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
-        // TODO: Handle the position and length for each suggestion
-        // do nothing for now
+    private SpellCheckSpan onGetSuggestionsInternal(
+            SuggestionsInfo suggestionsInfo, int offset, int length) {
+        if (suggestionsInfo.getCookie() != mCookie) {
+            return null;
+        }
+        final Editable editable = (Editable) mTextView.getText();
+        final int sequenceNumber = suggestionsInfo.getSequence();
+        for (int k = 0; k < mLength; ++k) {
+            if (sequenceNumber == mIds[k]) {
+                final int attributes = suggestionsInfo.getSuggestionsAttributes();
+                final boolean isInDictionary =
+                        ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
+                final boolean looksLikeTypo =
+                        ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
+
+                final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[k];
+                //TODO: we need to change that rule for results from a sentence-level spell
+                // checker that will probably be in dictionary.
+                if (!isInDictionary && looksLikeTypo) {
+                    createMisspelledSuggestionSpan(
+                            editable, suggestionsInfo, spellCheckSpan, offset, length);
+                }
+                return spellCheckSpan;
+            }
+        }
+        return null;
     }
 
     @Override
     public void onGetSuggestions(SuggestionsInfo[] results) {
-        Editable editable = (Editable) mTextView.getText();
-
-        for (int i = 0; i < results.length; i++) {
-            SuggestionsInfo suggestionsInfo = results[i];
-            if (suggestionsInfo.getCookie() != mCookie) continue;
-            final int sequenceNumber = suggestionsInfo.getSequence();
-
-            for (int j = 0; j < mLength; j++) {
-                if (sequenceNumber == mIds[j]) {
-                    final int attributes = suggestionsInfo.getSuggestionsAttributes();
-                    boolean isInDictionary =
-                            ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
-                    boolean looksLikeTypo =
-                            ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
-
-                    SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
-
-                    if (!isInDictionary && looksLikeTypo) {
-                        createMisspelledSuggestionSpan(editable, suggestionsInfo, spellCheckSpan);
-                    }
-
-                    editable.removeSpan(spellCheckSpan);
-                    break;
-                }
+        final Editable editable = (Editable) mTextView.getText();
+        for (int i = 0; i < results.length; ++i) {
+            final SpellCheckSpan spellCheckSpan =
+                    onGetSuggestionsInternal(results[i], USE_SPAN_RANGE, USE_SPAN_RANGE);
+            if (spellCheckSpan != null) {
+                editable.removeSpan(spellCheckSpan);
             }
         }
+        scheduleNewSpellCheck();
+    }
 
+    @Override
+    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
+        final Editable editable = (Editable) mTextView.getText();
+
+        for (int i = 0; i < results.length; ++i) {
+            final SentenceSuggestionsInfo ssi = results[i];
+            SpellCheckSpan spellCheckSpan = null;
+            for (int j = 0; j < ssi.getSuggestionsCount(); ++j) {
+                final SuggestionsInfo suggestionsInfo = ssi.getSuggestionsInfoAt(j);
+                final int offset = ssi.getOffsetAt(j);
+                final int length = ssi.getLengthAt(j);
+                final SpellCheckSpan scs = onGetSuggestionsInternal(
+                        suggestionsInfo, offset, length);
+                if (spellCheckSpan == null && scs != null) {
+                    // the spellCheckSpan is shared by all the "SuggestionsInfo"s in the same
+                    // SentenceSuggestionsInfo
+                    spellCheckSpan = scs;
+                }
+            }
+            if (spellCheckSpan != null) {
+                editable.removeSpan(spellCheckSpan);
+            }
+        }
         scheduleNewSpellCheck();
     }
 
@@ -338,10 +378,11 @@
     }
 
     private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo,
-            SpellCheckSpan spellCheckSpan) {
-        final int start = editable.getSpanStart(spellCheckSpan);
-        final int end = editable.getSpanEnd(spellCheckSpan);
-        if (start < 0 || end <= start) return; // span was removed in the meantime
+            SpellCheckSpan spellCheckSpan, int offset, int length) {
+        final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan);
+        final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan);
+        if (spellCheckSpanStart < 0 || spellCheckSpanEnd <= spellCheckSpanStart)
+            return; // span was removed in the meantime
 
         final int suggestionsCount = suggestionsInfo.getSuggestionsCount();
         if (suggestionsCount <= 0) {
@@ -349,6 +390,16 @@
             return;
         }
 
+        final int start;
+        final int end;
+        if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) {
+            start = spellCheckSpanStart + offset;
+            end = start + length;
+        } else {
+            start = spellCheckSpanStart;
+            end = spellCheckSpanEnd;
+        }
+
         String[] suggestions = new String[suggestionsCount];
         for (int i = 0; i < suggestionsCount; i++) {
             suggestions[i] = suggestionsInfo.getSuggestionAt(i);
@@ -419,67 +470,97 @@
             int wordCount = 0;
             boolean scheduleOtherSpellCheck = false;
 
-            while (wordStart <= end) {
-                if (wordEnd >= start && wordEnd > wordStart) {
-                    if (wordCount >= MAX_NUMBER_OF_WORDS) {
-                        scheduleOtherSpellCheck = true;
+            if (mIsSentenceSpellCheckSupported) {
+                int regionEnd;
+                if (wordIteratorWindowEnd < end) {
+                    // Several batches needed on that region. Cut after last previous word
+                    regionEnd = mWordIterator.preceding(wordIteratorWindowEnd);
+                    scheduleOtherSpellCheck = true;
+                } else {
+                    regionEnd = mWordIterator.preceding(end);
+                }
+                boolean correct = regionEnd != BreakIterator.DONE;
+                if (correct) {
+                    regionEnd = mWordIterator.getEnd(regionEnd);
+                    correct = regionEnd != BreakIterator.DONE;
+                }
+                if (!correct) {
+                    editable.removeSpan(mRange);
+                    return;
+                }
+                wordStart = regionEnd;
+                // TODO: Find the start position of the sentence.
+                // Set span with the context
+                final int spellCheckStart =  Math.min(
+                        start, Math.max(wordStart, regionEnd - WORD_ITERATOR_INTERVAL));
+                if (regionEnd <= spellCheckStart) {
+                    return;
+                }
+                addSpellCheckSpan(editable, spellCheckStart, regionEnd);
+            } else {
+                while (wordStart <= end) {
+                    if (wordEnd >= start && wordEnd > wordStart) {
+                        if (wordCount >= MAX_NUMBER_OF_WORDS) {
+                            scheduleOtherSpellCheck = true;
+                            break;
+                        }
+                        // A new word has been created across the interval boundaries with this
+                        // edit. The previous spans (that ended on start / started on end) are
+                        // not valid anymore and must be removed.
+                        if (wordStart < start && wordEnd > start) {
+                            removeSpansAt(editable, start, spellCheckSpans);
+                            removeSpansAt(editable, start, suggestionSpans);
+                        }
+
+                        if (wordStart < end && wordEnd > end) {
+                            removeSpansAt(editable, end, spellCheckSpans);
+                            removeSpansAt(editable, end, suggestionSpans);
+                        }
+
+                        // Do not create new boundary spans if they already exist
+                        boolean createSpellCheckSpan = true;
+                        if (wordEnd == start) {
+                            for (int i = 0; i < spellCheckSpans.length; i++) {
+                                final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
+                                if (spanEnd == start) {
+                                    createSpellCheckSpan = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (wordStart == end) {
+                            for (int i = 0; i < spellCheckSpans.length; i++) {
+                                final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
+                                if (spanStart == end) {
+                                    createSpellCheckSpan = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (createSpellCheckSpan) {
+                            addSpellCheckSpan(editable, wordStart, wordEnd);
+                        }
+                        wordCount++;
+                    }
+
+                    // iterate word by word
+                    int originalWordEnd = wordEnd;
+                    wordEnd = mWordIterator.following(wordEnd);
+                    if ((wordIteratorWindowEnd < end) &&
+                            (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
+                        wordIteratorWindowEnd =
+                                Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
+                        mWordIterator.setCharSequence(
+                                editable, originalWordEnd, wordIteratorWindowEnd);
+                        wordEnd = mWordIterator.following(originalWordEnd);
+                    }
+                    if (wordEnd == BreakIterator.DONE) break;
+                    wordStart = mWordIterator.getBeginning(wordEnd);
+                    if (wordStart == BreakIterator.DONE) {
                         break;
                     }
-
-                    // A new word has been created across the interval boundaries with this edit.
-                    // The previous spans (that ended on start / started on end) are not valid
-                    // anymore and must be removed.
-                    if (wordStart < start && wordEnd > start) {
-                        removeSpansAt(editable, start, spellCheckSpans);
-                        removeSpansAt(editable, start, suggestionSpans);
-                    }
-
-                    if (wordStart < end && wordEnd > end) {
-                        removeSpansAt(editable, end, spellCheckSpans);
-                        removeSpansAt(editable, end, suggestionSpans);
-                    }
-
-                    // Do not create new boundary spans if they already exist
-                    boolean createSpellCheckSpan = true;
-                    if (wordEnd == start) {
-                        for (int i = 0; i < spellCheckSpans.length; i++) {
-                            final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
-                            if (spanEnd == start) {
-                                createSpellCheckSpan = false;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (wordStart == end) {
-                        for (int i = 0; i < spellCheckSpans.length; i++) {
-                            final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
-                            if (spanStart == end) {
-                                createSpellCheckSpan = false;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (createSpellCheckSpan) {
-                        addSpellCheckSpan(editable, wordStart, wordEnd);
-                    }
-                    wordCount++;
-                }
-
-                // iterate word by word
-                int originalWordEnd = wordEnd;
-                wordEnd = mWordIterator.following(wordEnd);
-                if ((wordIteratorWindowEnd < end) &&
-                        (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
-                    wordIteratorWindowEnd = Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
-                    mWordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd);
-                    wordEnd = mWordIterator.following(originalWordEnd);
-                }
-                if (wordEnd == BreakIterator.DONE) break;
-                wordStart = mWordIterator.getBeginning(wordEnd);
-                if (wordStart == BreakIterator.DONE) {
-                    break;
                 }
             }
 
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 2cacbdca..89c506f 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -40,13 +41,13 @@
  *
  * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
  * tutorial</a>.</p>
- * 
+ *
  * @attr ref android.R.styleable#Spinner_prompt
  */
 @Widget
 public class Spinner extends AbsSpinner implements OnClickListener {
     private static final String TAG = "Spinner";
-    
+
     // Only measure this many items to get a decent max width.
     private static final int MAX_ITEMS_MEASURED = 15;
 
@@ -54,7 +55,7 @@
      * Use a dialog window for selecting spinner options.
      */
     public static final int MODE_DIALOG = 0;
-    
+
     /**
      * Use a dropdown anchored to the Spinner for selecting spinner options.
      */
@@ -759,13 +760,30 @@
 
         @Override
         public void show() {
+            final Drawable background = getBackground();
+            int bgOffset = 0;
+            if (background != null) {
+                background.getPadding(mTempRect);
+                bgOffset = -mTempRect.left;
+            } else {
+                mTempRect.left = mTempRect.right = 0;
+            }
+
             final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
             if (mDropDownWidth == WRAP_CONTENT) {
                 final int spinnerWidth = Spinner.this.getWidth();
                 final int spinnerPaddingRight = Spinner.this.getPaddingRight();
+
+                int contentWidth =  measureContentWidth(
+                        (SpinnerAdapter) mAdapter, getBackground());
+                final int contentWidthLimit = mContext.getResources()
+                        .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
+                if (contentWidth > contentWidthLimit) {
+                    contentWidth = contentWidthLimit;
+                }
+
                 setContentWidth(Math.max(
-                        measureContentWidth((SpinnerAdapter) mAdapter, getBackground()),
-                        spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
+                       contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
             } else if (mDropDownWidth == MATCH_PARENT) {
                 final int spinnerWidth = Spinner.this.getWidth();
                 final int spinnerPaddingRight = Spinner.this.getPaddingRight();
@@ -773,12 +791,6 @@
             } else {
                 setContentWidth(mDropDownWidth);
             }
-            final Drawable background = getBackground();
-            int bgOffset = 0;
-            if (background != null) {
-                background.getPadding(mTempRect);
-                bgOffset = -mTempRect.left;
-            }
             setHorizontalOffset(bgOffset + spinnerPaddingLeft);
             setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
             super.show();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d6dd15e..56a0d1e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -243,10 +243,6 @@
     private static final int SIGNED = 2;
     private static final int DECIMAL = 4;
 
-    private static enum TEXT_ALIGN {
-        INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END;
-    }
-
     /**
      * Draw marquee text with fading edges as usual
      */
@@ -329,9 +325,6 @@
     // The alignment to pass to Layout, or null if not resolved.
     private Layout.Alignment mLayoutAlignment;
 
-    // The default value for mTextAlign.
-    private TEXT_ALIGN mTextAlign = TEXT_ALIGN.INHERIT;
-
     private boolean mResolvedDrawables;
 
     /**
@@ -406,9 +399,15 @@
 
     private InputFilter[] mFilters = NO_FILTERS;
 
+    // It is possible to have a selection even when mEditor is null (programmatically set, like when
+    // a link is pressed). These highlight-related fields do not go in mEditor.
+    private int mHighlightColor = 0x6633B5E5;
+    private Path mHighlightPath;
+    private final Paint mHighlightPaint;
+    private boolean mHighlightPathBogus = true;
+
     // Although these fields are specific to editable text, they are not added to Editor because
     // they are defined by the TextView's style and are theme-dependent.
-    private int mHighlightColor = 0x6633B5E5;
     private int mCursorDrawableRes;
     // These four fields, could be moved to Editor, since we know their default values and we
     // could condition the creation of the Editor to a non standard value. This is however
@@ -477,6 +476,9 @@
         mTextPaint.density = res.getDisplayMetrics().density;
         mTextPaint.setCompatibilityScaling(compat.applicationScale);
 
+        mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
+
         mMovement = getDefaultMovementMethod();
 
         mTransformation = null;
@@ -4064,7 +4066,7 @@
     }
 
     private void invalidateCursorPath() {
-        if (getEditor().mHighlightPathBogus) {
+        if (mHighlightPathBogus) {
             invalidateCursor();
         } else {
             final int horizontalPadding = getCompoundPaddingLeft();
@@ -4088,7 +4090,8 @@
 
                     thick /= 2.0f;
 
-                    getEditor().mHighlightPath.computeBounds(TEMP_RECTF, false);
+                    // mHighlightPath is guaranteed to be non null at that point.
+                    mHighlightPath.computeBounds(TEMP_RECTF, false);
 
                     invalidate((int) FloatMath.floor(horizontalPadding + TEMP_RECTF.left - thick),
                             (int) FloatMath.floor(verticalPadding + TEMP_RECTF.top - thick),
@@ -4150,7 +4153,8 @@
 
                 int bottom = mLayout.getLineBottom(lineEnd);
 
-                if (invalidateCursor) {
+                // mEditor can be null in case selection is set programmatically.
+                if (invalidateCursor && mEditor != null) {
                     for (int i = 0; i < getEditor().mCursorCount; i++) {
                         Rect bounds = getEditor().mCursorDrawable[i].getBounds();
                         top = Math.min(top, bounds.top);
@@ -4515,6 +4519,55 @@
         return drawableState;
     }
 
+    private Path getUpdatedHighlightPath() {
+        Path highlight = null;
+        Paint highlightPaint = mHighlightPaint;
+
+        final int selStart = getSelectionStart();
+        final int selEnd = getSelectionEnd();
+        if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) {
+            if (selStart == selEnd) {
+                if (mEditor != null && isCursorVisible() &&
+                        (SystemClock.uptimeMillis() - getEditor().mShowCursor) % (2 * BLINK) < BLINK) {
+                    if (mHighlightPathBogus) {
+                        if (mHighlightPath == null) mHighlightPath = new Path();
+                        mHighlightPath.reset();
+                        mLayout.getCursorPath(selStart, mHighlightPath, mText);
+                        getEditor().updateCursorsPositions();
+                        mHighlightPathBogus = false;
+                    }
+
+                    // XXX should pass to skin instead of drawing directly
+                    highlightPaint.setColor(mCurTextColor);
+                    if (mCurrentAlpha != 255) {
+                        highlightPaint.setAlpha(
+                                (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255);
+                    }
+                    highlightPaint.setStyle(Paint.Style.STROKE);
+                    highlight = mHighlightPath;
+                }
+            } else {
+                if (mHighlightPathBogus) {
+                    if (mHighlightPath == null) mHighlightPath = new Path();
+                    mHighlightPath.reset();
+                    mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
+                    mHighlightPathBogus = false;
+                }
+
+                // XXX should pass to skin instead of drawing directly
+                highlightPaint.setColor(mHighlightColor);
+                if (mCurrentAlpha != 255) {
+                    highlightPaint.setAlpha(
+                            (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
+                }
+                highlightPaint.setStyle(Paint.Style.FILL);
+
+                highlight = mHighlightPath;
+            }
+        }
+        return highlight;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
@@ -4666,68 +4719,21 @@
 
         final int cursorOffsetVertical = voffsetCursor - voffsetText;
 
+        Path highlight = getUpdatedHighlightPath();
         if (mEditor != null) {
-            getEditor().onDraw(canvas, layout, cursorOffsetVertical);
+            getEditor().onDraw(canvas, layout, highlight, cursorOffsetVertical);
         } else {
-            layout.draw(canvas, null, null, cursorOffsetVertical);
+            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
 
             if (mMarquee != null && mMarquee.shouldDrawGhost()) {
                 canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
-                layout.draw(canvas, null, null, cursorOffsetVertical);
+                layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
             }
         }
 
         canvas.restore();
     }
 
-    private void updateCursorsPositions() {
-        if (mCursorDrawableRes == 0) {
-            getEditor().mCursorCount = 0;
-            return; 
-        }
-
-        final int offset = getSelectionStart();
-        final int line = mLayout.getLineForOffset(offset);
-        final int top = mLayout.getLineTop(line);
-        final int bottom = mLayout.getLineTop(line + 1);
-
-        getEditor().mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
-
-        int middle = bottom;
-        if (getEditor().mCursorCount == 2) {
-            // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
-            middle = (top + bottom) >> 1;
-        }
-
-        updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
-
-        if (getEditor().mCursorCount == 2) {
-            updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
-        }
-    }
-
-    private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
-        if (getEditor().mCursorDrawable[cursorIndex] == null)
-            getEditor().mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
-
-        if (mTempRect == null) mTempRect = new Rect();
-        getEditor().mCursorDrawable[cursorIndex].getPadding(mTempRect);
-        final int width = getEditor().mCursorDrawable[cursorIndex].getIntrinsicWidth();
-        horizontal = Math.max(0.5f, horizontal - 0.5f);
-        final int left = (int) (horizontal) - mTempRect.left;
-        getEditor().mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
-                bottom + mTempRect.bottom);
-    }
-
-    private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
-        final boolean translate = cursorOffsetVertical != 0;
-        if (translate) canvas.translate(0, cursorOffsetVertical);
-        for (int i = 0; i < getEditor().mCursorCount; i++) {
-            getEditor().mCursorDrawable[i].draw(canvas);
-        }
-        if (translate) canvas.translate(0, -cursorOffsetVertical);
-    }
-
     @Override
     public void getFocusedRect(Rect r) {
         if (mLayout == null) {
@@ -4759,21 +4765,16 @@
             } else {
                 // Selection extends across multiple lines -- make the focused
                 // rect cover the entire width.
-                if (mEditor != null) {
-                    if (getEditor().mHighlightPath == null) getEditor().mHighlightPath = new Path();
-                    if (getEditor().mHighlightPathBogus) {
-                        getEditor().mHighlightPath.reset();
-                        mLayout.getSelectionPath(selStart, selEnd, getEditor().mHighlightPath);
-                        getEditor().mHighlightPathBogus = false;
-                    }
-                    synchronized (TEMP_RECTF) {
-                        getEditor().mHighlightPath.computeBounds(TEMP_RECTF, true);
-                        r.left = (int)TEMP_RECTF.left-1;
-                        r.right = (int)TEMP_RECTF.right+1;
-                    }
-                } else {
-                    r.left = 0;
-                    r.right = getMeasuredWidth();
+                if (mHighlightPathBogus) {
+                    if (mHighlightPath == null) mHighlightPath = new Path();
+                    mHighlightPath.reset();
+                    mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
+                    mHighlightPathBogus = false;
+                }
+                synchronized (TEMP_RECTF) {
+                    mHighlightPath.computeBounds(TEMP_RECTF, true);
+                    r.left = (int)TEMP_RECTF.left-1;
+                    r.right = (int)TEMP_RECTF.right+1;
                 }
             }
         }
@@ -5584,7 +5585,7 @@
         }
 
         if (curs >= 0) {
-            getEditor().mHighlightPathBogus = true;
+            mHighlightPathBogus = true;
             makeBlink();
             bringPointIntoView(curs);
         }
@@ -5659,69 +5660,28 @@
                       physicalWidth, false);
     }
 
-    @Override
-    protected void resetResolvedLayoutDirection() {
-        super.resetResolvedLayoutDirection();
-
-        if (mLayoutAlignment != null &&
-                (mTextAlign == TEXT_ALIGN.VIEW_START ||
-                mTextAlign == TEXT_ALIGN.VIEW_END)) {
-            mLayoutAlignment = null;
-        }
-    }
-
     private Layout.Alignment getLayoutAlignment() {
         if (mLayoutAlignment == null) {
-            Layout.Alignment alignment;
-            TEXT_ALIGN textAlign = mTextAlign;
-            switch (textAlign) {
-                case INHERIT:
-                    // fall through to gravity temporarily
-                    // intention is to inherit value through view hierarchy.
-                case GRAVITY:
-                    switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
-                        case Gravity.START:
-                            alignment = Layout.Alignment.ALIGN_NORMAL;
-                            break;
-                        case Gravity.END:
-                            alignment = Layout.Alignment.ALIGN_OPPOSITE;
-                            break;
-                        case Gravity.LEFT:
-                            alignment = Layout.Alignment.ALIGN_LEFT;
-                            break;
-                        case Gravity.RIGHT:
-                            alignment = Layout.Alignment.ALIGN_RIGHT;
-                            break;
-                        case Gravity.CENTER_HORIZONTAL:
-                            alignment = Layout.Alignment.ALIGN_CENTER;
-                            break;
-                        default:
-                            alignment = Layout.Alignment.ALIGN_NORMAL;
-                            break;
-                    }
+            switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+                case Gravity.START:
+                    mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
                     break;
-                case TEXT_START:
-                    alignment = Layout.Alignment.ALIGN_NORMAL;
+                case Gravity.END:
+                    mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
                     break;
-                case TEXT_END:
-                    alignment = Layout.Alignment.ALIGN_OPPOSITE;
+                case Gravity.LEFT:
+                    mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
                     break;
-                case CENTER:
-                    alignment = Layout.Alignment.ALIGN_CENTER;
+                case Gravity.RIGHT:
+                    mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
                     break;
-                case VIEW_START:
-                    alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
-                            Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
-                    break;
-                case VIEW_END:
-                    alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
-                            Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+                case Gravity.CENTER_HORIZONTAL:
+                    mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
                     break;
                 default:
-                    alignment = Layout.Alignment.ALIGN_NORMAL;
+                    mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
                     break;
             }
-            mLayoutAlignment = alignment;
         }
         return mLayoutAlignment;
     }
@@ -5741,7 +5701,7 @@
         mOldMaximum = mMaximum;
         mOldMaxMode = mMaxMode;
 
-        if (mEditor != null) getEditor().mHighlightPathBogus = true;
+        mHighlightPathBogus = true;
 
         if (wantWidth < 0) {
             wantWidth = 0;
@@ -6036,7 +5996,6 @@
                 if (des < 0) {
                     des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint));
                 }
-
                 width = des;
             } else {
                 width = boring.width;
@@ -6057,7 +6016,7 @@
                 }
 
                 if (hintDes < 0) {
-                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring);
+                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
                     if (hintBoring != null) {
                         mHintBoring = hintBoring;
                     }
@@ -6065,10 +6024,8 @@
 
                 if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
                     if (hintDes < 0) {
-                        hintDes = (int) FloatMath.ceil(
-                                Layout.getDesiredWidth(mHint, mTextPaint));
+                        hintDes = (int) FloatMath.ceil(Layout.getDesiredWidth(mHint, mTextPaint));
                     }
-
                     hintWidth = hintDes;
                 } else {
                     hintWidth = hintBoring.width;
@@ -6332,20 +6289,25 @@
         if (changed && mEditor != null) getEditor().mTextDisplayListIsValid = false;
     }
 
+    private boolean isShowingHint() {
+        return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint);
+    }
+
     /**
      * Returns true if anything changed.
      */
     private boolean bringTextIntoView() {
+        Layout layout = isShowingHint() ? mHintLayout : mLayout;
         int line = 0;
         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
-            line = mLayout.getLineCount() - 1;
+            line = layout.getLineCount() - 1;
         }
 
-        Layout.Alignment a = mLayout.getParagraphAlignment(line);
-        int dir = mLayout.getParagraphDirection(line);
+        Layout.Alignment a = layout.getParagraphAlignment(line);
+        int dir = layout.getParagraphDirection(line);
         int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
         int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
-        int ht = mLayout.getHeight();
+        int ht = layout.getHeight();
 
         int scrollx, scrolly;
 
@@ -6364,8 +6326,8 @@
              * keep leading edge in view.
              */
 
-            int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
-            int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+            int left = (int) FloatMath.floor(layout.getLineLeft(line));
+            int right = (int) FloatMath.ceil(layout.getLineRight(line));
 
             if (right - left < hspace) {
                 scrollx = (right + left) / 2 - hspace / 2;
@@ -6377,10 +6339,10 @@
                 }
             }
         } else if (a == Layout.Alignment.ALIGN_RIGHT) {
-            int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+            int right = (int) FloatMath.ceil(layout.getLineRight(line));
             scrollx = right - hspace;
         } else { // a == Layout.Alignment.ALIGN_LEFT (will also be the default)
-            scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
+            scrollx = (int) FloatMath.floor(layout.getLineLeft(line));
         }
 
         if (ht < vspace) {
@@ -6408,22 +6370,24 @@
     public boolean bringPointIntoView(int offset) {
         boolean changed = false;
 
-        if (mLayout == null) return changed;
+        Layout layout = isShowingHint() ? mHintLayout: mLayout;
 
-        int line = mLayout.getLineForOffset(offset);
+        if (layout == null) return changed;
+
+        int line = layout.getLineForOffset(offset);
 
         // FIXME: Is it okay to truncate this, or should we round?
-        final int x = (int)mLayout.getPrimaryHorizontal(offset);
-        final int top = mLayout.getLineTop(line);
-        final int bottom = mLayout.getLineTop(line + 1);
+        final int x = (int)layout.getPrimaryHorizontal(offset);
+        final int top = layout.getLineTop(line);
+        final int bottom = layout.getLineTop(line + 1);
 
-        int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
-        int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
-        int ht = mLayout.getHeight();
+        int left = (int) FloatMath.floor(layout.getLineLeft(line));
+        int right = (int) FloatMath.ceil(layout.getLineRight(line));
+        int ht = layout.getHeight();
 
         int grav;
 
-        switch (mLayout.getParagraphAlignment(line)) {
+        switch (layout.getParagraphAlignment(line)) {
             case ALIGN_LEFT:
                 grav = 1;
                 break;
@@ -6431,10 +6395,10 @@
                 grav = -1;
                 break;
             case ALIGN_NORMAL:
-                grav = mLayout.getParagraphDirection(line);
+                grav = layout.getParagraphDirection(line);
                 break;
             case ALIGN_OPPOSITE:
-                grav = -mLayout.getParagraphDirection(line);
+                grav = -layout.getParagraphDirection(line);
                 break;
             case ALIGN_CENTER:
             default:
@@ -6982,10 +6946,7 @@
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
-        // mEditor may be null if selection is created programatically.
-        createEditorIfNeeded("onSelectionChanged");
-        // Invalidate even when selection range is empty, to remove previous highlight
-        getEditor().mTextDisplayListIsValid = false;
+        if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
     }
 
     /**
@@ -7141,10 +7102,8 @@
         }
 
         if (selChanged) {
-            if (mEditor != null) {
-                getEditor().mHighlightPathBogus = true;
-                if (!isFocused()) getEditor().mSelectionMoved = true;
-            }
+            mHighlightPathBogus = true;
+            if (mEditor != null && !isFocused()) getEditor().mSelectionMoved = true;
 
             if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) {
                 if (newSelStart < 0) {
@@ -7161,7 +7120,7 @@
                 what instanceof CharacterStyle) {
             if (ims == null || ims.mBatchEditNesting == 0) {
                 invalidate();
-                if (mEditor != null) getEditor().mHighlightPathBogus = true;
+                mHighlightPathBogus = true;
                 checkForResize();
             } else {
                 ims.mContentChanged = true;
@@ -7170,7 +7129,7 @@
         }
 
         if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
-            if (mEditor != null) getEditor().mHighlightPathBogus = true;
+            mHighlightPathBogus = true;
             if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
                 ims.mSelectionModeChanged = true;
             }
@@ -8186,24 +8145,26 @@
     @Override
     public boolean performLongClick() {
         boolean handled = false;
-        boolean vibrate = true;
 
         if (super.performLongClick()) {
             handled = true;
         }
 
+        if (mEditor == null) {
+            return handled;
+        }
+
         // Long press in empty space moves cursor and shows the Paste affordance if available.
-        if (!handled && mEditor != null && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) &&
+        if (!handled && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) &&
                 getEditor().mInsertionControllerEnabled) {
             final int offset = getOffsetForPosition(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
             getInsertionController().showWithActionPopup();
             handled = true;
-            vibrate = false;
         }
 
-        if (!handled && mEditor != null && getEditor().mSelectionActionMode != null) {
+        if (!handled && getEditor().mSelectionActionMode != null) {
             if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = getSelectionStart();
@@ -8223,14 +8184,11 @@
 
         // Start a new selection
         if (!handled) {
-            vibrate = handled = startSelectionActionMode();
+            handled = startSelectionActionMode();
         }
 
-        if (vibrate) {
+        if (handled) {
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        }
-
-        if (handled && mEditor != null) {
             getEditor().mDiscardNextActionUp = true;
         }
 
@@ -8322,8 +8280,11 @@
     @Override
     protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
         super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
-        if (mEditor != null && getEditor().mPositionListener != null) {
-            getEditor().mPositionListener.onScrollChanged();
+        if (mEditor != null) {
+            if (getEditor().mPositionListener != null) {
+                getEditor().mPositionListener.onScrollChanged();
+            }
+            getEditor().mTextDisplayListIsValid = false;
         }
     }
 
@@ -8619,7 +8580,7 @@
     public boolean onDragEvent(DragEvent event) {
         switch (event.getAction()) {
             case DragEvent.ACTION_DRAG_STARTED:
-                return hasInsertionController();
+                return mEditor != null && hasInsertionController();
 
             case DragEvent.ACTION_DRAG_ENTERED:
                 TextView.this.requestFocus();
@@ -8754,8 +8715,10 @@
     }
 
     @Override
-    public void onResolveTextDirection() {
+    public void onResolvedTextDirectionChanged() {
         if (hasPasswordTransformationMethod()) {
+            // TODO: take care of the content direction to show the password text and dots justified
+            // to the left or to the right
             mTextDir = TextDirectionHeuristics.LOCALE;
             return;
         }
@@ -10730,9 +10693,12 @@
                 return;
             }
 
-            if (offset != mPreviousOffset || parentScrolled) {
-                updateSelection(offset);
-                addPositionToTouchUpFilter(offset);
+            boolean offsetChanged = offset != mPreviousOffset;
+            if (offsetChanged || parentScrolled) {
+                if (offsetChanged) {
+                    updateSelection(offset);
+                    addPositionToTouchUpFilter(offset);
+                }
                 final int line = mLayout.getLineForOffset(offset);
 
                 mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX);
@@ -11330,12 +11296,6 @@
     }
 
     private class Editor {
-        Editor() {
-            mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            final CompatibilityInfo compat = TextView.this.getResources().getCompatibilityInfo();
-            mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
-        }
-
         // Cursor Controllers.
         InsertionPointCursorController mInsertionPointCursorController;
         SelectionModifierCursorController mSelectionModifierCursorController;
@@ -11349,10 +11309,6 @@
         InputContentType mInputContentType;
         InputMethodState mInputMethodState;
 
-        Path mHighlightPath;
-        boolean mHighlightPathBogus = true;
-        final Paint mHighlightPaint;
-
         DisplayList mTextDisplayList;
         boolean mTextDisplayListIsValid;
 
@@ -11391,7 +11347,7 @@
         Runnable mShowSuggestionRunnable;
 
         final Drawable[] mCursorDrawable = new Drawable[2];
-        int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split)
+        int mCursorCount; // Current number of used mCursorDrawable: 0 (resource=0), 1 or 2 (split)
 
         Drawable mSelectHandleLeft;
         Drawable mSelectHandleRight;
@@ -11649,65 +11605,9 @@
             }
         }
 
-        void onDraw(Canvas canvas, Layout layout, int cursorOffsetVertical) {
-            Path highlight = null;
-            Paint highlightPaint = null;
-
-            int selStart = -1, selEnd = -1;
-            boolean drawCursor = false;
-
-            highlightPaint = mHighlightPaint;
-            //  If there is no movement method, then there can be no selection.
-            //  Check that first and attempt to skip everything having to do with
-            //  the cursor.
-            //  XXX This is not strictly true -- a program could set the
-            //  selection manually if it really wanted to.
-            if (mMovement != null && (isFocused() || isPressed())) {
-                selStart = getSelectionStart();
-                selEnd = getSelectionEnd();
-
-                if (selStart >= 0) {
-                    if (mHighlightPath == null) mHighlightPath = new Path();
-
-                    if (selStart == selEnd) {
-                        if (isCursorVisible() &&
-                                (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) {
-                            if (mHighlightPathBogus) {
-                                mHighlightPath.reset();
-                                mLayout.getCursorPath(selStart, mHighlightPath, mText);
-                                updateCursorsPositions();
-                                mHighlightPathBogus = false;
-                            }
-
-                            // XXX should pass to skin instead of drawing directly
-                            highlightPaint.setColor(mCurTextColor);
-                            if (mCurrentAlpha != 255) {
-                                highlightPaint.setAlpha(
-                                        (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255);
-                            }
-                            highlightPaint.setStyle(Paint.Style.STROKE);
-                            highlight = mHighlightPath;
-                            drawCursor = mCursorCount > 0;
-                        }
-                    } else if (textCanBeSelected()) {
-                        if (mHighlightPathBogus) {
-                            mHighlightPath.reset();
-                            mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
-                            mHighlightPathBogus = false;
-                        }
-
-                        // XXX should pass to skin instead of drawing directly
-                        highlightPaint.setColor(mHighlightColor);
-                        if (mCurrentAlpha != 255) {
-                            highlightPaint.setAlpha(
-                                    (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
-                        }
-                        highlightPaint.setStyle(Paint.Style.FILL);
-
-                        highlight = mHighlightPath;
-                    }
-                }
-            }
+        void onDraw(Canvas canvas, Layout layout, Path highlight, int cursorOffsetVertical) {
+            final int selectionStart = getSelectionStart();
+            final int selectionEnd = getSelectionEnd();
 
             final InputMethodState ims = mInputMethodState;
             if (ims != null && ims.mBatchEditNesting == 0) {
@@ -11729,7 +11629,8 @@
                                 candStart = EditableInputConnection.getComposingSpanStart(sp);
                                 candEnd = EditableInputConnection.getComposingSpanEnd(sp);
                             }
-                            imm.updateSelection(TextView.this, selStart, selEnd, candStart, candEnd);
+                            imm.updateSelection(TextView.this,
+                                    selectionStart, selectionEnd, candStart, candEnd);
                         }
                     }
 
@@ -11758,7 +11659,7 @@
                 mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
             }
 
-            if (drawCursor) {
+            if (highlight != null && selectionStart == selectionEnd && mCursorCount > 0) {
                 drawCursor(canvas, cursorOffsetVertical);
                 // Rely on the drawable entirely, do not draw the cursor line.
                 // Has to be done after the IMM related code above which relies on the highlight.
@@ -11781,7 +11682,7 @@
                         // The dirty rect should always be null for a display list
                         hardwareCanvas.onPreDraw(null);
                         hardwareCanvas.translate(-mScrollX, -mScrollY);
-                        layout.draw(hardwareCanvas, highlight, highlightPaint, cursorOffsetVertical);
+                        layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical);
                         hardwareCanvas.translate(mScrollX, mScrollY);
                     } finally {
                         hardwareCanvas.onPostDraw();
@@ -11794,13 +11695,61 @@
                         DisplayList.FLAG_CLIP_CHILDREN);
                 canvas.translate(-mScrollX, -mScrollY);
             } else {
-                layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+                layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
             }
 
             if (mMarquee != null && mMarquee.shouldDrawGhost()) {
                 canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
-                layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+                layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
             }
         }
+
+        private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
+            final boolean translate = cursorOffsetVertical != 0;
+            if (translate) canvas.translate(0, cursorOffsetVertical);
+            for (int i = 0; i < getEditor().mCursorCount; i++) {
+                mCursorDrawable[i].draw(canvas);
+            }
+            if (translate) canvas.translate(0, -cursorOffsetVertical);
+        }
+
+        private void updateCursorsPositions() {
+            if (mCursorDrawableRes == 0) {
+                mCursorCount = 0;
+                return;
+            }
+
+            final int offset = getSelectionStart();
+            final int line = mLayout.getLineForOffset(offset);
+            final int top = mLayout.getLineTop(line);
+            final int bottom = mLayout.getLineTop(line + 1);
+
+            mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
+
+            int middle = bottom;
+            if (mCursorCount == 2) {
+                // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
+                middle = (top + bottom) >> 1;
+            }
+
+            updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
+
+            if (mCursorCount == 2) {
+                updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
+            }
+        }
+
+        private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
+            if (mCursorDrawable[cursorIndex] == null)
+                mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
+
+            if (mTempRect == null) mTempRect = new Rect();
+            mCursorDrawable[cursorIndex].getPadding(mTempRect);
+            final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
+            horizontal = Math.max(0.5f, horizontal - 0.5f);
+            final int left = (int) (horizontal) - mTempRect.left;
+            mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+                    bottom + mTempRect.bottom);
+        }
     }
 }
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index ef1d7d0..7eff1aa 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -136,7 +136,7 @@
         TypedArray attributesArray = context.obtainStyledAttributes(
                 attrs, R.styleable.TimePicker, defStyle, 0);
         int layoutResourceId = attributesArray.getResourceId(
-                R.styleable.TimePicker_layout, R.layout.time_picker);
+                R.styleable.TimePicker_internalLayout, R.layout.time_picker);
         attributesArray.recycle();
 
         LayoutInflater inflater = (LayoutInflater) context.getSystemService(
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3fba1be..0563846 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,8 @@
 package com.android.internal.app;
 
 import com.android.internal.R;
+import com.android.internal.content.PackageMonitor;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -58,6 +60,12 @@
     private TextView mClearDefaultHint;
     private PackageManager mPm;
 
+    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override public void onSomePackagesChanged() {
+            mAdapter.handlePackagesChanged();
+        }
+    };
+
     private Intent makeMyIntent() {
         Intent intent = new Intent(getIntent());
         // The resolver activity is set to be hidden from recent tasks.
@@ -88,6 +96,8 @@
         ap.mTitle = title;
         ap.mOnClickListener = this;
 
+        mPackageMonitor.register(this, false);
+
         if (alwaysUseOption) {
             LayoutInflater inflater = (LayoutInflater) getSystemService(
                     Context.LAYOUT_INFLATER_SERVICE);
@@ -114,6 +124,19 @@
         setupAlert();
     }
 
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        mPackageMonitor.register(this, false);
+        mAdapter.handlePackagesChanged();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mPackageMonitor.unregister();
+    }
+
     public void onClick(DialogInterface dialog, int which) {
         ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
         Intent intent = mAdapter.intentForPosition(which);
@@ -225,29 +248,48 @@
     }
 
     private final class ResolveListAdapter extends BaseAdapter {
+        private final Intent[] mInitialIntents;
+        private final List<ResolveInfo> mBaseResolveList;
         private final Intent mIntent;
         private final LayoutInflater mInflater;
 
+        private List<ResolveInfo> mCurrentResolveList;
         private List<DisplayResolveInfo> mList;
 
         public ResolveListAdapter(Context context, Intent intent,
                 Intent[] initialIntents, List<ResolveInfo> rList) {
             mIntent = new Intent(intent);
             mIntent.setComponent(null);
+            mInitialIntents = initialIntents;
+            mBaseResolveList = rList;
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            rebuildList();
+        }
 
-            if (rList == null) {
-                rList = mPm.queryIntentActivities(
-                        intent, PackageManager.MATCH_DEFAULT_ONLY
+        public void handlePackagesChanged() {
+            rebuildList();
+            notifyDataSetChanged();
+            if (mList.size() <= 0) {
+                // We no longer have any items...  just finish the activity.
+                finish();
+            }
+        }
+
+        private void rebuildList() {
+            if (mBaseResolveList != null) {
+                mCurrentResolveList = mBaseResolveList;
+            } else {
+                mCurrentResolveList = mPm.queryIntentActivities(
+                        mIntent, PackageManager.MATCH_DEFAULT_ONLY
                         | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0));
             }
             int N;
-            if ((rList != null) && ((N = rList.size()) > 0)) {
+            if ((mCurrentResolveList != null) && ((N = mCurrentResolveList.size()) > 0)) {
                 // Only display the first matches that are either of equal
                 // priority or have asked to be default options.
-                ResolveInfo r0 = rList.get(0);
+                ResolveInfo r0 = mCurrentResolveList.get(0);
                 for (int i=1; i<N; i++) {
-                    ResolveInfo ri = rList.get(i);
+                    ResolveInfo ri = mCurrentResolveList.get(i);
                     if (false) Log.v(
                         "ResolveListActivity",
                         r0.activityInfo.name + "=" +
@@ -257,7 +299,7 @@
                    if (r0.priority != ri.priority ||
                         r0.isDefault != ri.isDefault) {
                         while (i < N) {
-                            rList.remove(i);
+                            mCurrentResolveList.remove(i);
                             N--;
                         }
                     }
@@ -265,15 +307,15 @@
                 if (N > 1) {
                     ResolveInfo.DisplayNameComparator rComparator =
                             new ResolveInfo.DisplayNameComparator(mPm);
-                    Collections.sort(rList, rComparator);
+                    Collections.sort(mCurrentResolveList, rComparator);
                 }
                 
                 mList = new ArrayList<DisplayResolveInfo>();
                 
                 // First put the initial items at the top.
-                if (initialIntents != null) {
-                    for (int i=0; i<initialIntents.length; i++) {
-                        Intent ii = initialIntents[i];
+                if (mInitialIntents != null) {
+                    for (int i=0; i<mInitialIntents.length; i++) {
+                        Intent ii = mInitialIntents[i];
                         if (ii == null) {
                             continue;
                         }
@@ -300,14 +342,14 @@
                 
                 // Check for applications with same name and use application name or
                 // package name if necessary
-                r0 = rList.get(0);
+                r0 = mCurrentResolveList.get(0);
                 int start = 0;
                 CharSequence r0Label =  r0.loadLabel(mPm);
                 for (int i = 1; i < N; i++) {
                     if (r0Label == null) {
                         r0Label = r0.activityInfo.packageName;
                     }
-                    ResolveInfo ri = rList.get(i);
+                    ResolveInfo ri = mCurrentResolveList.get(i);
                     CharSequence riLabel = ri.loadLabel(mPm);
                     if (riLabel == null) {
                         riLabel = ri.activityInfo.packageName;
@@ -315,13 +357,13 @@
                     if (riLabel.equals(r0Label)) {
                         continue;
                     }
-                    processGroup(rList, start, (i-1), r0, r0Label);
+                    processGroup(mCurrentResolveList, start, (i-1), r0, r0Label);
                     r0 = ri;
                     r0Label = riLabel;
                     start = i;
                 }
                 // Process last group
-                processGroup(rList, start, (N-1), r0, r0Label);
+                processGroup(mCurrentResolveList, start, (N-1), r0, r0Label);
             }
         }
 
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 41993c4..ccd2763 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -22,12 +22,12 @@
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
 import android.net.NetworkStats;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Slog;
 
 import com.android.internal.util.ProcFileReader;
 import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
 
 import java.io.BufferedReader;
@@ -36,7 +36,6 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.StringTokenizer;
 
@@ -62,22 +61,6 @@
     /** Path to {@code /proc/net/xt_qtaguid/stats}. */
     private final File mStatsXtUid;
 
-    /** {@link #mStatsXtUid} and {@link #mStatsXtIfaceAll} headers. */
-    private static final String KEY_IDX = "idx";
-    private static final String KEY_IFACE = "iface";
-    private static final String KEY_ACTIVE = "active";
-    private static final String KEY_UID = "uid_tag_int";
-    private static final String KEY_COUNTER_SET = "cnt_set";
-    private static final String KEY_TAG_HEX = "acct_tag_hex";
-    private static final String KEY_SNAP_RX_BYTES = "snap_rx_bytes";
-    private static final String KEY_SNAP_RX_PACKETS = "snap_rx_packets";
-    private static final String KEY_SNAP_TX_BYTES = "snap_tx_bytes";
-    private static final String KEY_SNAP_TX_PACKETS = "snap_tx_packets";
-    private static final String KEY_RX_BYTES = "rx_bytes";
-    private static final String KEY_RX_PACKETS = "rx_packets";
-    private static final String KEY_TX_BYTES = "tx_bytes";
-    private static final String KEY_TX_PACKETS = "tx_packets";
-
     public NetworkStatsFactory() {
         this(new File("/proc/"));
     }
@@ -106,47 +89,39 @@
     }
 
     private NetworkStats readNetworkStatsSummarySingleFile() {
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
-        // TODO: transition to ProcFileReader
-        // TODO: read directly from proc once headers are added
-        final ArrayList<String> keys = Lists.newArrayList(KEY_IFACE, KEY_ACTIVE, KEY_SNAP_RX_BYTES,
-                KEY_SNAP_RX_PACKETS, KEY_SNAP_TX_BYTES, KEY_SNAP_TX_PACKETS, KEY_RX_BYTES,
-                KEY_RX_PACKETS, KEY_TX_BYTES, KEY_TX_PACKETS);
-        final ArrayList<String> values = Lists.newArrayList();
-        final HashMap<String, String> parsed = Maps.newHashMap();
-
-        BufferedReader reader = null;
+        ProcFileReader reader = null;
         try {
-            reader = new BufferedReader(new FileReader(mStatsXtIfaceAll));
+            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
 
-            String line;
-            while ((line = reader.readLine()) != null) {
-                splitLine(line, values);
-                parseLine(keys, values, parsed);
-
-                entry.iface = parsed.get(KEY_IFACE);
+            while (reader.hasMoreData()) {
+                entry.iface = reader.nextString();
                 entry.uid = UID_ALL;
                 entry.set = SET_DEFAULT;
                 entry.tag = TAG_NONE;
 
+                final boolean active = reader.nextInt() != 0;
+
                 // always include snapshot values
-                entry.rxBytes = getParsedLong(parsed, KEY_SNAP_RX_BYTES);
-                entry.rxPackets = getParsedLong(parsed, KEY_SNAP_RX_PACKETS);
-                entry.txBytes = getParsedLong(parsed, KEY_SNAP_TX_BYTES);
-                entry.txPackets = getParsedLong(parsed, KEY_SNAP_TX_PACKETS);
+                entry.rxBytes = reader.nextLong();
+                entry.rxPackets = reader.nextLong();
+                entry.txBytes = reader.nextLong();
+                entry.txPackets = reader.nextLong();
 
                 // fold in active numbers, but only when active
-                final boolean active = getParsedInt(parsed, KEY_ACTIVE) != 0;
                 if (active) {
-                    entry.rxBytes += getParsedLong(parsed, KEY_RX_BYTES);
-                    entry.rxPackets += getParsedLong(parsed, KEY_RX_PACKETS);
-                    entry.txBytes += getParsedLong(parsed, KEY_TX_BYTES);
-                    entry.txPackets += getParsedLong(parsed, KEY_TX_PACKETS);
+                    entry.rxBytes += reader.nextLong();
+                    entry.rxPackets += reader.nextLong();
+                    entry.txBytes += reader.nextLong();
+                    entry.txPackets += reader.nextLong();
                 }
 
                 stats.addValues(entry);
+                reader.finishLine();
             }
         } catch (NullPointerException e) {
             throw new IllegalStateException("problem parsing stats: " + e);
@@ -156,6 +131,7 @@
             throw new IllegalStateException("problem parsing stats: " + e);
         } finally {
             IoUtils.closeQuietly(reader);
+            StrictMode.setThreadPolicy(savedPolicy);
         }
         return stats;
     }
@@ -165,6 +141,8 @@
      */
     @Deprecated
     private NetworkStats readNetworkStatsSummaryMultipleFiles() {
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
@@ -241,6 +219,7 @@
             throw new IllegalStateException("problem parsing stats: " + e);
         } finally {
             IoUtils.closeQuietly(reader);
+            StrictMode.setThreadPolicy(savedPolicy);
         }
 
         return stats;
@@ -257,6 +236,8 @@
      * @throws IllegalStateException when problem parsing stats.
      */
     public NetworkStats readNetworkStatsDetail(int limitUid) throws IllegalStateException {
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
@@ -300,23 +281,12 @@
             throw new IllegalStateException("problem parsing idx " + idx, e);
         } finally {
             IoUtils.closeQuietly(reader);
+            StrictMode.setThreadPolicy(savedPolicy);
         }
 
         return stats;
     }
 
-    @Deprecated
-    private static int getParsedInt(HashMap<String, String> parsed, String key) {
-        final String value = parsed.get(key);
-        return value != null ? Integer.parseInt(value) : 0;
-    }
-
-    @Deprecated
-    private static long getParsedLong(HashMap<String, String> parsed, String key) {
-        final String value = parsed.get(key);
-        return value != null ? Long.parseLong(value) : 0;
-    }
-
     /**
      * Split given line into {@link ArrayList}.
      */
@@ -331,21 +301,6 @@
     }
 
     /**
-     * Zip the two given {@link ArrayList} as key and value pairs into
-     * {@link HashMap}.
-     */
-    @Deprecated
-    private static void parseLine(
-            ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
-        outParsed.clear();
-
-        final int size = Math.min(keys.size(), values.size());
-        for (int i = 0; i < size; i++) {
-            outParsed.put(keys.get(i), values.get(i));
-        }
-    }
-
-    /**
      * Utility method to read a single plain-text {@link Long} from the given
      * {@link File}, usually from a {@code /proc/} filesystem.
      */
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 8a8f315..6cfb97d 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -48,7 +48,7 @@
  */
 public class FileRotator {
     private static final String TAG = "FileRotator";
-    private static final boolean LOGD = true;
+    private static final boolean LOGD = false;
 
     private final File mBasePath;
     private final String mPrefix;
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 0cadb16..d462d7f 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -44,6 +44,7 @@
     public static final int BASE_WIFI_P2P_MANAGER                                   = 0x00022000;
     public static final int BASE_WIFI_P2P_SERVICE                                   = 0x00023000;
     public static final int BASE_WIFI_MONITOR                                       = 0x00024000;
+    public static final int BASE_WIFI_MANAGER                                       = 0x00025000;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 9579bce..4cbdf78 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -70,7 +70,7 @@
     public boolean endBatchEdit() {
         synchronized(this) {
             if (mBatchEditNesting > 0) {
-                // When the connection is reset by the InputMethodManager and finishComposingText
+                // When the connection is reset by the InputMethodManager and reportFinish
                 // is called, some endBatchEdit calls may still be asynchronously received from the
                 // IME. Do not take these into account, thus ensuring that this IC's final
                 // contribution to mTextView's nested batch edit count is zero.
@@ -83,6 +83,19 @@
     }
 
     @Override
+    protected void reportFinish() {
+        super.reportFinish();
+
+        synchronized(this) {
+            while (mBatchEditNesting > 0) {
+                endBatchEdit();
+            }
+            // Will prevent any further calls to begin or endBatchEdit
+            mBatchEditNesting = -1;
+        }
+    }
+
+    @Override
     public boolean clearMetaKeyStates(int states) {
         final Editable content = getEditable();
         if (content == null) return false;
@@ -99,23 +112,6 @@
     }
 
     @Override
-    public boolean finishComposingText() {
-        final boolean superResult = super.finishComposingText();
-        synchronized(this) {
-            if (mBatchEditNesting < 0) {
-                // The connection was already finished
-                return false;
-            }
-            while (mBatchEditNesting > 0) {
-                endBatchEdit();
-            }
-            // Will prevent any further calls to begin or endBatchEdit
-            mBatchEditNesting = -1;
-        }
-        return superResult;
-    }
-
-    @Override
     public boolean commitCompletion(CompletionInfo text) {
         if (DEBUG) Log.v(TAG, "commitCompletion " + text);
         mTextView.beginBatchEdit();
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 7f5d54d..efeba5c 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -36,6 +36,8 @@
 #define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
 #define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
 #define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
+#define TYPEFACE_DEVANAGARI "/system/fonts/Lohit-Devanagari.ttf"
+#define TYPEFACE_TAMIL "/system/fonts/Lohit-Tamil.ttf"
 #define TYPEFACE_THAI "/system/fonts/DroidSansThai.ttf"
 
 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
@@ -828,6 +830,20 @@
 #endif
         break;
 
+    case HB_Script_Devanagari:
+        typeface = getCachedTypeface(&mDevanagariTypeface, TYPEFACE_DEVANAGARI);
+#if DEBUG_GLYPHS
+        ALOGD("Using Devanagari Typeface");
+#endif
+        break;
+
+    case HB_Script_Tamil:
+        typeface = getCachedTypeface(&mTamilTypeface, TYPEFACE_TAMIL);
+#if DEBUG_GLYPHS
+        ALOGD("Using Tamil Typeface");
+#endif
+        break;
+
     default:
         if (!typeface) {
             typeface = mDefaultTypeface;
@@ -859,6 +875,8 @@
     case HB_Script_Arabic:
     case HB_Script_Hebrew:
     case HB_Script_Bengali:
+    case HB_Script_Devanagari:
+    case HB_Script_Tamil:
     case HB_Script_Thai:{
         const uint16_t* text16 = (const uint16_t*)(mShaperItem.string + mShaperItem.item.pos);
         SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 7ac2f18..3c834a4 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -194,6 +194,8 @@
     SkTypeface* mHebrewBoldTypeface;
     SkTypeface* mBengaliTypeface;
     SkTypeface* mThaiTypeface;
+    SkTypeface* mDevanagariTypeface;
+    SkTypeface* mTamilTypeface;
 
     /**
      * Cache of Harfbuzz faces
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 7c88dfc..088062a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -21,13 +21,16 @@
 #include <dlfcn.h>
 #include <fcntl.h>
 
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_app_NativeActivity.h>
 #include <android_runtime/android_util_AssetManager.h>
-#include <surfaceflinger/Surface.h>
-#include <system/window.h>
+#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/AndroidRuntime.h>
 #include <androidfw/InputTransport.h>
+
+#include <gui/Surface.h>
+
+#include <system/window.h>
+
 #include <utils/Looper.h>
 
 #include "JNIHelp.h"
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index d53644d..579d6ad 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -30,7 +30,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "binder/CursorWindow.h"
+#include <androidfw/CursorWindow.h>
 #include "android_util_Binder.h"
 #include "android_database_SQLiteCommon.h"
 
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index e061ac3..c8f911f 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "binder/CursorWindow.h"
+#include <androidfw/CursorWindow.h>
 
 #include <sqlite3.h>
 #include <sqlite3_android.h>
@@ -128,15 +128,6 @@
         return 0;
     }
 
-    // Enable WAL auto-checkpointing after a commit whenever at least one frame is in the log.
-    // This ensures that a checkpoint will occur after each transaction if needed.
-    err = sqlite3_wal_autocheckpoint(db, 1);
-    if (err) {
-        throw_sqlite3_exception(env, db, "Could not enable auto-checkpointing.");
-        sqlite3_close(db);
-        return 0;
-    }
-
     // Register custom Android functions.
     err = register_android_functions(db, UTF16_STORAGE);
     if (err) {
@@ -426,11 +417,11 @@
     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
 
+    // We ignore the result of sqlite3_finalize because it is really telling us about
+    // whether any errors occurred while executing the statement.  The statement itself
+    // is always finalized regardless.
     ALOGV("Finalized statement %p on connection %p", statement, connection->db);
-    int err = sqlite3_finalize(statement);
-    if (err != SQLITE_OK) {
-        throw_sqlite3_exception(env, connection->db, NULL);
-    }
+    sqlite3_finalize(statement);
 }
 
 static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jint connectionPtr,
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index b89273b..599211e 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -26,7 +26,7 @@
 #include <utils/Vector.h>
 
 #include <gui/SurfaceTexture.h>
-#include <surfaceflinger/Surface.h>
+#include <gui/Surface.h>
 #include <camera/Camera.h>
 #include <binder/IMemory.h>
 
diff --git a/core/jni/android_view_Display.cpp b/core/jni/android_view_Display.cpp
index f076cc8..aedf1e4 100644
--- a/core/jni/android_view_Display.cpp
+++ b/core/jni/android_view_Display.cpp
@@ -19,7 +19,7 @@
 
 #include <cutils/properties.h>
 
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceComposerClient.h>
 #include <ui/PixelFormat.h>
 #include <ui/DisplayInfo.h>
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 18bcea1..c387752 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -22,11 +22,13 @@
 #include "android/graphics/GraphicsJNI.h"
 
 #include <binder/IMemory.h>
+
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceTexture.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/Surface.h>
-#include <ui/Region.h>
+
 #include <ui/Rect.h>
+#include <ui/Region.h>
 
 #include <EGL/egl.h>
 
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 0f334c3..5483867 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -21,20 +21,20 @@
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <utils/misc.h>
 
+
+#include <EGL/egl_display.h>
 #include <EGL/egl.h>
 #include <GLES/gl.h>
 
-#include <EGL/egl_display.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
 
-#include <surfaceflinger/Surface.h>
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
 
 #include <ui/ANativeObjectBase.h>
 
-#include <gui/SurfaceTexture.h>
-#include <gui/SurfaceTextureClient.h>
-
 namespace android {
 
 static jclass gConfig_class;
@@ -46,7 +46,6 @@
 static jfieldID gSurface_EGLSurfaceFieldID;
 static jfieldID gSurface_NativePixelRefFieldID;
 static jfieldID gConfig_EGLConfigFieldID;
-static jfieldID gSurface_SurfaceFieldID;
 static jfieldID gBitmap_NativeBitmapFieldID;
 
 static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d4d29ae..17d2212 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -121,6 +121,7 @@
     <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
+    <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
@@ -1538,6 +1539,13 @@
         android:label="@string/permlab_accessContentProvidersExternally"
         android:description="@string/permdesc_accessContentProvidersExternally"
         android:protectionLevel="signature" />
+    <!-- Allows an application to hold an UpdateLock, recommending that a headless
+         OTA reboot *not* occur while the lock is held.
+         @hide -->
+    <permission android:name="android.permission.UPDATE_LOCK"
+        android:label="@string/permlab_updateLock"
+        android:description="@string/permdesc_updateLock"
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml
index 807daf2..2967696 100644
--- a/core/res/res/layout/number_picker.xml
+++ b/core/res/res/layout/number_picker.xml
@@ -25,7 +25,8 @@
         style="?android:attr/numberPickerUpButtonStyle"
         android:contentDescription="@string/number_picker_increment_button" />
 
-    <EditText android:id="@+id/numberpicker_input"
+    <view class="android.widget.NumberPicker$CustomEditText"
+        android:id="@+id/numberpicker_input"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         style="?android:attr/numberPickerInputTextStyle" />
diff --git a/core/res/res/layout/simple_list_item_multiple_choice.xml b/core/res/res/layout/simple_list_item_multiple_choice.xml
index 0305427..cb23dfd 100644
--- a/core/res/res/layout/simple_list_item_multiple_choice.xml
+++ b/core/res/res/layout/simple_list_item_multiple_choice.xml
@@ -17,10 +17,10 @@
 <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/text1"
     android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeight"
-    android:textAppearance="?android:attr/textAppearanceListItem"
+    android:layout_height="?android:attr/listPreferredItemHeightSmall"
+    android:textAppearance="?android:attr/textAppearanceListItemSmall"
     android:gravity="center_vertical"
     android:checkMark="?android:attr/listChoiceIndicatorMultiple"
-    android:paddingLeft="8dip"
-    android:paddingRight="8dip"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
 />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index abf6676..1de0390 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -315,6 +315,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktiveer of deaktiveer programkomponente"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Laat die program toe om te verander of \'n komponent van ander program geaktiveer is of nie. Kwaadwillige programme kan dit dalk gebruik om belangrike tabletvermoëns te deaktiveer. Wees versigtig met hierdie toestemming, want dit kan programkomponente tot \'n onbruikbare, inkonsekwente of onstabiele toestand bring."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Laat die program toe om te verander of \'n komponent van ander program geaktiveer is of nie. Kwaadwillige programme kan dit gebruik om belangrike foonvermoëns te deaktiveer. Wees versigtig met hierdie toestemming, want dit kan programkomponente tot \'n onbruikbare, inkonsekwente of onstabiele toestand bring."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"toestemmings te verleen of te herroep"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Laat \'n program toe om spesifieke toestemmings te verleen of te herroep vir die betrokke program of ander programme. Skadelike programme kan dit gebruik om toegang te verkry tot kenmerke waarvoor jy nie toestemming verleen het nie."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"stel voorkeurprogramme"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Laat die program toe om jou voorkeur-programme te verander. Kwaadwillige programme kan stilweg die programme wat loop, verander, wat jou bestaande programme bedrieg om private data oor jou in te samel."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"verander globale stelselinstellings"</string>
@@ -1006,6 +1008,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Stel datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Stel"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Verstek"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUUT: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Geen toestemmings benodig nie"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Versteek"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Wys alle"</b></string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index bb175e7..262a102 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"የመተግበሪያ ምንዝሮችን አንቃ ወይም አቦዝን"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"የሌላ መተግበሪያ ክፍለ አካል እንደነቃ ወይም እንዳልነቃ መተግበሪያው እንዲለውጥ ይፈቅዳል፡፡ አስፈላጊ የጡባዊ ተኮ አቅሞችን ለማስወገድ ጎጂ መተግበሪያዎች ይሄንን ሊጠቀሙበት ይችላሉ፡፡ ከፍቃድ ጋር ጥንቃቄ መወሰድ ይገባል፤ ልክ የማያገለግል፣ ወጥ ያልሆነ፣ ወይም ያልተረጋጋ ሁኔታ ወደ የመተግበሪያ ክፍለ አካል ማግኘት እንደሚቻል ሁሉ፡፡"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"የሌላ መተግበሪያ ክፍለ አካል እንደነቃ ወይም እንዳልነቃ መተግበሪያው እንዲለውጥ ይፈቅዳል፡፡ አስፈላጊ የስልክ አቅሞችን ለማስወገድ ተንኮል አዘል መተግበሪያዎች ይሄንን ሊጠቀሙበት ይችላሉ፡፡ ከፍቃድ ጋር ጥንቃቄ መወሰድ ይገባል፤ ልክ የማያገለግል፣ ወጥ ያልሆነ፣ ወይም ያልተረጋጋ ሁኔታ ወደ የመተግበሪያ ክፍለ አካል ማግኘት እንደሚቻል ሁሉ፡፡"</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"ፍቃዶች ስጥ ወይም ከልክል"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"የተወሰነ ፍቃዶች እንዲሰጥ ወይም እንዲከለክል ለመተግበሪያ ይፈቅዳል ወይም ሌላ መተግበሪያዎች፡፡ ተንኮል አዘል መተግበሪያዎች ያልፈቀድክላቸውን ባህሪያት ላይ ለመድረስ ይሄንን ሊጠቀሙት ይችላሉ፡፡"</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ተመራጭ መተግበሪያዎች አዘጋጅ"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"ተመራጭ መተግበሪያዎችህን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡ ካንተ የግል ውሂብ ለመሰብሰብ ያሉትን መተግበሪያዎች በመላክ፤ በመሄድ ላይ ያሉ መተግበሪያዎችን  ተንኮል አዘል መተግበሪያዎች በዝምታ ሊለውጡ ይችላሉ፡፡"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"የሁሉንም ስርዓት ቅንብሮች ቀይር"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"ውሂብ አዘጋጅ"</string>
     <string name="date_time_set" msgid="5777075614321087758">"አዘጋጅ"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"ነባሪ"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"አዲስ፦ "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"ምንም ፍቃዶች አይጠየቁም"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"ደብቅ "</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"ሁሉንም አሳይ"</b></string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a98a7d4..abdda71 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"تمكين مكونات التطبيق أو تعطيلها"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"للسماح للتطبيق بتغيير ما إذا كان سيتم تمكين مكون لتطبيق آخر أم لا. يمكن أن تستخدم التطبيقات الضارة ذلك لتعطيل قدرات الجهاز اللوحي المهمة. يجب توخي الحذر عند استخدام هذا الإذن، وذلك لأنه من الممكن أن يؤدي ذلك إلى جعل حالة مكونات التطبيق غير قابلة للاستخدام أو غير متناسقة أو غير مستقرة."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"للسماح للتطبيق بتغيير ما إذا كان سيتم تمكين مكون لتطبيق آخر أم لا. يمكن أن تستخدم التطبيقات الضارة ذلك لتعطيل قدرات الهاتف المهمة. يجب توخي الحذر عند استخدام هذا الإذن، وذلك لأنه من الممكن أن يؤدي ذلك إلى جعل حالة مكونات التطبيق غير قابلة للاستخدام أو غير متناسقة أو غير مستقرة."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"منح الأذونات أو إلغائها"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"للسماح لأحد التطبيقات بمنح أذونات محددة أو إلغائها لنفسه أو لتطبيقات أخرى. قد تستخدم التطبيقات الضارة هذا للدخول إلى ميزات لم تمنحها إذنًا لدخولها."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"تعيين التطبيقات المفضلة"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"للسماح للتطبيق بتعديل التطبيقات المفضلة. يمكن أن تغيّر التطبيقات الضارة التطبيقات قيد التشغيل بشكل غير ملحوظ، وانتحال صفة التطبيقات الحالية لجمع بيانات خاصة منك."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"تعديل إعدادات النظام العمومية"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"تعيين التاريخ"</string>
     <string name="date_time_set" msgid="5777075614321087758">"تعيين"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"افتراضي"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"جديد: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"لا أذونات مطلوبة"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"إخفاء"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"عرض الكل"</b></string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 233d5a9..5cfa349 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"уключыць або адключыць кампаненты прыкладання"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дазваляе прыкладанням змяняць вызначэнне таго, будзе ўключаны кампанент іншага прыкладання ці не. Шкоднасныя прыкладанні могуць выкарыстоўваць гэта для адключэння важных магчымасцяў планшэта. З гэтым дазволам трэба быць уважлівым, бо можна прывесці кампаненты прыкладання ў непрыдатны, супярэчлівы або няўстойлівы стан."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дазваляе прыкладанням змяняць вызначэнне таго, будзе ўключаны кампанент іншага прыкладання ці не. Шкоднасныя прыкладанні могуць выкарыстоўваць гэта для адключэння важных магчымасцяў тэлефона. З гэтым дазволам трэба быць уважлівым, бо можна прывесці кампаненты прыкладання ў непрыдатны, супярэчлівы або няўстойлівы стан."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"прадаставiць або адмяніць дазвол"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Дазваляе прыкладанню прадастаўляць або адмяняць пэўныя дазволы для гэтага або іншых прыкладанняў. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб атрымаць доступ да функцый, якiя вы iм не прадаставiлi."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"усталяваць пажаданыя прыкладанні"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дазваляе прыкладанням змяняць вашы пажаданыя прыкладанні. Шкоднасныя прыкладанні могуць непрыкметна змяняць запушчаныя прыкладанні, падмяняючы існуючыя прыкладанні, каб збiраць вашы асабістыя дадзеныя."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"змена глабальных параметраў сістэмы"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Усталяваць дату"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Задаць"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Па змаўчанні"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВАЕ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Дазволу не патрабуецца"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Не паказваць"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Паказаць усе"</b></string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 66516b9..06a2577 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"активиране или деактивиране на компоненти на приложенията"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Разрешава на приложението да активира или деактивира компонент на друго приложение. Злонамерените приложения могат да използват това, за да деактивират важни възможности на таблета. С това разрешение трябва да се внимава, тъй като компонентите на приложенията може да бъдат приведени в неизползваемо, несъгласувано или нестабилно състояние."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Разрешава на приложението да активира или деактивира компонент на друго приложение. Злонамерените приложения могат да използват това, за да деактивират важни възможности на телефона. С това разрешение трябва да се внимава, тъй като компонентите на приложенията може да бъдат приведени в неизползваемо, несъгласувано или нестабилно състояние."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"задаване на предпочитани приложения"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Разрешава на приложението да променя предпочитаните ви приложения. Злонамерените приложения могат скрито да променят приложенията, които се изпълняват, като ги фалшифицират, за да се сдобият с ваши лични данни."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"промяна на глобалните системни настройки"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Задаване на дата"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Задаване"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"По подразбиране"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Не се изискват разрешения"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Скриване"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Показване на всички"</b></string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index b848c2c..c66451a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activa o desactiva els components de l\'aplicació"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permet que l\'aplicació canviï si un component d\'una altra aplicació està activat o no. Les aplicacions malicioses poden utilitzar aquesta funció per desactivar funcions importants de la tauleta. Cal anar amb compte amb aquest permís, ja que és possible que els components de l\'aplicació esdevinguin inutilitzables, incoherents o inestables."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permet que l\'aplicació canviï si un component d\'una altra aplicació està activat o no. Les aplicacions malicioses poden utilitzar aquesta funció per desactivar funcions importants del telèfon. Cal anar amb compte amb aquest permís, perquè és possible que els components de l\'aplicació esdevinguin inutilitzables, incoherents o inestables."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"concedeix o denega permisos"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permet que una aplicació concedeixi o denegui permisos específics per a aquesta o per a altres aplicacions. És possible que les aplicacions malicioses ho facin servir per accedir a funcions a les quals no heu concedit accés."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"defineix les aplicacions preferides"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permet que l\'aplicació modifiqui les aplicacions preferides. Les aplicacions malicioses poden canviar silenciosament les aplicacions que s\'executen, falsejar les aplicacions existents o recollir dades privades de l\'usuari."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuració global del sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Establiment de data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Defineix"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predeterminat"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOU: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Amaga"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostra\'ls tots"</b></string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 544395f..89ff9cff3 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivace či deaktivace komponent aplikací"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Umožňuje aplikaci změnit, zda je komponenta jiné aplikace povolena nebo ne. Škodlivé aplikace mohou toto oprávnění použít k vypnutí důležitých funkcí tabletu. Toto oprávnění je třeba používat opatrně, protože může dojít k nepoužitelnosti, nekonzistenci nebo nestabilitě komponent aplikací."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Umožňuje aplikaci změnit, zda je komponenta jiné aplikace povolena nebo ne. Škodlivé aplikace mohou toto oprávnění použít k vypnutí důležitých funkcí telefonu. Toto oprávnění je třeba používat opatrně, protože může dojít k nepoužitelnosti, nekonzistenci nebo nestabilitě komponent aplikací."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"udělení nebo odebrání oprávnění"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Umožňuje aplikaci udělit nebo odebrat sobě samotné nebo jiným aplikacím určitá oprávnění. Škodlivé aplikace pomocí tohoto oprávnění mohou získat přístup k funkcím, které jste jim nepovolili."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastavení upřednostňovaných aplikací"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Umožňuje aplikaci upravit preferované aplikace. Škodlivé aplikace mohou tajně měnit běžící aplikace a přinutit stávající aplikace, aby shromažďovaly vaše soukromé údaje."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"změna globálních nastavení systému"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastavení data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastavit"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Výchozí"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVÉ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skrýt"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Zobrazit vše"</b></string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ceefe1d..114cac2 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivere eller deaktivere appkomponenter"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Tillader, at appen kan ændre, om en komponent i en anden app er aktiveret eller ej. Ondsindede apps kan bruge dette til at deaktivere vigtige tabletfunktioner. Man skal være forsigtig med denne tilladelse, da det er muligt at bringe appkomponenter ind i en ubrugelig, inkonsekvent eller ustabil tilstand."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Tillader, at appen kan ændre, om en komponent i en anden app er aktiveret eller ej. Ondsindede apps kan bruge dette til at deaktivere vigtige funktioner på telefonen. Denne tilladelse skal anvendes med forsigtighed, da det kan forårsage ubrugelige eller ustabile appkomponenter."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"udsted eller tilbagekald tilladelser"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Tillader, at en applikation udsteder eller tilbagekalder særlige tilladelser til den selv eller andre applikationer. Ondsindede applikationer kan bruge dette til at få adgang til funktioner uden din tilladelse."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"angive foretrukne apps"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Tillader, at appen kan ændre dine foretrukne apps. Ondsindede apps kan ubemærket ændre kørende apps derved udgive sig for at være dine eksisterende apps og på den måde indsamle private data fra dig."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"rediger globale systemindstillinger"</string>
@@ -364,7 +366,7 @@
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Tillader, at appen kan læse indholdet fra rammebufferen."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="7343951185408396919">"Tillader, at appen kan ændre globale lydindstillinger, f.eks. lydstyrke og kanalisering."</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"optag lyd"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
     <string name="permdesc_recordAudio" msgid="2387462233976248635">"Tillader, at appen får adgang til stien til lydoptagelse."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tag billeder og optag video"</string>
     <string name="permdesc_camera" msgid="1507407407002492176">"Tillader, at appen kan tage billeder og optage video med kameraet. Dette tillader, at appen når som helst kan indsamle billeder, som kameraet kan se."</string>
@@ -390,7 +392,7 @@
     <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Tillader, at appen kan montere/demontere det interne lager."</string>
     <string name="permlab_asec_rename" msgid="7496633954080472417">"omdøbe internt lager"</string>
     <string name="permdesc_asec_rename" msgid="1794757588472127675">"Tillader, at appen kan omdøbe det interne lager."</string>
-    <string name="permlab_vibrate" msgid="7768356019980849603">"kontroller vibrator"</string>
+    <string name="permlab_vibrate" msgid="7768356019980849603">"kontrollere vibrator"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Tillader, at appen kan kontrollere vibratoren."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"kontroller lommelygte"</string>
     <string name="permdesc_flashlight" msgid="6522284794568368310">"Tillader, at appen kan kontrollere lommelygten."</string>
@@ -418,7 +420,7 @@
     <string name="permlab_readPhoneState" msgid="2326172951448691631">"læs telefontilstand og identitet"</string>
     <string name="permdesc_readPhoneState" msgid="5127767618743602782">"Tillader, at appen kan få adgang til enhedens telefonfunktioner. En app med denne tilladelse kan fastlægge telefonens telefon- og serienummer, om et opkald er aktivt, det nummer, som opkaldet er tilknyttet osv."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"afhold tabletcomputeren fra at gå i dvale"</string>
-    <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"afhold telefonen fra at gå i dvale"</string>
+    <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"afholde telefonen fra at gå i dvale"</string>
     <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillader, at appen kan forhindre tabletten i at gå i dvale."</string>
     <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillader, at appen kan forhindre, at telefonen går i dvale."</string>
     <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"tænd eller sluk for tabletcomputeren"</string>
@@ -447,11 +449,11 @@
     <string name="permdesc_getAccounts" product="default" msgid="2735689364629830348">"Tillader, at appen kan hente listen over konti, der er kendt af telefonen."</string>
     <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"fungerer som en kontogodkender"</string>
     <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"Tillader, at en app kan bruge kontoadministratorens kontogodkendelsesegenskaber, bl.a. oprettelse af konti samt hentning og angivelse af deres adgangskoder."</string>
-    <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrer kontolisten"</string>
+    <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrere kontolisten"</string>
     <string name="permdesc_manageAccounts" msgid="8698295625488292506">"Tillader, at appen kan foretage handlinger såsom at tilføje og fjerne konti og slette adgangskoden."</string>
-    <string name="permlab_useCredentials" msgid="6401886092818819856">"brug en kontos godkendelsesoplysninger"</string>
+    <string name="permlab_useCredentials" msgid="6401886092818819856">"bruge en kontos godkendelsesoplysninger"</string>
     <string name="permdesc_useCredentials" msgid="7984227147403346422">"Tillader, at appen kan anmode om godkendelsestokens."</string>
-    <string name="permlab_accessNetworkState" msgid="6865575199464405769">"vis netværkstilstand"</string>
+    <string name="permlab_accessNetworkState" msgid="6865575199464405769">"vise netværkstilstand"</string>
     <string name="permdesc_accessNetworkState" msgid="479772796952547198">"Tillader, at appen kan vise tilstanden for alle netværk."</string>
     <string name="permlab_createNetworkSockets" msgid="9121633680349549585">"fuld internetadgang"</string>
     <string name="permdesc_createNetworkSockets" msgid="5963922297444265950">"Tillader, at appen kan skabe netværkssockets."</string>
@@ -483,11 +485,11 @@
     <string name="permdesc_nfc" msgid="7120611819401789907">"Tillader, at appen kan kommunikere med NFC-tags (Near Field Communication), -kort og -læsere."</string>
     <string name="permlab_disableKeyguard" msgid="4977406164311535092">"deaktiver tastaturlås"</string>
     <string name="permdesc_disableKeyguard" msgid="6231611286892232626">"Tillader, at appen kan deaktivere tastaturlåsen og al associeret adgangskodesikkerhed. Et legitimt eksempel på dette er, at telefonen deaktiverer tastaturlåsen ved indgående telefonopkald, og aktiverer tastaturlåsen igen, når opkaldet er afsluttet."</string>
-    <string name="permlab_readSyncSettings" msgid="6201810008230503052">"læs indstillinger for synkronisering"</string>
+    <string name="permlab_readSyncSettings" msgid="6201810008230503052">"læse indstillinger for synkronisering"</string>
     <string name="permdesc_readSyncSettings" msgid="5464056785274229278">"Tillader, at appen kan læse synkroniseringsindstillingerne, f.eks. om synkronisering er aktiveret for appen Personer."</string>
-    <string name="permlab_writeSyncSettings" msgid="6297138566442486462">"skriv indstillinger for synkronisering"</string>
+    <string name="permlab_writeSyncSettings" msgid="6297138566442486462">"skrive indstillinger for synkronisering"</string>
     <string name="permdesc_writeSyncSettings" msgid="1466056564502117130">"Tillader, at appen kan ændre indstillingerne for synkronisering, f.eks. om appen Personer skal synkroniseres."</string>
-    <string name="permlab_readSyncStats" msgid="7396577451360202448">"læs synkroniseringsstatistikker"</string>
+    <string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string>
     <string name="permdesc_readSyncStats" msgid="3801971839939951678">"Tillader, at appen kan læse synkroniseringsstatistikker, f.eks. synkroniseringshistorikken."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læs abonnerede feeds"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string>
@@ -747,9 +749,9 @@
     <string name="autofill_parish" msgid="8202206105468820057">"Sogn"</string>
     <string name="autofill_area" msgid="3547409050889952423">"Område"</string>
     <string name="autofill_emirate" msgid="2893880978835698818">"Emirat"</string>
-    <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string>
+    <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læse browserens oversigt og bogmærker"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4577476392604595921">"Tillader, at appen kan læse alle de webadresser, som Browser har besøgt, og alle bogmærker i Browser."</string>
-    <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
+    <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive i browserens oversigt og bogmærker"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="1757103804824209530">"Tillader, at appen kan ændre historik eller bogmærker i Browser, som er gemt på din tablet. Ondsindede apps kan bruge dette til at slette eller ændre dine browserdata."</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="6693764355720719197">"Tillader, at appen kan ændre browserhistorikken eller bogmærker, der er gemt på din telefon. Ondsindede apps kan bruge dette til at slette eller ændre dine browserdata."</string>
     <string name="permlab_setAlarm" msgid="5924401328803615165">"angiv alarm i alarmprogram"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angiv dato"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Angiv"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NYHED! "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skjul"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 3b3d08d..426a8c8 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"App-Komponenten aktivieren oder deaktivieren"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Ermöglicht der App, Komponenten einer anderen App zu aktivieren oder zu deaktivieren. Schädliche Apps können so wichtige Tabletfunktionen deaktivieren. Bei der Erteilung dieser Berechtigung ist Vorsicht geboten, da die App-Komponenten unbrauchbar, inkonsistent oder instabil werden können."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Ermöglicht der App, Komponenten einer anderen App zu aktivieren oder zu deaktivieren. Schädliche Apps können so wichtige Telefonfunktionen deaktivieren. Bei der Erteilung dieser Berechtigung ist Vorsicht geboten, da die App-Komponenten unbrauchbar, inkonsistent oder instabil werden können."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"Berechtigungen erteilen oder entziehen"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Hiermit kann eine App sich selbst oder anderen Apps bestimmte Berechtigungen erteilen oder entziehen. Schädliche Apps können hierdurch Zugriff auf Funktionen erlangen, den Sie nicht gewährt haben."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"Bevorzugte Apps festlegen"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Ermöglicht der App, Änderungen an Ihren bevorzugten Apps vorzunehmen. Schädliche Apps können so aktive Apps ohne Ihr Wissen ändern, damit die vorhandenen Apps private Daten von Ihnen erfassen."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"allgemeine Systemeinstellungen ändern"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Datum festlegen"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Speichern"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"Neu: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Keine Berechtigungen erforderlich"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ausblenden"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Alle anzeigen"</b></string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e2bc244..91da279 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"ενεργοποίηση ή απενεργοποίηση στοιχείων εφαρμογής"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης ενεργοποίησης κάποιου στοιχείου. Τυχόν κακόβουλες εφαρμογές μπορούν να χρησιμοποιήσουν αυτήν τη δυνατότητα για την απενεργοποίηση σημαντικών δυνατοτήτων του tablet. Αυτή η άδεια θα πρέπει να χρησιμοποιείται προσεκτικά, καθώς είναι πιθανό να θέσει τα στοιχεία εφαρμογών σε κατάσταση αχρηστίας, μη συνοχής και αστάθειας."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης ενεργοποίησης κάποιου στοιχείου. Τυχόν κακόβουλες εφαρμογές μπορούν να χρησιμοποιήσουν αυτήν τη δυνατότητα για την απενεργοποίηση σημαντικών δυνατοτήτων του τηλεφώνου. Αυτή η άδεια θα πρέπει να χρησιμοποιείται προσεκτικά, καθώς είναι πιθανό να θέσει τα στοιχεία εφαρμογών σε κατάσταση αχρηστίας, μη συνοχής και αστάθειας."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"εκχώρηση ή ανάκληση δικαιωμάτων"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Επιτρέπει σε μια εφαρμογή να εκχωρήσει ή να ανακαλέσει ειδικά δικαιώματα για αυτήν ή για άλλες εφαρμογές. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν αυτό ώστε να αποκτήσουν πρόσβαση σε λειτουργίες για τις οποίες δεν τους έχει εκχωρηθεί δικαίωμα."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ορισμός προτιμώμενων εφαρμογών"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Επιτρέπει στην εφαρμογή την τροποποίηση των εφαρμογών της προτίμησής σας. Τυχόν κακόβουλες εφαρμογές ενδέχεται να να αλλάξουν χωρίς ειδοποίηση τις εφαρμογές που εκτελούνται, \"ξεγελώντας\" τις υπάρχουσες εφαρμογές ώστε να συλλέξουν ιδιωτικά δεδομένα από εσάς."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"τροποποίηση καθολικών ρυθμίσεων συστήματος"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Ορισμός ημερομηνίας"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ορισμός"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Προεπιλεγμένο"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ΝΕΟ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Δεν απαιτούνται άδειες"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Απόκρυψη"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Εμφάνιση όλων"</b></string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bed4de2..cfd9965 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"enable or disable app components"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Allows the app to change whether a component of another app is enabled or not. Malicious apps may use this to disable important tablet capabilities. Care must be taken with this permission, as it is possible to get app components into an unusable, inconsistent or unstable state."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Allows the app to change whether a component of another app is enabled or not. Malicious apps may use this to disable important phone capabilities. Care must be taken with this permission, as it is possible to get app components into an unusable, inconsistent or unstable state."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"grant or revoke permissions"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Allows an application to grant or revoke specific permissions for it or other applications. Malicious applications may use this to access features for which you have not granted them permission."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"set preferred apps"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Allows the app to modify your preferred apps. Malicious apps may silently change the apps that are run, spoofing your existing apps to collect private data from you."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modify global system settings"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Set date"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Set"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NEW: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Hide"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Show all"</b></string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 58df147..fe292e6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activar o desactivar componentes de la aplicación"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o no. Las aplicaciones maliciosas pueden utilizar este permiso para desactivar funciones importantes de la tableta. Es necesario ser precavido con este permiso, ya que es posible que los componentes de la aplicación queden inservibles, incoherentes o inestables."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o no. Las aplicaciones maliciosas pueden utilizar este permiso para desactivar funciones importantes del dispositivo. Es necesario ser precavido con este permiso, ya que es posible que los componentes de la aplicación queden inservibles, incoherentes o inestables."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"Otorgar o revocar permisos"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite a una aplicación otorgar permisos específicos a otras aplicaciones, autoconcedérselos o revocarlos. Las aplicaciones maliciosas pueden así acceder a funciones para las que no les otorgaste permiso."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"establecer aplicaciones preferidas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite que la aplicación modifique tus aplicaciones preferidas. Las aplicaciones maliciosas pueden modificar sin aviso las aplicaciones que se ejecutan y así engañar a tus aplicaciones existentes para que recopilen tu información privada."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuración global del sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Configurar fecha"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Establecer"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUEVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No se requieren permisos"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9499214..d0c0cb9 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"habilitar o inhabilitar componentes de la aplicación"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o inhabilitado. Las aplicaciones malintencionadas pueden usar este permiso para inhabilitar funciones importantes del tablet. Este permiso se debe usar con precaución, ya que los componentes de las aplicaciones se pueden volver inestables, incoherentes o inservibles."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o inhabilitado. Las aplicaciones malintencionadas pueden usar este permiso para inhabilitar funciones importantes del teléfono. Este permiso se debe usar con precaución, ya que los componentes de las aplicaciones se pueden volver inestables, incoherentes o inservibles."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"conceder o revocar permisos"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite que una aplicación conceda o revoque permisos específicos para sí misma o para otras aplicaciones. Las aplicaciones malintencionadas pueden aprovechar este permiso para acceder a funciones sin tu autorización."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"establecer aplicaciones preferidas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite que la aplicación modifique las aplicaciones preferidas del usuario. De esta forma, las aplicaciones malintencionadas pueden cambiar sin aviso las aplicaciones que se están ejecutando y falsificarlas para obtener datos privados del usuario."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuración global del sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Establecer fecha"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Establecer"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUEVO:"</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No es necesario ningún permiso"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index da06538..2a4352d 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"Rakenduse komponentide lubamine või keelamine"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Võimaldab rakendusel määrata, kas teise rakenduse komponent on lubatud või mitte. Pahatahtlikud rakendused võivad kasutada seda oluliste tahvelarvutirakenduste keelamiseks. Nende õiguste puhul peab olema ettevaatlik, kuna need võimaldavad muuta rakenduse komponente kasutuks, ebaühtlaseks või ebastabiilseks."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Võimaldab rakendusel määrata, kas teise rakenduse komponent on lubatud või mitte. Pahatahtlikud rakendused võivad kasutada seda oluliste telefonirakenduste keelamiseks. Nende õiguste puhul peab olema ettevaatlik, kuna need võimaldavad muuta rakenduse komponente kasutuks, ebaühtlaseks või ebastabiilseks."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"lubade andmine või tühistamine"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Võimaldab rakendusel anda või tühistada teatud lubasid endale või teistele rakendustele. Pahatahtlikud rakendused võivad kasutada seda juurdepääsu hankimiseks sellistele funktsioonidele, mille jaoks te pole luba andnud."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"Eelistatud rakenduste määramine"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Võimaldab rakendusel muuta teie eelistatud rakendusi. Pahatahtlikud rakendused võivad salaja muuta töötavaid rakendusi, pettes teie olemasolevad rakendused koguma teilt privaatseid andmeid."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"muuda üldisi süsteemiseadeid"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Kuupäeva määramine"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Määra"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Vaikimisi"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"UUS: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Lube pole vaja"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Peida"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Näita kõiki"</b></string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 68988f2..084e16d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"فعال یا غیر فعال کردن اجزای برنامه"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"به برنامه اجازه می‎دهد تا فعال بودن یا نبودن اجزای برنامه دیگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا قابلیتهای مهم رایانه لوحی را غیرفعال کنند. باید دقت کرد که با این مجوز ممکن است وضعیت اجزای برنامه ناپایدار، ناهماهنگ یا غیرقابل استفاده شود."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"به برنامه اجازه می‎دهد تا فعال بودن یا غیرفعال بودن جزئیات برنامه دیگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا ویژگیهای مهم را غیرفعال کنند. برای این مجوز باید دقت کنید چون ممکن است وضعیت جزئیات برنامه ناپایدار، بی‎ثبات یا غیرقابل استفاده شود."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"تنظیم برنامه‎های ترجیحی"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"به برنامه اجازه می‎دهد تا برنامه‎های ترجیحی شما را تغییر دهد. برنامه‎های مخرب می‎توانند بدون اعلان برنامه‎هایی را که اجرا می‎شوند، تغییر دهند خود را به جای برنامه‎های کنونی قلمداد کنند تا داده‎های شخصی را از شما جمع آوری کنند."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"اصلاح کردن تنظیمات سیستم کلی"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"تاریخ تنظیم"</string>
     <string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"پیش فرض"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"پنهان کردن"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"نمایش همه"</b></string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2fe9b4e..ee7efc8 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"sovelluskomponenttien ottaminen käyttöön tai pois käytöstä"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Antaa sovelluksen muuttaa, onko toisen sovelluksen komponentti käytössä vai ei. Haitalliset sovellukset voivat käyttää tätä tablet-laitteen tärkeiden ominaisuuksien poistamiseen käytöstä. Tämän luvan käyttöönotto edellyttää varovaisuutta, sillä sen avulla sovelluskomponentit on mahdollista saada epäkäytettävään, epäyhtenäiseen tai epävakaaseen tilaan."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Antaa sovelluksen muuttaa sitä, onko toisen sovelluksen komponentti käytössä vai ei. Haitalliset sovellukset voivat käyttää tätä puhelimen tärkeiden ominaisuuksien poistamiseen käytöstä. Tämän luvan käyttöönotto edellyttää varovaisuutta, sillä sen avulla sovelluskomponentit on mahdollista saada epäkäytettävään, epäyhtenäiseen tai epävakaaseen tilaan."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"myönnä tai kiellä käyttöluvat"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Antaa sovelluksen myöntää tai kieltää tiettyjä käyttööoikeuksia itselleen tai muille sovelluksille. Haittaohjelmat voivat käyttää tämän ominaisuuden avulla toimintoja, joiden käyttöön et ole antanut lupaa."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ensisijaisten sovellusten asettaminen"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Antaa sovelluksen muokata ensisijaisia sovelluksia. Haitalliset sovellukset voivat muuttaa käynnistettäviä sovelluksia huomaamattomasti ja kerätä henkilökohtaisia tietoja matkimalla nykyisiä sovelluksia."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"muokkaa yleisiä järjestelmän asetuksia"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Aseta päivämäärä"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Aseta"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Oletus"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"UUTTA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Piilota"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Näytä kaikki"</b></string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d6e8f30..bca5701 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activer ou désactiver les composants de l\'application"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permet à l\'application d\'activer ou de désactiver un composant d\'une autre application. Des applications malveillantes peuvent exploiter cette fonctionnalité pour désactiver les fonctionnalités principales de votre tablette. Cette autorisation doit être utilisée avec prudence, car elle peut rendre les composants d\'une application instables, voire inutilisables."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permet à l\'application d\'activer ou de désactiver un composant d\'une autre application. Des applications malveillantes peuvent exploiter cette fonctionnalité pour désactiver les fonctionnalités principales de votre téléphone. Cette autorisation doit être utilisée avec prudence, car elle peut rendre les composants d\'une application instables, voire inutilisables."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"accorder ou révoquer des autorisations"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permet à une application d\'accorder ou de révoquer des autorisations spécifiques pour celle-ci ou pour d\'autres applications. Des applications malveillantes peuvent exploiter cette autorisation pour accéder à des fonctionnalités auxquelles vous ne leur avez pas donné l\'accès."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"définir les applications préférées"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permet à l\'application de modifier vos applications préférées. Des applications malveillantes peuvent exploiter cette fonctionnalité pour modifier les applications exécutées en usurpant l\'identité de vos applications existantes dans le but de recueillir des données privées."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"Modification des paramètres généraux du système"</string>
@@ -765,7 +767,7 @@
     <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe fournisseurs de contenu"</string>
-    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis l\'interface. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis la commande shell. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Définir la date"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Définir"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Par défaut"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOUVEAU"</font>" :"</string>
     <string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Masquer"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Tout afficher"</b></string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f5a3cb7..1454253 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"एप्‍लिकेशन घटकों को सक्षम या अक्षम करें"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"एप्‍लिकेशन को यह बदलने देता है कि किसी अन्‍य एप्‍लिकेशन का घटक सक्षम है या नहीं. दुर्भावनापूर्ण एप्‍लिकेशन महत्‍वपूर्ण फ़ोन क्षमताओं को अक्षम करने में इसका उपयोग कर सकते हैं. इस अनुमति का उपयोग सावधानी के साथ करना चाहिए, क्योंकि इससे एप्‍लिकेशन घटकों के अनुपयोगी, असंगत, या अस्‍थिर स्‍थिति में जाने की संभावना है."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"एप्‍लिकेशन को यह बदलने देता है कि किसी अन्‍य एप्‍लिकेशन का घटक सक्षम है या नहीं. दुर्भावनापूर्ण एप्‍लिकेशन महत्‍वपूर्ण फ़ोन क्षमताओं को अक्षम करने में इसका उपयोग कर सकते हैं. इस अनुमति का उपयोग सावधानी के साथ करना चाहिए, क्योंकि इससे एप्‍लिकेशन घटकों के अनुपयोगी, असंगत, या अस्‍थिर स्‍थिति में जाने की संभावना है."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"अनुमति दें या रद्द करें"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"एप्लिकेशन को उसके या अन्य एप्लिकेशन के लिए विशेष अनुमतियां देने या रद्द करने देता है. दुर्भावनापूर्ण एप्लिकेशन इसका उपयोग उन विशेषताओं तक पहुंचने के लिए कर सकते हैं जो आपने उन्हें नहीं दी हैं."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"पसंदीदा एप्‍लिकेशन सेट करें"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"एप्लिकेशन को आपके पसंदीदा एप्लिकेशन को संशोधित करने देता है. दुर्भावनापूर्ण एप्लिकेशन आपसे निजी डेटा एकत्रित करने के लिए आपके मौजूदा एप्लिकेशन को स्पूफ़ करके, चलाए जाने वाले एप्लिकेशन को चुपचाप बदल सकते हैं."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"वैश्विक सिस्‍टम सेटिंग संशोधित करें"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"दिनांक सेट करें"</string>
     <string name="date_time_set" msgid="5777075614321087758">"सेट करें"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"डिफ़ॉल्ट"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"नया: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"किसी अनुमति की आवश्‍यकता नहीं है"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"छुपाएं"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"सभी दिखाएं"</b></string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 01378666..1908840 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"omogućavanje ili onemogućavanje komponenti aplikacije"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Omogućuje aplikaciji da promijeni hoće li komponenta neke druge aplikacije biti omogućena ili neće. Zlonamjerne aplikacije mogu to upotrijebiti da bi onemogućile važne mogućnosti tabletnog računala. Treba biti oprezan s tom dozvolom jer je moguće dovesti komponente aplikacija u neupotrebljivo, nedosljedno ili nestabilno stanje."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Omogućuje aplikaciji da promijeni hoće li komponenta neke druge aplikacije biti omogućena. Zlonamjerne aplikacije mogu to upotrijebiti da bi onemogućile važne mogućnosti telefona. Treba biti oprezan s tom dozvolom jer je moguće dovesti komponente aplikacija u neupotrebljivo, nedosljedno ili nestabilno stanje."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"dati ili oduzeti dopuštenja"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Aplikaciji omogućuje da odobri ili odbije određena dopuštenja za sebe ili druge aplikacije. Zlonamjerne aplikacije to mogu upotrijebiti za pristup značajkama za koje im niste odobrili pristup."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"postavljanje željenih aplikacija"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Omogućuje aplikaciji promjenu vaših željenih aplikacija. Zlonamjerne aplikacije mogu potajno promijeniti aplikacije koje su pokrenute, zavaravajući vaše postojeće aplikacije kako bi prikupljale privatne podatke od vas."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"izmjena postavki globalnog sustava"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Postavi datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Postavi"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Zadano"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nije potrebno dopuštenje"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Sakrij"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaži sve"</b></string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 54e8b06..81f697b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"alkalmazáskomponensek be- és kikapcsolása"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy más alkalmazások komponensei engedélyezve vannak-e vagy sem. A rosszindulatú alkalmazások ezt a táblagép fontos funkcióinak kikapcsolására használhatják. Óvatosan kell eljárni az engedély megadásával, mert lehetséges, hogy a komponensek használhatatlanok, inkonzisztensek vagy instabilak lesznek."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy más alkalmazások komponensei engedélyezve vannak-e vagy sem. A rosszindulatú alkalmazások ezt a telefon fontos funkcióinak kikapcsolására használhatják. Óvatosan kell eljárni az engedély megadásával, mert lehetséges, hogy a komponensek használhatatlanok, inkonzisztensek vagy instabilak lesznek."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"engedélyek megadása vagy visszavonása"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Lehetővé teszi, hogy egy alkalmazás engedélyeket adjon vagy vonjon vissza saját maga vagy más alkalmazás számára. A rosszindulatú alkalmazások olyan funkciókhoz való hozzáféréshez használhatják ezt, amelyeket nem engedélyezett számukra."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"előnyben részesített alkalmazások beállítása"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Lehetővé teszi az alkalmazás számára a preferált alkalmazások módosítását. A rosszindulatú alkalmazások ezáltal észrevétlenül megváltoztathatják a futó alkalmazásokat, személyes adatokat gyűjtve Öntől."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"rendszer globális beállításainak módosítása"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Dátum beállítása"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Beállítás"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Alapértelmezett"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ÚJ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nincs szükség engedélyre"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Elrejtés"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Az összes megjelenítése"</b></string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index ac5d463..08b0f60 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"mengaktifkan atau menonaktifkan komponen apl"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Mengizinkan apl mengubah apakah komponen apl lain diaktifkan atau tidak. Apl berbahaya dapat menggunakan ini untuk menonaktifkan kemampuan tablet yang penting. Izin ini harus digunakan dengan hati-hati karena dapat menjadikan komponen apl tidak dapat digunakan, tidak konsisten, atau tidak stabil."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Mengizinkan apl mengubah apakah komponen apl lain diaktifkan atau tidak. Apl berbahaya dapat menggunakan izin ini untuk menonaktifkan kemampuan ponsel yang penting. Izin ini harus digunakan dengan hati-hati, karena mungkin saja menjadikan komponen apl tidak dapat digunakan, tidak konsisten, atau tidak stabil."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"memberi atau mencabut izin"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Memungkinkan aplikasi memberikan atau mencabut izin khusus untuk aplikasi tersebut atau aplikasi lainnya. Aplikasi berbahaya dapat menggunakannya untuk mengakses fitur yang tidak Anda beri izin."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"menyetel apl yang disukai"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Mengizinkan apl memodifikasi apl pilihan Anda. Apl berbahaya dapat diam-diam mengubah apl yang berjalan, menipu apl yang ada untuk mengumpulkan data pribadi dari Anda."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"ubah setelan sistem global"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Setel tanggal"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Setel"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Bawaan"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"BARU: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Tidak perlu izin"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Sembunyikan"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Tampilkan semua"</b></string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 77733b3..21855eb6 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"attivazione/disattivazione componenti applicazioni"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Consente all\'applicazione di cambiare lo stato di attivazione o disattivazione del componente di un\'altra applicazione. Le applicazioni dannose potrebbero farne uso per disattivare importanti funzionalità del tablet. È necessario prestare attenzione con questa autorizzazione perché è possibile rendere inutilizzabili, incoerenti o instabili i componenti delle applicazioni."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Consente all\'applicazione di cambiare lo stato di attivazione o disattivazione del componente di un\'altra applicazione. Le applicazioni dannose potrebbero farne uso per disattivare importanti funzionalità del telefono. È necessario prestare attenzione con questa autorizzazione perché è possibile rendere inutilizzabili, incoerenti o instabili i componenti delle applicazioni."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"concessione o revoca di autorizzazioni"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Consente a un\'applicazione di concedere o revocare autorizzazioni specifiche per essa o per altre applicazioni. Le applicazioni dannose potrebbero utilizzare questa autorizzazione per accedere a funzioni che non hai concesso loro."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"impostazione applicazioni preferite"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Consente all\'applicazione di modificare le tue applicazioni preferite. Le applicazioni dannose potrebbero cambiare di nascosto le applicazioni che vengono eseguite, facendo in modo che le applicazioni esistenti raccolgano dati riservati su di te."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modifica impostazioni di sistema globali"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Imposta data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Imposta"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predefinito"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUOVA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nessuna autorizzazione richiesta"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Nascondi"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostra tutto"</b></string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a232c69..b009ea1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"הפעלה או השבתה של רכיבי יישומים"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"מאפשר ליישום לשנות את מצב ההפעלה של רכיב ביישום אחר. יישומים זדוניים עלולים להשתמש בכך כדי להשבית יכולות חשובות של הטבלט. יש לנהוג בהרשאה זו בזהירות, מכיוון שהיא יכולה להביא רכיבי יישומים למצב לא שמיש, לא עקבי או לא יציב."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"מאפשר ליישום לשנות את מצב ההפעלה של רכיב ביישום אחר. יישומים זדוניים עלולים להשתמש בכך כדי להשבית יכולות חשובות של הטלפון. יש לנהוג בהרשאה זו בזהירות, מכיוון שהיא יכולה להביא רכיבי יישומים למצב לא שמיש, לא עקבי או לא יציב."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"הענק או בטל הרשאות"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"מאפשר ליישום להעניק או לבטל הרשאות ספציפיות ביחס לעצמו או ליישומים אחרים. יישומים זדוניים עלולים להשתמש באפשרות זו על מנת לקבל גישה לתכונות שלא אישרת להם."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"הגדרת יישומים מועדפים"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"מאפשר ליישום לשנות את היישומים המועדפים עליך. יישומים זדוניים עלולים לשנות בחשאי את היישומים שמופעלים, תוך שידול במרמה של היישומים הקיימים שלך לאסוף ממך נתונים פרטיים."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"שנה את הגדרות המערכת הכלליות"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"הגדר תאריך"</string>
     <string name="date_time_set" msgid="5777075614321087758">"הגדר"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"ברירת מחדל"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"חדש: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"לא דרושים אישורים"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"הסתר"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"הצג הכל"</b></string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 66e847a..9898734 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"アプリのコンポーネントの有効/無効化"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"別のアプリのコンポーネントの有効/無効を変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、タブレットの重要な機能が無効にされる恐れがあります。アプリのコンポーネントが利用できなくなったり、整合性が取れなくなったり、不安定な状態になったりする恐れがあるので許可には注意が必要です。"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"別のアプリのコンポーネントの有効/無効を変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、携帯端末の重要な機能が無効にされる恐れがあります。アプリのコンポーネントが利用できなくなったり、整合性が取れなくなったり、不安定な状態になったりする恐れがあるので許可には注意が必要です。"</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"権限の許可または取り消し"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"このアプリケーションや他のアプリケーションに対して特定の権限を許可したり取り消したりすることをアプリケーションに許可します。悪意のあるアプリケーションがユーザーの許可なく複数の機能にアクセスする恐れがあります。"</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"優先アプリの設定"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"優先アプリを変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、実行中のアプリが密かに変更され、既存のアプリへのなりすましにより非公開データがだまし取られる恐れがあります。"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"システムの全般設定の変更"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"日付設定"</string>
     <string name="date_time_set" msgid="5777075614321087758">"設定"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"端末既定"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NEW: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"権限の許可は必要ありません"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"隠す"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"すべて表示"</b></string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 754f791..5eea0ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"앱 구성요소 사용 또는 사용 안함"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"앱이 다른 앱 구성요소 사용 여부를 변경할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 중요한 태블릿 기능을 중지시킬 수 있습니다. 이 권한을 허용할 경우 앱 구성요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"앱이 다른 앱 구성요소 사용 여부를 변경할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 중요한 휴대전화 기능을 중지시킬 수 있습니다. 이 권한을 허용할 경우 앱 구성요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"권한 승인 또는 취소"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"애플리케이션이 해당 애플리케이션 및 기타 애플리케이션에 대한 특정 권한을 승인 또는 취소하도록 허용합니다. 이 경우 악성 애플리케이션이 승인되지 않은 기능에 액세스할 수 있습니다."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"기본 앱 설정"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"앱이 기본 앱을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 실행되는 앱을 몰래 변경하고 기존 앱으로 위장하여 사용자의 개인 정보를 수집할 수 있습니다."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"전체 시스템 설정 수정"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"날짜 설정"</string>
     <string name="date_time_set" msgid="5777075614321087758">"설정"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"기본값"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"신규: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"숨기기"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"모두 표시"</b></string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 43aa1d3..7ea172f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"įgalinti programos komponentus arba jų neleisti"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Leidžiama programai pakeisti, ar įgalintas kitos programos komponentas, ar ne. Kenkėjiškos programos gali tai naudoti, kad neleistų svarbių planšetinio kompiuterio funkcijų. Šį leidimą reikia naudoti atsargiai, nes programos komponentai gali tapti nenaudojami, nenuoseklūs ar nestabilūs."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Leidžiama programai pakeisti nustatymą, ar įgalintas kitos programos komponentas, ar ne. Kenkėjiškos programos gali tai naudoti, kad neleistų svarbių telefono funkcijų. Šį leidimą reikia naudoti atsargiai, nes programos komponentai gali tapti nenaudojami, nenuoseklūs ar nestabilūs."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"suteikti arba panaikinti leidimus"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Programai leidžiama suteikti arba panaikinti konkrečius savo arba kitų programų leidimus. Tuo pasinaudoję kenkėjiškos programos gali pasiekti funkcijas, kurių pasiekti joms neleidote."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nustatyti pageidaujamas programas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Leidžiama programai keisti jūsų pageidaujamas programas. Kenkėjiškos programos gali nepastebimai pakeisti vykdomas programas, klastodama esamas programas, kad rinktų jūsų privačius duomenis."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"keisti visuotinius sistemos nustatymus"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nustatyti datą"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nustatyti"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Numatytasis"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NAUJAS: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nereikia leidimų"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Slėpti"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Rodyti viską"</b></string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5f84a48..1581341 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"iespējot vai atspējot lietotnes komponentus"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Ļauj lietotnei mainīt to, vai tiek iespējots citas lietotnes komponents. Ļaunprātīgas lietotnes to var izmantot, lai atspējotu svarīgas planšetdatora iespējas. Izmantojiet šo atļauju uzmanīgi, jo pastāv iespēja, ka lietotnes komponenti var kļūt neizmantojami, neatbilstīgi vai nestabili."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Ļauj lietotnei mainīt to, vai tiek iespējots citas lietotnes komponents. Ļaunprātīgas lietotnes to var izmantot, lai atspējotu svarīgas tālruņa iespējas. Izmantojiet šo atļauju uzmanīgi, jo pastāv iespēja, ka lietotnes komponenti var kļūt neizmantojami, neatbilstīgi vai nestabili."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"atļauju piešķiršana vai atsaukšana"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Ļauj lietojumprogrammai piešķirt sev vai citām lietojumprogrammām noteiktas atļaujas un atsaukt tās. Ļaunprātīgas lietojumprogrammas var izmantot šo iespēju, lai piekļūtu funkcijām, kuras neesat atļāvis."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"iestatīt vēlamās lietotnes"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Ļauj lietotnei modificēt jūsu vēlamās lietotnes. Ļaunprātīgas lietotnes var nemanāmi mainīt izmantotās lietotnes, atveidojot esošās lietotnes, lai no jums iegūtu privātus datus."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"pārveidot globālos sistēmas iestatījumus"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Datuma iestatīšana"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Iestatīt"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Noklusējums"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"JAUNA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Atļaujas nav nepieciešamas."</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Slēpt"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Rādīt visu"</b></string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 3f240ab..d3c4885 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"dayakan atau lumpuhkan komponen apl"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Membenarkan apl untuk menukar sama ada komponen apl lain didayakan atau tidak. Apl hasad boleh menggunakannya untuk melumpuhkan keupayaan telefon yang penting. Berhati-hati semasa menggunakan kebenaran ini, kerana hal ini boleh menjadikan komponen apl berada dalam keadaan tidak boleh digunakan, tidak konsisten, atau tidak stabil."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Membenarkan apl untuk menukar sama ada komponen apl lain didayakan atau tidak. Apl hasad boleh menggunakannya untuk melumpuhkan keupayaan telefon yang penting. Berhati-hati semasa menggunakan kebenaran ini, kerana hal ini boleh menjadikan komponen apl berada dalam keadaan tidak boleh digunakan, tidak konsisten, atau tidak stabil."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"tetapkan keutamaan apl"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Membenarkan apl untuk mengubah suai apl pilihan anda. Apl hasad secara diam-diam boleh menukar apl yang dijalankan, menipu apl anda yang sedia ada untuk mengumpul data peribadi daripada anda."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"mengubah suai tetapan sistem global"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Tetapkan tarikh"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Tetapkan"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Lalai"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Tiada kebenaran diperlukan"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Sembunyikan"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Tunjukkan semua"</b></string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 63df5f4..ee58558 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivere eller deaktivere appkomponenter"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Lar appen endre hvorvidt en komponent i en annen app er aktivert eller ikke. Ondsinnede apper kan bruke dette til å deaktivere viktige nettbrettfunksjoner. Denne tillatelsen må brukes med forsiktighet, ettersom det er mulig å få appkomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Lar appen endre hvorvidt en komponent i en annen app er aktivert eller ikke. Ondsinnede apper kan bruke dette til å deaktivere viktige telefonfunksjoner. Denne tillatelsen må brukes med forsiktighet, ettersom det er mulig å få appkomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"gi eller trekke tilbake tillatelser"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Lar et program gi eller trekke tilbake spesielle tillatelser for eget bruk eller for andre programmer. Skadelige programmer kan bruke dette for å få tilgang til funksjoner de ikke skal ha tilgang til."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"angi foretrukne apper"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Lar appen endre de foretrukne appene dine. Ondsinnede apper kan ubemerket endre apper som kjøres, og forfalske eksisterende apper til å samle private data fra deg."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"endre globale systeminnstillinger"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angi dato"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Lagre"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NY: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skjul"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 8e5e8bc..584a7d1 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"componenten van apps in- of uitschakelen"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Hiermee kan de app wijzigen of een component van een andere app wel of niet is ingeschakeld. Schadelijke apps kunnen dit gebruiken om belangrijke tabletfuncties uit te schakelen. U moet voorzichtig omgaan met deze rechten, aangezien het mogelijk is dat onderdelen van apps onbruikbaar, inconsistent of instabiel worden."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Hiermee kan de app wijzigen of een component van een andere app wel of niet is ingeschakeld. Schadelijke apps kunnen dit gebruiken om belangrijke telefoonfuncties uit te schakelen. U moet voorzichtig omgaan met deze rechten, aangezien het mogelijk is dat onderdelen van apps onbruikbaar, inconsistent of instabiel worden."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"rechten verlenen of intrekken"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Toestaan dat een app specifieke rechten aan zichzelf of andere apps verleent of intrekt. Schadelijke apps kunnen dit gebruiken om toegang te krijgen tot functies waartoe u de apps geen toegang heeft gegeven."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"voorkeursapps instellen"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Hiermee kan de app uw voorkeursapps aanpassen. Schadelijke apps kunnen de apps die worden uitgevoerd zonder uw medeweten wijzigen om uw bestaande apps te imiteren en privégegevens van u te verzamelen."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"algemene systeeminstellingen wijzigen"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Datum instellen"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Instellen"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standaard"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NIEUW: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Geen machtigingen vereist"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Verbergen"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Alles weergeven"</b></string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c17da8f..4d0e0fa 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"włączanie lub wyłączanie składników aplikacji"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Pozwala aplikacji na włączenie lub wyłączenie składnika innej aplikacji. Złośliwe aplikacje mogą wykorzystać to uprawnienie do wyłączenia ważnych funkcji tabletu. W przypadku tego uprawnienia należy zachować ostrożność, ponieważ istnieje możliwość wprowadzenia składników aplikacji w stan nieużywalności, niespójności lub niestabilności."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Pozwala aplikacji na włączenie lub wyłączenie składnika innej aplikacji. Złośliwe aplikacje mogą wykorzystać to uprawnienie do wyłączenia ważnych funkcji telefonu. W przypadku tego uprawnienia należy zachować ostrożność, ponieważ istnieje możliwość wprowadzenia składników aplikacji w stan nieużywalności, niespójności lub niestabilności."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"przyznaj lub cofnij uprawnienia"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Pozwala aplikacji na przyznanie lub cofnięcie określonych uprawnień do niej lub do innych aplikacji. Złośliwe aplikacje mogą to wykorzystać, by uzyskać dostęp do nieprzyznanych im funkcji."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ustawianie preferowanych aplikacji"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Pozwala aplikacji na zmianę Twoich preferowanych aplikacji. Złośliwe aplikacje mogą dyskretnie zmienić uruchamiane aplikacje, podszywając się pod dotychczasowe aplikacje w celu zebrania od Ciebie prywatnych danych."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modyfikowanie ogólnych ustawień systemu"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Ustaw datę"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ustaw"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Domyślne"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOWE: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nie są wymagane żadne uprawnienia"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ukryj"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaż wszystko"</b></string>
@@ -1234,7 +1237,7 @@
     <string name="sha1_fingerprint" msgid="7930330235269404581">"Odcisk cyfrowy SHA-1:"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Zobacz wszystkie"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Wybierz działanie"</string>
-    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Udostępnij"</string>
+    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Udostępnij przez"</string>
     <string name="status_bar_device_locked" msgid="3092703448690669768">"Urządzenie zablokowane."</string>
     <string name="list_delimeter" msgid="3975117572185494152">", "</string>
     <string name="sending" msgid="3245653681008218030">"Wysyłanie..."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 74870ff..9f1e053 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"ativar ou desativar componentes da aplicação"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que a aplicação mude a opção de ativar ou não um componente de outra aplicação. As aplicações maliciosas podem utilizar isto para desativar funcionalidades importantes do tablet. É necessário ter cuidado com esta autorização, uma vez que é possível colocar alguns componentes de aplicações num estado inutilizável, inconsistente ou instável."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que a aplicação mude a opção de ativar ou não um componente de outra aplicação. As aplicações maliciosas podem utilizar isto para desativar funcionalidades importantes do telemóvel. É necessário ter cuidado com esta autorização, uma vez que é possível colocar alguns componentes de aplicações num estado inutilizável, inconsistente ou instável."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"conceder ou revogar permissões"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite que uma aplicação conceda ou revogue permissões específicas para si própria ou para outras aplicações. As aplicações maliciosas podem utilizar isto para aceder a funcionalidades para as quais não têm permissão."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"definir aplicações preferidas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite ao aplicativo modificar as suas aplicações preferidas. As aplicações maliciosas podem alterar sem aviso as aplicações que são executadas, espiando as aplicações existentes para recolher os seus dados privados."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar definições globais do sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Definir data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predefinido"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar tudo"</b></string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index efe6833..4826ddf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"ativar ou desativar os componentes do aplicativo"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que o aplicativo ative ou desative um componente de outro aplicativo. Aplicativos maliciosos podem usar esse recurso para desativar recursos importantes do tablet. Tenha cuidado com essa permissão, pois é possível fazer com que os componentes do aplicativo fiquem inutilizáveis, inconsistentes ou instáveis."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que o aplicativo altere se um componente de outro aplicativo está ativado ou não. Aplicativos maliciosos podem usar esse recurso para desativar recursos importantes do telefone. Deve-se tomar cuidado com essa permissão, uma vez que isso pode deixar os componentes do aplicativo inutilizáveis, inconsistentes ou instáveis."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"conceder ou revogar permissões"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite que um aplicativo conceda ou revogue permissões específicas para ele ou outros aplicativos. Aplicativos maliciosos podem usar isso para acessar recursos aos quais você não concedeu permissão."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"definir aplicativos preferidos"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite que o aplicativo modifique seus aplicativos preferidos. Aplicativos maliciosos podem alterar os aplicativos que são executados, falsificando seus aplicativos existentes para coletar seus dados particulares."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar configurações globais do sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Definir data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Padrão"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todas"</b></string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 5b8efa6..c7579de 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -451,6 +451,10 @@
     <skip />
     <!-- no translation found for permdesc_changeComponentState (1827232484416505615) -->
     <skip />
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <!-- no translation found for permlab_setPreferredApplications (8463181628695396391) -->
     <skip />
     <!-- no translation found for permdesc_setPreferredApplications (4973986762241783712) -->
@@ -1474,6 +1478,8 @@
     <skip />
     <string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Naginas permissiuns obligatoricas"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Zuppentar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mussar tut"</b></string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8ffa16e..0890cc0 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activare sau dezactivare a componentelor aplicaţiei"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite aplicaţiei să modifice starea activată sau dezactivată a unei componente a altei aplicaţii. Aplicaţiile rău intenţionate pot să utilizeze această permisiune pentru a dezactiva funcţii importante ale tabletei. Este necesar să utilizaţi cu atenţie această permisiune, deoarece este posibil să aduceţi componentele aplicaţiei într-o stare inutilizabilă, inconsecventă sau instabilă."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite aplicaţiei să modifice starea activată sau dezactivată a unei componente a altei aplicaţii. Aplicaţiile rău intenţionate pot să utilizeze această permisiune pentru a dezactiva funcţii importante ale telefonului. Este necesar să utilizaţi cu atenţie această permisiune, deoarece este posibil să aduceţi componentele aplicaţiei într-o stare inutilizabilă, inconsecventă sau instabilă."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"setare aplicaţii preferate"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite aplicaţiei să modifice aplicaţiile dvs. preferate. Aplicaţiile rău intenţionate pot să modifice fără a vă înştiinţa aplicaţiile care rulează, păcălind aplicaţiile existente să colecteze date private de la dvs."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificare setări sistem globale"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Setaţi data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Setaţi"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Prestabilit"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Nu se solicită nicio permisiune"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ascundeţi"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Afişaţi-le pe toate"</b></string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a6644e5..662a54e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"включение и отключение компонентов приложения"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Приложение сможет включать и отключать компоненты других программ. Вредоносные программы смогут таким образом отключать важные функции планшетного ПК. Используйте это разрешение с особой осторожностью, чтобы случайно не нарушить работу компонентов приложения."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Приложение сможет включать и отключать компоненты других программ. Вредоносные программы смогут таким образом отключать важные функции телефона. Используйте это разрешение с особой осторожностью, чтобы случайно не нарушить работу компонентов приложения."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"предоставление и отзыв разрешений"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Позволяет приложению предоставлять и отзывать разрешения самому себе и другим программам. Вредоносные приложения могут использовать эту функцию для получения прав, которых вы им не предоставляли."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"настройка предпочтительных приложений"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Приложение сможет изменять предпочтительные приложения. Вредоносные программы смогут незаметно изменять запускаемые вами программы и собирать ваши личные данные от имени существующих приложений."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"изменять общие настройки системы"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Настройка даты"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Установить"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"По умолчанию"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВОЕ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Скрыть"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Показать все"</b></string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index df25ac7..98b829f 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"povoliť alebo zakázať súčasti aplikácie"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Umožňuje aplikácii zmeniť to, či je súčasť inej aplikácie povolená alebo zakázaná. Škodlivé aplikácie môžu pomocou tohto nastavenia zakázať dôležité funkcie tabletu. S týmto povolením musíte zaobchádzať opatrne, pretože súčasti aplikácie môžete dostať do nepoužiteľného, nekonzistentného alebo nestabilného stavu."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Umožňuje aplikácii zmeniť to, či je súčasť inej aplikácie povolená alebo zakázaná. Škodlivé aplikácie môžu pomocou tohto nastavenia zakázať dôležité funkcie telefónu. S týmto povolením musíte zaobchádzať opatrne, pretože súčasti aplikácie môžete dostať do nepoužiteľného, nekonzistentného alebo nestabilného stavu."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"povoliť alebo zakázať povolenia"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Umožňuje aplikácii povoliť alebo zakázať konkrétne povolenia pre seba alebo iné aplikácie. Škodlivé aplikácie môžu použiť túto možnosť na pristupovanie k funkciám, ktoré ste im nepovolili."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastaviť preferované aplikácie"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Umožňuje aplikácii zmeniť vaše preferované aplikácie. Škodlivé aplikácie môžu v tichosti zmeniť spustené aplikácie a oklamať existujúce aplikácie, aby zhromažďovali vaše súkromné údaje."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"zmeny globálnych nastavení systému"</string>
@@ -1006,6 +1008,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastaviť dátum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastaviť"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predvolené"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVINKA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nevyžadujú sa žiadne oprávnenia."</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skryť"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Zobraziť všetky"</b></string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 159d504..9d99013 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"omogočanje ali onemogočanje komponent programa"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Programu omogoča, da spremeni, ali je komponenta drugega programa omogočena ali ne. Zlonamerni programi lahko to uporabijo za onemogočanje pomembnih zmožnosti tabličnega računalnika. Pri dodeljevanju dovoljenja je treba biti previden, saj lahko komponente programa nastavite tako, da jih ni mogoče uporabiti, da niso dosledne ali da niso stabilne."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Programu omogoča, da spremeni, ali je komponenta drugega programa omogočena ali ne. Zlonamerni programi lahko to uporabijo za onemogočanje pomembnih zmožnosti telefona. Pri dodeljevanju dovoljenja je treba biti previden, saj lahko komponente programa nastavite tako, da jih ni mogoče uporabiti, da niso dosledne ali da niso stabilne."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"dodeljevanje ali preklic dovoljenj"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Programu omogoča dodeljevanje ali preklic posebnih dovoljenj zanj ali za druge programe. Zlonamerni programi lahko to uporabijo za dostop do funkcij, za katere jim niste dodelili pravic."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastavitev prednostnih programov"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Programu omogoča spreminjanje priljubljenih programov. Zlonamerni programi lahko s tem neopazno spremenijo programe, ki se izvajajo, tako da se izdajajo za obstoječe programe in zbirajo osebne podatke."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"spreminjanje splošnih nastavitev sistema"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastavi datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastavi"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Privzeto"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Ni zahtevanih dovoljenj"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skrij"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaži vse"</b></string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 199d318..a9e7b9b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"омогућавање или онемогућавање компоненти апликације"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дозвољава апликацији да промени да ли је компонента друге апликације омогућена или онемогућена. Злонамерне апликације могу то да искористе да онемогуће важне функције таблета. Треба бити опрезан при додељивању ове дозволе, јер компоненте апликација могу постати неупотребљиве, непоуздане или нестабилне."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дозвољава апликацији да промени да ли је компонента друге апликације омогућена или онемогућена. Злонамерне апликације могу то да искористе да онемогуће важне функције телефона. Треба бити опрезан при додељивању ове дозволе, јер компоненте апликација могу да постану неупотребљиве, непоуздане или нестабилне."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"додела или опозив дозвола"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Омогућава апликацији да додели или опозове посебне дозволе за њу или друге апликације. Злонамерне апликације могу то да користе да би приступале функцијама које им нисте одобрили."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"подешавање жељених апликација"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дозвољава апликацији да мења омиљене апликације. Злонамерне апликације могу без обавештења да промене апликације које су покренуте и да преко њих прикупљају ваше приватне податке."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"измена глобалних подешавања система"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Подешавање датума"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Подеси"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Подразумевано"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВО: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Сакриј"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Прикажи све"</b></string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index be397b0..f780ca4 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivera eller inaktivera appkomponenter"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i pekdatorn. Var försiktig med behörigheten, eftersom appkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Tillåter att appen ändrar inställningen för om en komponent i en annan app ska aktiveras eller inte. Skadliga appar kan använda detta för att inaktivera viktiga funktioner i pekdatorn. Var försiktig med behörigheten, eftersom programkomponenter kan bli oanvändbara, inkonsekventa eller instabila."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ange önskade appar"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Tillåter att appen ändrar dina önskade appar. Skadliga appar kan utan förvarning ändra de appar som körs och kapa dina befintliga appar så att de börjar samla privata uppgifter från dig."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"ändra globala systeminställningar"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Ange datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ställ in"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standardinställning"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Inga behörigheter krävs"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Dölj"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Visa alla"</b></string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 4cb3a3a..c9e4692 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"wezesha au lemeza vijenzi vya programu"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Inaruhusu programu kubadilisha kama kijenzi cha programu nyingine kimewezeshwa au la. Programu hasidi zinaweza kutumia hii kulemeza uwezo muhimu wa kompyuta kibao. Lazina uangalifu utumike kwa ruhusa hii, kwani kuna uwezekano kupata vijenzi vya programu katika hali isiyotumika, isiyowiana, au hali isiyo thabiti."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Inaruhusu programu kubadilisha kama kijenzi cha programu nyingine kimewezeshwa au la. Programu hasidi zinaweza kutumia hii kulemeza mambo muhimu ambayo simu inaweza kufanya. Lazima uangalifu utumike kwa ruhusa hii, kwani kuna uwezekano kufanya vijenzi vya programu kuwa katika hali ya kutotumika, kutokuwa na uwiano, au kutokuwa thabiti."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"kuweka programu zinazopendelewa"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Inaruhusu programu kurekebisha programu unazopendelea. Programu hasidi zinaweza, polepole, kubadilisha programu zinazoendeshwa, na kuzifanya programu ulizo nazo kukusanya data ya kibinafsi kutoka kwako."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"rekebisha mipangilio ya mfumo jumla"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Weka tarehe"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Weka"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Chaguo-msingi"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"Hakuna vibali vinavyohitajika"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ficha"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Onyesha zote"</b></string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 41c56f4..a787a2a 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"เปิดหรือปิดใช้งานคอมโพเนนต์ของแอปพลิเคชัน"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการเปิดใช้งานส่วนประกอบของแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปิดใช้งานความสามารถของแท็บเล็ตที่สำคัญ ต้องใช้ความระมัดระวังสำหรับการอนุญาตนี้ เนื่องจากอาจทำให้ส่วนประกอบต่างๆ ของแอปพลิเคชันไม่สามารถใช้งาน ไม่สอดคล้อง หรือมีสถานะที่ไม่เสถียร"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการเปิดใช้งานส่วนประกอบของแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปิดใช้งานความสามารถที่สำคัญของโทรศัพท์ ต้องใช้ความระมัดระวังสำหรับการอนุญาตนี้เนื่องจากอาจทำให้ส่วนประกอบต่างๆ ของแอปพลิเคชันไม่สามารถใช้งาน ไม่สอดคล้อง หรือมีสถานะที่ไม่เสถียร"</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"อนุญาตหรือยกเลิกการอนุญาต"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"อนุญาตให้แอปพลิเคชันให้หรือยกเลิกการอนุญาตบางอย่างของตัวเองหรือแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การทำงานนี้ในการเข้าถึงคุณลักษณะที่คุณไม่อนุญาต"</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ตั้งค่าแอปพลิเคชันที่ต้องการ"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"อนุญาตให้แอปพลิเคชันแก้ไขแอปพลิเคชันต่างๆ ที่คุณชอบใช้ แอปพลิเคชันที่เป็นอันตรายอาจจะแอบเปลี่ยนแอปพลิเคชันที่กำลังทำงาน ปลอมแปลงเป็นแอปพลิเคชันที่มีอยู่ของคุณเพื่อรวบรวมข้อมูลส่วนบุคคลจากคุณ"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"แก้ไขการตั้งค่าระบบสากล"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"ตั้งวันที่"</string>
     <string name="date_time_set" msgid="5777075614321087758">"ตั้งค่า"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"เริ่มต้น"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ใหม่: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"ซ่อน"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"แสดงทั้งหมด"</b></string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9548fe9..99731af 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"paganahin o huwag paganahin ang mga bahagi ng app"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Pinapayagan ang app na baguhin kung ang bahagi ng isa pang app ay pinagana o hindi. Maaari itong gamitin ng nakakahamak na apps upang huwag paganahin ang mahahalagang kakayahan ng tablet. Dapat na mayroong pag-iingat sa pahintulot na ito, dahil posibleng mailagay ng mga bahagi ng app sa isang hindi nagagamit, pabagu-bago, o hindi matatag na katayuan."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Pinapayagan ang app na baguhin kung ang isang bahagi ng isa pang app ay pinapagana o hindi. Maaari itong gamitin ng nakakahamak na apps upang huwag paganahin ang mga mahalagang kakayahan ng telepono. Dapat na mayroong pag-iingat sa pahintulot na ito, dahil posibleng mailagay ang mga bahagi ng app sa isang hindi nagagamit, pabagu-bago, o hindi matatag na katayuan."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"ibigay o bawiin ang mga pahintulot"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Binibigyang-daan ang isang application na ibigay o bawiin ang mga tukoy na pahintulot para dito o sa iba pang mga application. Maaari itong gamitin ng mga nakakapahamak na application upang i-access ang mga tampok na hindi mo ibinigay sa mga ito."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"itakda ang gustong apps"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Pinapayagan ang app na baguhin ang iyong gustong apps. Maaaring tahimik na baguhin ng nakakahamak na apps ang apps na tumatakbo, na dinadaya ang iyong umiiral nang apps upang mangolekta ng pribadong data mula sa iyo."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"baguhin ang mga setting ng global system"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Itakda ang petsa"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Itakda"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"BAGO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Itago"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Ipakita lahat"</b></string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 6da7fcd..730c69f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"uygulama bileşenlerini etkinleştir veya devre dışı bırak"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Uygulamaya, başka bir uygulamanın bir bileşeninin etkin veya devre dışı olma durumunu değiştirme izni verir. Kötü amaçlı uygulamalar tabletin önemli özelliklerini devre dışı bırakmak için bunu kullanabilir. Uygulama bileşenlerini kullanılamaz, tutarsız ya da kararsız hale getirebileceğinden bu izin ayarlanırken dikkat edilmelidir."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Uygulamaya, başka bir uygulamanın bir bileşeninin etkin veya devre dışı olma durumunu değiştirme izni verir. Kötü amaçlı uygulamalar telefonun önemli özelliklerini devre dışı bırakmak için bunu kullanabilir. Uygulama bileşenlerini kullanılamaz, tutarsız ya da kararsız getirebileceğinden bu izin ayarlanırken dikkat edilmelidir."</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"tercih edilen uygulamaları ayarla"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Uygulamaya, tercih edilen uygulamalarınızı değiştirme izni verir. Kötü amaçlı uygulamalar çalışmakta olan uygulamaları sessizce değiştirip gizli verilerinizi toplamak için mevcut uygulamalarınızı yanlış yönlendirebilir."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"genel sistem ayarlarını değiştir"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Tarihi ayarla"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ayarla"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Varsayılan"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Gizle"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Tümünü göster"</b></string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 3fd6264..4d3c8537 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"вмикати чи вимикати компоненти програми"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дозволяє програмі змінювати статус ввімкнення чи вимкнення компонента іншої програми. Шкідливі програми можуть використовувати це для вимкнення важливих характеристик планшетного ПК. З цим типом дозволу треба поводитися обережно, оскільки компоненти програми можуть стати непридатними, невідповідними чи нестабільними."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дозволяє програмі змінювати статус ввімкнення чи вимкнення компонента іншої програми. Шкідливі програми можуть використовувати це для вимкнення важливих характеристик телефону. З цим типом дозволу треба поводитися обережно, оскільки компоненти програми можуть стати непридатними, невідповідними чи нестабільними."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"надавати або скасовувати дозволи"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Дозволяє програмі надавати або скасовувати певні дозволи для себе чи інших програм. Шкідливі програми можуть використовувати це для доступу до функцій, якого ви їм не надавали."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"установлювати потрібні програми"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дозволяє програмі змінювати ваші вибрані програми. Шкідливі програми можуть непомітно змінювати запущені програми, примушуючи існуючі програми оманливим шляхом збирати ваші особисті дані."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"змін. загальні налашт-ня сист."</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Установити дату"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Застосувати"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"За умовч."</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВИЙ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Сховати"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Показ. всі"</b></string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8503dc5..c9b200d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"bật hoặc tắt cấu phần ứng dụng"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Cho phép ứng dụng thay đổi việc có bật cấu phần của ứng dụng khác hay không. Ứng dụng độc hại có thể sử dụng quyền này để vô hiệu hóa những tính năng quan trọng của máy tính bảng. Phải cẩn trọng khi sử dụng quyền này vì quyền này có thể khiến các cấu phần rơi vào trạng thái không sử dụng được, không đồng nhất hoặc không ổn định."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Cho phép ứng dụng thay đổi việc có bật cấu phần của một ứng dụng khác hay không. Ứng dụng độc hại có thể sử dụng quyền này để tắt những tính năng quan trọng của điện thoại. Phải sử dụng quyền này thận trọng vì có thể khiến các cấu phần của ứng dụng rơi vào trạng thái không thể sử dụng được, không đồng nhất hoặc không ổn định."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"cấp hoặc thu hồi quyền"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Cho phép ứng dụng cấp hoặc thu hồi quyền cụ thể đối với ứng dụng đó hoặc các ứng dụng khác. Các ứng dụng độc hại có thể lợi dụng điều này để truy cập các tính năng mà bạn không cấp."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"đặt ứng dụng ưa thích"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Cho phép ứng dụng sửa đổi ứng dụng ưa thích của bạn. Ứng dụng độc hại có thể ngầm thay đổi các ứng dụng đã được chạy, giả mạo các ứng dụng hiện có để thu thập dữ liệu cá nhân của bạn."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"sửa đổi cài đặt hệ thống chung"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Đặt ngày"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Đặt"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Mặc định"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"MỚI: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Không yêu cầu quyền"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ẩn"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Hiển thị tất cả"</b></string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index bc74518..c2d59e9 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"启用或停用应用程序组件"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"允许应用程序启用或停用其他应用程序的组件。恶意应用程序可能借此停用重要的平板电脑功能。请务必谨慎使用此权限,因为这可能导致某些应用程序组件处于无法使用、不一致或不稳定的状态。"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"允许应用程序启用或停用其他应用程序的组件。恶意应用程序可能借此停用重要的手机功能。请务必谨慎使用此权限,因为这可能导致某些应用程序组件进入无法使用、不一致或不稳定的状态。"</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"设置首选应用程序"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"允许应用程序修改您的首选应用程序。恶意应用程序可能会在后台更改运行的应用程序,欺骗您现有的应用程序,以收集您的私人数据。"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"修改全局系统设置"</string>
@@ -765,7 +769,7 @@
     <string name="permlab_serialPort" msgid="546083327654631076">"访问串行端口"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"允许持有人使用 SerialManager API 访问串行端口。"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string>
-    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允许持有者通过界面访问内容提供程序。普通应用程序绝不需要此权限。"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允许持有者通过界面访问内容提供程序。普通应用绝不需要此权限。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"暂不保存"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"记住"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"设置日期"</string>
     <string name="date_time_set" msgid="5777075614321087758">"设置"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"默认"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"不需要任何权限"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"隐藏"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"全部显示"</b></string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5c89004..18d0836 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -313,6 +313,10 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"啟用或停用應用程式元件"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"允許應用程式啟用或停用其他應用程式的元件。請注意,惡意應用程式可能利用此功能停用重要的平板電腦功能。這項權限可能導致應用程式元件無法使用、不一致或不穩定,請務必謹慎使用。"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"允許應用程式啟用或停用其他應用程式的元件。請注意,惡意應用程式可能利用此功能停用重要的手機功能。這項權限可能導致應用程式元件無法使用、不一致或不穩定,請務必謹慎使用。"</string>
+    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
+    <skip />
+    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
+    <skip />
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"設定偏好的應用程式"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"允許應用程式修改您偏好的應用程式。請注意,惡意應用程式可能利用此功能擅自竄改執行的應用程式,並冒充現有的程式收集您的私人資料。"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"編輯全域系統設定"</string>
@@ -1004,6 +1008,8 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"日期設定"</string>
     <string name="date_time_set" msgid="5777075614321087758">"設定"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"預設值"</string>
+    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
+    <skip />
     <string name="no_permissions" msgid="7283357728219338112">"無須許可"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>" 隱藏"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"顯示全部"</b></string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 282fb91..6d61260 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -70,11 +70,11 @@
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Isevisi ayilungiselelwe."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Ngeke ukwazi ukuguqul izilungiselelo zemininingwane yoshayayo."</string>
     <string name="RestrictedChangedTitle" msgid="5592189398956187498">"Ukufinyelela okuvinjelwe kushintshiwe"</string>
-    <string name="RestrictedOnData" msgid="8653794784690065540">"Insizakalo yedatha ivaliwe."</string>
-    <string name="RestrictedOnEmergency" msgid="6581163779072833665">"Insizakalo ephuthumayo ivimbelwe."</string>
-    <string name="RestrictedOnNormal" msgid="4953867011389750673">"Insizakalo yezwi ivimbelwe."</string>
+    <string name="RestrictedOnData" msgid="8653794784690065540">"Isevisi yedatha ivaliwe."</string>
+    <string name="RestrictedOnEmergency" msgid="6581163779072833665">"Isevisi ephuthumayo ivimbelwe."</string>
+    <string name="RestrictedOnNormal" msgid="4953867011389750673">"Isevisi yezwi ivimbelwe."</string>
     <string name="RestrictedOnAllVoice" msgid="3396963652108151260">"Wonke amasevisi Wezwi avimbelwe."</string>
-    <string name="RestrictedOnSms" msgid="8314352327461638897">"Insizakalo ye-SMS ivaliwe."</string>
+    <string name="RestrictedOnSms" msgid="8314352327461638897">"Isevisi ye-SMS ivaliwe."</string>
     <string name="RestrictedOnVoiceData" msgid="996636487106171320">"Amasevisi Wezwi/Idatha avimbelwe."</string>
     <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Amasevisi Wezwi/SMS avimbelwe."</string>
     <string name="RestrictedOnAll" msgid="5643028264466092821">"Wonke amasevisi Wezwi/Idatha/SMS avimbelwe."</string>
@@ -276,7 +276,7 @@
     <string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ivumela umnini ukuthi abophele kwissekelo esingaphezulu sesevisi ye-Vpm. Ayidingakeli izinsiza ezejwayelekile."</string>
     <string name="permlab_bindWallpaper" msgid="8716400279937856462">"hlanganisa kwiphephadonga"</string>
     <string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lwephephadonga. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
-    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bophezela kube insizakalo yesinqunjana"</string>
+    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bophezela kube isevisi yesinqunjana"</string>
     <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lensizakalo yesinqunjwana. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
     <string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"xhumana nomphathi wedivaysi"</string>
     <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Ivumela ummeli ukuthumela okuqukethwe kumphathi wedivaysi. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
@@ -313,6 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"vumela noma vimbela izingxenye zensiza"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Ivumela ukuthi insiza iguqule ukuthi okuqukethwe kwenye insiza kuyasebenza noma cha. Izinsiza ezinobungozi zingasebenzisa lokhu ukwenza ukuthi izinto ezisemqoka ekhompyutheni yepeni zingasebenzi. Kufanele kuqashelwe uma kukhishwa lemvume njengoba kungenzeka kwenze izinto zensiza zibe sesimweni esingazinzile, nesiguquguqukayo."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Ivumela ukuthi insiza iguqule ukuthi okuqukethwe kwenye insiza kuyasebenza noma cha. Izinsiza ezinobungozi zingasebenzisa lokhu ukwenza ukuthi izinto ezisemqoka ocingweni zingasebenzi. Kufanele kuqashelwe uma kukhishwa lemvume njengoba kungenzeka kwenze izinto zensiza zibe sesmweni esingazinzile, nesiguquguqukayo."</string>
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"nika noma buyisa izimvume"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Ivumela izinhlelo zokusebenza ukunika noma ukubuyisa izimvume ezithile zayo noma ezinye izinhlelo lokusebenza. Izinhlelo zokusebenza ezingalungile zingasebenzisa lokhu ukufinyelela izici ongazinikanga zona."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"setha izinsiza ezincamelwayo"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Ivuela insiza ukuthi iguqule izinsiza ezincanyelwayo. Izinsiza ezinobungozi zingashintsha izinsiz buthule ezisebenzyo okwenza ukuthi izinsiza zakho ezikhona zingasebenzi ukuthola ze zithole imininingwane yakho eyimfihlo."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"guqula izilungiselelo zohlelo jikelele"</string>
@@ -1004,6 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Setha idethi"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Hlela"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Okuzenzakalelayo"</string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"Okusha: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Ayikho imvume edingekayo"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Fihla "</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Bonisa konke"</b></string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9375730..f31deef 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3376,9 +3376,13 @@
     </declare-styleable>
 
     <declare-styleable name="DatePicker">
-        <!-- The first year (inclusive), for example "1940". -->
+        <!-- The first year (inclusive), for example "1940".
+             {@deprecated Use minDate instead.}
+         -->
         <attr name="startYear" format="integer" />
-        <!-- The last year (inclusive), for example "2010". -->
+        <!-- The last year (inclusive), for example "2010".
+             {@deprecated Use maxDate instead.}
+         -->
         <attr name="endYear" format="integer" />
         <!-- Whether the spinners are shown. -->
         <attr name="spinnersShown" format="boolean" />
@@ -3388,8 +3392,8 @@
         <attr name="minDate" format="string" />
         <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="maxDate" format="string" />
-        <!-- @hide The layout of the time picker. -->
-        <attr name="layout" />
+        <!-- @hide The layout of the date picker. -->
+        <attr name="internalLayout" format="reference"  />
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
@@ -3593,15 +3597,15 @@
         <attr name="shownWeekCount" format="integer"/>
         <!-- The background color for the selected week. -->
         <attr name="selectedWeekBackgroundColor" format="color|reference" />
-        <!-- The color for the dates of the selected month. -->
+        <!-- The color for the dates of the focused month. -->
         <attr name="focusedMonthDateColor" format="color|reference" />
         <!-- The color for the dates of an unfocused month. -->
         <attr name="unfocusedMonthDateColor" format="color|reference" />
         <!-- The color for the week numbers. -->
         <attr name="weekNumberColor" format="color|reference" />
-        <!-- The color for the sepatator line between weeks. -->
+        <!-- The color for the separator line between weeks. -->
         <attr name="weekSeparatorLineColor" format="color|reference" />
-        <!-- Drawable for the vertical bar shown at the beggining and at the end of a selected date. -->
+        <!-- Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->
         <attr name="selectedDateVerticalBar" format="reference" />
         <!-- The text appearance for the week day abbreviation of the calendar header. -->
         <attr name="weekDayTextAppearance" format="reference" />
@@ -3619,20 +3623,18 @@
         <!-- @hide The height of the selection divider. -->
         <attr name="selectionDividerHeight" format="dimension" />
         <!-- @hide The min height of the NumberPicker. -->
-        <attr name="minHeight" />
+        <attr name="internalMinHeight" format="dimension" />
         <!-- @hide The max height of the NumberPicker. -->
-        <attr name="maxHeight" />
+        <attr name="internalMaxHeight" format="dimension" />
         <!-- @hide The min width of the NumberPicker. -->
-        <attr name="minWidth" />
+        <attr name="internalMinWidth" format="dimension" />
         <!-- @hide The max width of the NumberPicker. -->
-        <attr name="maxWidth" />
-        <!-- @hide The max width of the NumberPicker. -->
-        <attr name="maxWidth" />
+        <attr name="internalMaxWidth" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="TimePicker">
         <!-- @hide The layout of the time picker. -->
-        <attr name="layout" />
+        <attr name="internalLayout" />
     </declare-styleable>
 
     <!-- ========================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1eab01a..eaf9c8c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -505,6 +505,7 @@
             0 - Nothing
             1 - Recent apps dialog
             2 - Recent apps view in SystemUI
+            3 - Voice search
          This needs to match the constants in
          policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
     -->
@@ -645,9 +646,46 @@
     <bool translatable="false" name="skip_restoring_network_selection">false</bool>
 
     <!-- Maximum number of database connections opened and managed by framework layer
-         to handle queries on each database. -->
+         to handle queries on each database when using Write-Ahead Logging. -->
     <integer name="db_connection_pool_size">4</integer>
 
+    <!-- The default journal mode to use use when Write-Ahead Logging is not active.
+         Choices are: OFF, DELETE, TRUNCATE, PERSIST and MEMORY.
+         PERSIST may improve performance by reducing how often journal blocks are
+         reallocated (compared to truncation) resulting in better data block locality
+         and less churn of the storage media. -->
+    <string name="db_default_journal_mode">PERSIST</string>
+
+    <!-- Maximum size of the persistent journal file in bytes.
+         If the journal file grows to be larger than this amount then SQLite will
+         truncate it after committing the transaction. -->
+    <integer name="db_journal_size_limit">524288</integer>
+
+    <!-- The database synchronization mode when using the default journal mode.
+         FULL is safest and preserves durability at the cost of extra fsyncs.
+         NORMAL also preserves durability in non-WAL modes and uses checksums to ensure
+         integrity although there is a small chance that an error might go unnoticed.
+         Choices are: FULL, NORMAL, OFF. -->
+    <string name="db_default_sync_mode">FULL</string>
+
+    <!-- The database synchronization mode when using Write-Ahead Logging.
+         FULL is safest and preserves durability at the cost of extra fsyncs.
+         NORMAL sacrifices durability in WAL mode because syncs are only performed before
+         and after checkpoint operations.  If checkpoints are infrequent and power loss
+         occurs, then committed transactions could be lost and applications might break.
+         Choices are: FULL, NORMAL, OFF. -->
+    <string name="db_wal_sync_mode">FULL</string>
+
+    <!-- The Write-Ahead Log auto-checkpoint interval in database pages (typically 1 to 4KB).
+         The log is checkpointed automatically whenever it exceeds this many pages.
+         When a database is reopened, its journal mode is set back to the default
+         journal mode, which may cause a checkpoint operation to occur.  Checkpoints
+         can also happen at other times when transactions are committed.
+         The bigger the WAL file, the longer a checkpoint operation takes, so we try
+         to keep the WAL file relatively small to avoid long delays.
+         The size of the WAL file is also constrained by 'db_journal_size_limit'. -->
+    <integer name="db_wal_autocheckpoint">100</integer>
+
     <!-- Max space (in MB) allocated to DownloadManager to store the downloaded
          files if they are to be stored in DownloadManager's data dir,
          which typically is /data/data/com.android.providers.downloads/files -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 94a671d..39c6a18 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -250,6 +250,8 @@
   <java-symbol type="integer" name="config_wifi_framework_scan_interval" />
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
   <java-symbol type="integer" name="db_connection_pool_size" />
+  <java-symbol type="integer" name="db_journal_size_limit" />
+  <java-symbol type="integer" name="db_wal_autocheckpoint" />
   <java-symbol type="integer" name="max_action_buttons" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
@@ -438,6 +440,9 @@
   <java-symbol type="string" name="day_of_week_shortest_thursday" />
   <java-symbol type="string" name="day_of_week_shortest_tuesday" />
   <java-symbol type="string" name="day_of_week_shortest_wednesday" />
+  <java-symbol type="string" name="db_default_journal_mode" />
+  <java-symbol type="string" name="db_default_sync_mode" />
+  <java-symbol type="string" name="db_wal_sync_mode" />
   <java-symbol type="string" name="decline" />
   <java-symbol type="string" name="default_permission_group" />
   <java-symbol type="string" name="default_text_encoding" />
@@ -1950,7 +1955,9 @@
   <public type="attr" name="flipInterval" id="0x01010179" />
   <public type="attr" name="fillViewport" id="0x0101017a" />
   <public type="attr" name="prompt" id="0x0101017b" />
+  <!-- {@deprecated Use minDate instead.} -->
   <public type="attr" name="startYear" id="0x0101017c" />
+  <!-- {@deprecated Use maxDate instead.} -->
   <public type="attr" name="endYear" id="0x0101017d" />
   <public type="attr" name="mode" id="0x0101017e" />
   <public type="attr" name="layout_x" id="0x0101017f" />
@@ -3521,6 +3528,8 @@
 
   <public type="attr" name="textDirection"/>
 
+  <public type="attr" name="layoutDirection" />
+
   <public type="attr" name="paddingStart"/>
   <public type="attr" name="paddingEnd"/>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f548165..c8df649 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2263,6 +2263,10 @@
          content providers from outside an ApplicationThread. [CHAR LIMIT=NONE] -->
     <string name="permdesc_accessContentProvidersExternally">Allows the holder to access content
      providers from the shell. Should never be needed for normal apps.</string>
+    <!-- Title of an application permission which allows the application to suggest that now is a bad time to reboot the device in order to apply an OTA.  [CHAR LIMIT=40] -->
+    <string name="permlab_updateLock">discourage automatic device updates</string>
+    <!-- Description of an application permission which allows the application to suggest that now is a bad time to reboot the device in order to apply an OTA.  [CHAR LIMIT=NONE] -->
+    <string name="permdesc_updateLock">Allows the holder to offer information to the system about when would be a good time for a noninteractive reboot to upgrade the device.</string>
 
     <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
     <string name="save_password_message">Do you want the browser to remember this password?</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 610bad8..569be90 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -528,11 +528,11 @@
     </style>
 
     <style name="Widget.TimePicker">
-        <item name="android:layout">@android:layout/time_picker</item>
+        <item name="android:internalLayout">@android:layout/time_picker</item>
     </style>
 
     <style name="Widget.DatePicker">
-        <item name="android:layout">@android:layout/date_picker</item>
+        <item name="android:internalLayout">@android:layout/date_picker</item>
         <item name="android:calendarViewShown">false</item>
     </style>
 
@@ -1656,16 +1656,16 @@
         <item name="android:flingable">true</item>
         <item name="android:selectionDivider">@android:drawable/numberpicker_selection_divider</item>
         <item name="android:selectionDividerHeight">2dip</item>
-        <item name="android:minWidth">48dip</item>
-        <item name="android:maxHeight">200dip</item>
+        <item name="android:internalMinWidth">48dip</item>
+        <item name="android:internalMaxHeight">200dip</item>
     </style>
 
     <style name="Widget.Holo.TimePicker" parent="Widget.TimePicker">
-        <item name="android:layout">@android:layout/time_picker_holo</item>
+        <item name="android:internalLayout">@android:layout/time_picker_holo</item>
     </style>
 
     <style name="Widget.Holo.DatePicker" parent="Widget.DatePicker">
-        <item name="android:layout">@android:layout/date_picker_holo</item>
+        <item name="android:internalLayout">@android:layout/date_picker_holo</item>
         <item name="android:calendarViewShown">true</item>
     </style>
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index d375d4c..259f15f 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -94,6 +94,7 @@
      * Control Wifi States
      */
     public WifiManager mWifiManager;
+    public WifiManager.Channel mChannel;
 
     /*
      * Verify connectivity state
@@ -240,7 +241,7 @@
         mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
         // Get an instance of WifiManager
         mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
-        mWifiManager.asyncConnect(this, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
 
         initializeNetworkStates();
 
@@ -594,7 +595,14 @@
                         log("found " + ssid + " in the scan result list");
                         log("retry: " + retry);
                         foundApInScanResults = true;
-                        mWifiManager.connectNetwork(config);
+                        mWifiManager.connect(mChannel, config,
+                                new WifiManager.ActionListener() {
+                                    public void onSuccess() {
+                                    }
+                                    public void onFailure(int reason) {
+                                        log("connect failure " + reason);
+                                    }
+                                });
                         break;
                    }
                 }
@@ -641,7 +649,13 @@
         for (WifiConfiguration wifiConfig: wifiConfigList) {
             log("remove wifi configuration: " + wifiConfig.networkId);
             int netId = wifiConfig.networkId;
-            mWifiManager.forgetNetwork(netId);
+            mWifiManager.forget(mChannel, netId, new WifiManager.ActionListener() {
+                    public void onSuccess() {
+                    }
+                    public void onFailure(int reason) {
+                        log("Failed to forget " + reason);
+                    }
+                });
         }
         return true;
     }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index d33a445..8d73bc0 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -63,6 +63,7 @@
     private ConnectivityManagerTestActivity mAct;
     private ConnectivityManagerTestRunner mRunner;
     private WifiManager mWifiManager = null;
+    private WifiManager.Channel mChannel;
     private Set<WifiConfiguration> enabledNetworks = null;
 
     public WifiConnectionTest() {
@@ -76,7 +77,8 @@
         mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE);
 
         mAct = getActivity();
-        mWifiManager.asyncConnect(mAct, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mAct, mAct.getMainLooper(), null);
+
         networks = mAct.loadNetworkConfigurations();
         if (DEBUG) {
             printNetworkConfigurations();
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index 8d778c4..c3cc7c5 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -59,7 +59,7 @@
     private static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; // 10 seconds
     private static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
     public static final int SHORT_TIMEOUT = 5 * 1000;
-    public static final int LONG_TIMEOUT = 120 * 1000; // 2 minutes
+    public static final int LONG_TIMEOUT = 5 * 60 * 1000; // 5 minutes
     private ConnectivityReceiver mConnectivityReceiver = null;
     private WifiReceiver mWifiReceiver = null;
     private DownloadReceiver mDownloadReceiver = null;
@@ -74,6 +74,7 @@
     private int mWifiState;
     private NetworkInfo mWifiNetworkInfo;
     private WifiManager mWifiManager;
+    private WifiManager.Channel mChannel;
     private Context mContext;
     // Verify connectivity state
     private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
@@ -114,7 +115,7 @@
 
         // Get an instance of WifiManager
         mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
-        mWifiManager.asyncConnect(mContext, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
 
         mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
 
@@ -458,7 +459,13 @@
                 if (mNetworkInfo == null) {
                     Log.v(LOG_TAG, "Do not have networkInfo! Force fetch of network info.");
                     mNetworkInfo = mCM.getActiveNetworkInfo();
-                    Assert.assertNotNull(mNetworkInfo);
+                }
+                // Still null after force fetch? Maybe the network did not have time to be brought
+                // up yet.
+                if (mNetworkInfo == null) {
+                    Log.v(LOG_TAG, "Failed to force fetch networkInfo. " +
+                            "The network is still not ready. Wait for the next broadcast");
+                    continue;
                 }
                 if ((mNetworkInfo.getType() != networkType) ||
                         (mNetworkInfo.getState() != expectedState)) {
@@ -567,7 +574,14 @@
                         Log.v(LOG_TAG, "Found " + ssid + " in the scan result list.");
                         Log.v(LOG_TAG, "Retry: " + retry);
                         foundApInScanResults = true;
-                        mWifiManager.connectNetwork(config);
+                        mWifiManager.connect(mChannel, config, new WifiManager.ActionListener() {
+                                public void onSuccess() {
+                                }
+                                public void onFailure(int reason) {
+                                    Log.e(LOG_TAG, "connect failed " + reason);
+                                }
+                            });
+
                         break;
                     }
                 }
@@ -614,7 +628,13 @@
         for (WifiConfiguration wifiConfig: wifiConfigList) {
             Log.v(LOG_TAG, "Remove wifi configuration: " + wifiConfig.networkId);
             int netId = wifiConfig.networkId;
-            mWifiManager.forgetNetwork(netId);
+            mWifiManager.forget(mChannel, netId, new WifiManager.ActionListener() {
+                    public void onSuccess() {
+                    }
+                    public void onFailure(int reason) {
+                        Log.e(LOG_TAG, "forget failed " + reason);
+                    }
+                });
         }
         return true;
     }
@@ -723,4 +743,4 @@
         }
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/util/LocaleUtilTest.java b/core/tests/coretests/src/android/util/LocaleUtilTest.java
index ff3d539..0ca6043 100644
--- a/core/tests/coretests/src/android/util/LocaleUtilTest.java
+++ b/core/tests/coretests/src/android/util/LocaleUtilTest.java
@@ -22,7 +22,8 @@
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetNew;
 
-import static android.util.LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
 
 public class LocaleUtilTest extends AndroidTestCase {
 
@@ -32,168 +33,168 @@
         args = {Locale.class}
     )
     public void testGetLayoutDirectionFromLocale() {
-        assertEquals(TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
                 LocaleUtil.getLayoutDirectionFromLocale(null));
 
-        assertEquals(TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ENGLISH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CANADA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.FRANCE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.FRENCH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.GERMAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.GERMANY));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ITALIAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ITALY));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.UK));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.US));
 
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ROOT));
 
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CHINA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CHINESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.JAPAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.JAPANESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.KOREA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.KOREAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.PRC));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.TAIWAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
 
         Locale locale = new Locale("ar");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "AE");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "BH");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "DZ");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "EG");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "IQ");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "JO");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "KW");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LB");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LY");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "MA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "OM");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "QA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SD");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SY");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "TN");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "YE");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("fa");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "IR");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("iw");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("iw", "IL");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he", "IL");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("pa_Arab");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("pa_Arab", "PK");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("ps");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ps", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("ur");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ur", "IN");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ur", "PK");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("uz_Arab");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("uz_Arab", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         // Locale without a real language
         locale = new Locale("zz");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
     }
 }
diff --git a/core/tests/coretests/src/android/webkit/ZoomManagerTest.java b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
index 1c9defe..7e0e0b2 100644
--- a/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
+++ b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
@@ -24,8 +24,9 @@
     @Override
     public void setUp() {
         WebView webView = new WebView(this.getContext());
-        CallbackProxy callbackProxy = new CallbackProxy(this.getContext(), webView);
-        zoomManager = new ZoomManager(webView, callbackProxy);
+        WebViewClassic webViewClassic = WebViewClassic.fromWebView(webView);
+        CallbackProxy callbackProxy = new CallbackProxy(this.getContext(), webViewClassic);
+        zoomManager = new ZoomManager(webViewClassic, callbackProxy);
 
         zoomManager.init(1.00f);
     }
diff --git a/data/fonts/AndroidEmoji.ttf b/data/fonts/AndroidEmoji.ttf
index d543f75..4c017d5 100644
--- a/data/fonts/AndroidEmoji.ttf
+++ b/data/fonts/AndroidEmoji.ttf
Binary files differ
diff --git a/docs/html/design/building-blocks/buttons.html b/docs/html/design/building-blocks/buttons.html
index cc43fcb..9f9652f9 100644
--- a/docs/html/design/building-blocks/buttons.html
+++ b/docs/html/design/building-blocks/buttons.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -110,7 +114,8 @@
   <img src="../static/content/buttons_basic.png">
 </div>
 
-<h2>Basic Buttons</h2>
+<h2 id="basic">Basic Buttons</h2>
+
 <p>Basic buttons are traditional buttons with borders and background. Android supports two styles for
 basic buttons: default and small. Default buttons have slightly larger font size and are optimized
 for display outside of form content. Small buttons are intended for display alongside other content.
@@ -131,7 +136,8 @@
   </div>
 </div>
 
-<h2>Borderless Buttons</h2>
+<h2 id="borderless">Borderless Buttons</h2>
+
 <p>Borderless buttons resemble basic buttons except that they have no borders or background. You can
 use borderless buttons with both icons and text. Borderless buttons are visually more lightweight
 than basic buttons and integrate nicely with other content.</p>
diff --git a/docs/html/design/building-blocks/dialogs.html b/docs/html/design/building-blocks/dialogs.html
index fc00780..f03a57a 100644
--- a/docs/html/design/building-blocks/dialogs.html
+++ b/docs/html/design/building-blocks/dialogs.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/grid-lists.html b/docs/html/design/building-blocks/grid-lists.html
index b4cfdcb..3f60216 100644
--- a/docs/html/design/building-blocks/grid-lists.html
+++ b/docs/html/design/building-blocks/grid-lists.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -159,7 +163,7 @@
 </div>
 
 
-<h2 id="with_labels">Grid List with Labels</h2>
+<h2 id="with-labels">Grid List with Labels</h2>
 
 <p>Use labels to display additional contextual information for your grid list items.</p>
 
diff --git a/docs/html/design/building-blocks/index.html b/docs/html/design/building-blocks/index.html
index c99d85c..029cabf 100644
--- a/docs/html/design/building-blocks/index.html
+++ b/docs/html/design/building-blocks/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/lists.html b/docs/html/design/building-blocks/lists.html
index 914ae9e..dfd13d9 100644
--- a/docs/html/design/building-blocks/lists.html
+++ b/docs/html/design/building-blocks/lists.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/pickers.html b/docs/html/design/building-blocks/pickers.html
index 4c95a9f..fc9989c 100644
--- a/docs/html/design/building-blocks/pickers.html
+++ b/docs/html/design/building-blocks/pickers.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -123,7 +127,7 @@
   </div>
 </div>
 
-<h2>Date and time pickers</h2>
+<h2 id="date-time">Date and time pickers</h2>
 
 <p>Android provides these as ready-to-use dialogs. Each picker is a dialog with a set of controls for
 entering the parts of the date (month, day, year) or time (hour, minute, AM/PM). Using these in your
diff --git a/docs/html/design/building-blocks/progress.html b/docs/html/design/building-blocks/progress.html
index 7aae913..32183bc 100644
--- a/docs/html/design/building-blocks/progress.html
+++ b/docs/html/design/building-blocks/progress.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -105,7 +109,8 @@
 
 <p>When an operation of interest to the user is taking place over a relatively long period of time,
 provide visual feedback that it's still happening and in the process of being completed.</p>
-<h2>Progress</h2>
+<h2 id="progress">Progress</h2>
+
 <p>If you know the percentage of the operation that has been completed, use a determinate progress bar
 to give the user a sense of how much longer it will take.</p>
 
diff --git a/docs/html/design/building-blocks/scrolling.html b/docs/html/design/building-blocks/scrolling.html
index 3f1167c..3599a97 100644
--- a/docs/html/design/building-blocks/scrolling.html
+++ b/docs/html/design/building-blocks/scrolling.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -104,7 +108,8 @@
 
 <p>Scrolling allows the user to navigate to content in the overflow using a swipe gesture. The
 scrolling speed is proportional to the speed of the gesture.</p>
-<h2>Scroll Indicator</h2>
+<h2 id="indicator">Scroll Indicator</h2>
+
 <p>Appears during scrolling to indicate what portion of the content is currently in view.</p>
 
 <div class="framed-galaxynexus-land-span-13">
@@ -118,7 +123,8 @@
   <div class="video-instructions">&nbsp;</div>
 </div>
 
-<h2>Index Scrolling</h2>
+<h2 id="index-scrolling">Index Scrolling</h2>
+
 <p>In addition to traditional scrolling, a long alphabetical list can also offer index scrolling: a way
 to quickly navigate to the items that begin with a particular letter. With index scrolling, a scroll
 indicator appears even when the user isn't scrolling. Touching or dragging it causes the current
diff --git a/docs/html/design/building-blocks/seek-bars.html b/docs/html/design/building-blocks/seek-bars.html
index 84cf5d2..aef1823 100644
--- a/docs/html/design/building-blocks/seek-bars.html
+++ b/docs/html/design/building-blocks/seek-bars.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/spinners.html b/docs/html/design/building-blocks/spinners.html
index bf21fe8..5ef9d04 100644
--- a/docs/html/design/building-blocks/spinners.html
+++ b/docs/html/design/building-blocks/spinners.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/switches.html b/docs/html/design/building-blocks/switches.html
index 3d7bc9c..09af540 100644
--- a/docs/html/design/building-blocks/switches.html
+++ b/docs/html/design/building-blocks/switches.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -104,7 +108,8 @@
 
 <p>Switches allow the user to select options. There are three kinds of switches: checkboxes, radio
 buttons, and on/off switches.</p>
-<h2>Checkboxes</h2>
+<h2 id="checkboxes">Checkboxes</h2>
+
 <p>Checkboxes allow the user to select multiple options from a set. Avoid using a single checkbox to
 turn an option off or on. Instead, use an on/off switch.</p>
 
@@ -112,7 +117,8 @@
   <img src="../static/content/switches_checkboxes.png">
 </div>
 
-<h2>Radio Buttons</h2>
+<h2 id="radio-buttons">Radio Buttons</h2>
+
 <p>Radio buttons allow the user to select one option from a set. Use radio buttons for exclusive
 selection if you think that the user needs to see all available options side-by-side. Otherwise,
 consider a spinner, which uses less space.</p>
@@ -121,7 +127,8 @@
   <img src="../static/content/switches_radios.png">
 </div>
 
-<h2>On/off Switches</h2>
+<h2 id="switches">On/off Switches</h2>
+
 <p>On/off switches toggle the state of a single settings option.</p>
 
 <div style="text-align: center">
diff --git a/docs/html/design/building-blocks/tabs.html b/docs/html/design/building-blocks/tabs.html
index c094cce..d4b0e52 100644
--- a/docs/html/design/building-blocks/tabs.html
+++ b/docs/html/design/building-blocks/tabs.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/text-fields.html b/docs/html/design/building-blocks/text-fields.html
index 6496fa5..b9ec42d 100644
--- a/docs/html/design/building-blocks/text-fields.html
+++ b/docs/html/design/building-blocks/text-fields.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -141,7 +145,7 @@
   </div>
 </div>
 
-<h2>Text Selection</h2>
+<h2 id="text-selection">Text Selection</h2>
 
 <p>Users can select any word in a text field with a long press. This action triggers a text selection
 mode that facilitates extending the selection or choosing an action to perform on the selected text.
diff --git a/docs/html/design/downloads/index.html b/docs/html/design/downloads/index.html
new file mode 100644
index 0000000..f910b29
--- /dev/null
+++ b/docs/html/design/downloads/index.html
@@ -0,0 +1,278 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>
+
+Android Design - Downloads
+    </title>
+    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
+    <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,mediumitalic">
+    <link rel="stylesheet" href="../static/yui-3.3.0-reset-min.css">
+    <link rel="stylesheet" href="../static/default.css">
+
+  </head>
+  <body>
+
+    <div id="page-container">
+
+      <div id="page-header"><a href="../index.html">Android Design</a></div>
+
+      <div id="main-row">
+
+        <ul id="nav">
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../index.html">Get Started</a></div>
+            <ul>
+              <li><a href="../get-started/creative-vision.html">Creative Vision</a></li>
+              <li><a href="../get-started/principles.html">Design Principles</a></li>
+              <li><a href="../get-started/ui-overview.html">UI Overview</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../style/index.html">Style</a></div>
+            <ul>
+              <li><a href="../style/devices-displays.html">Devices and Displays</a></li>
+              <li><a href="../style/themes.html">Themes</a></li>
+              <li><a href="../style/touch-feedback.html">Touch Feedback</a></li>
+              <li><a href="../style/metrics-grids.html">Metrics and Grids</a></li>
+              <li><a href="../style/typography.html">Typography</a></li>
+              <li><a href="../style/color.html">Color</a></li>
+              <li><a href="../style/iconography.html">Iconography</a></li>
+              <li><a href="../style/writing.html">Writing Style</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../patterns/index.html">Patterns</a></div>
+            <ul>
+              <li><a href="../patterns/new-4-0.html">New in Android 4.0</a></li>
+              <li><a href="../patterns/gestures.html">Gestures</a></li>
+              <li><a href="../patterns/app-structure.html">App Structure</a></li>
+              <li><a href="../patterns/navigation.html">Navigation</a></li>
+              <li><a href="../patterns/actionbar.html">Action Bar</a></li>
+              <li><a href="../patterns/multi-pane-layouts.html">Multi-pane Layouts</a></li>
+              <li><a href="../patterns/swipe-views.html">Swipe Views</a></li>
+              <li><a href="../patterns/selection.html">Selection</a></li>
+              <li><a href="../patterns/notifications.html">Notifications</a></li>
+              <li><a href="../patterns/compatibility.html">Compatibility</a></li>
+              <li><a href="../patterns/pure-android.html">Pure Android</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../building-blocks/index.html">Building Blocks</a></div>
+            <ul>
+              <li><a href="../building-blocks/tabs.html">Tabs</a></li>
+              <li><a href="../building-blocks/lists.html">Lists</a></li>
+              <li><a href="../building-blocks/grid-lists.html">Grid Lists</a></li>
+              <li><a href="../building-blocks/scrolling.html">Scrolling</a></li>
+              <li><a href="../building-blocks/spinners.html">Spinners</a></li>
+              <li><a href="../building-blocks/buttons.html">Buttons</a></li>
+              <li><a href="../building-blocks/text-fields.html">Text Fields</a></li>
+              <li><a href="../building-blocks/seek-bars.html">Seek Bars</a></li>
+              <li><a href="../building-blocks/progress.html">Progress &amp; Activity</a></li>
+              <li><a href="../building-blocks/switches.html">Switches</a></li>
+              <li><a href="../building-blocks/dialogs.html">Dialogs</a></li>
+              <li><a href="../building-blocks/pickers.html">Pickers</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
+          <li>
+            <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
+          </li>
+
+        </ul>
+
+        <div id="content">
+
+          
+          <div class="layout-content-row content-header">
+            <div class="layout-content-col span-9">
+              <h2>Downloads</h2>
+            </div>
+            <div class="paging-links layout-content-col span-4">
+              <a href="#" class="prev-page-link">Previous</a>
+              <a href="#" class="next-page-link">Next</a>
+            </div>
+          </div>
+          
+
+          
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-9">
+
+<p>Want everything? We've bundled all the downloads available on Android Design into a single ZIP file.
+You can also download individual files listed below.</p>
+<p>You may use these materials without restriction in your apps and to develop your apps.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Downloads_20120229.zip">Download All</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="stencils">Stencils and Sources</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<p>Drag and drop your way to beautifully designed Ice Cream Sandwich apps. The stencils feature the
+rich typography, colors, interactive controls, and icons found throughout Android 4.0, along with
+phone and tablet outlines to frame your creations. Source files for icons and controls are also
+available.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_stencils.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe&reg; Photoshop&reg; Sources</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="action-bar-icon-pack">Action Bar Icon Pack</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<p>Action bar icons are graphic buttons that represent the most important actions people can take
+within your app. <a href="../style/iconography.html">More on Action Bar Iconography</a></p>
+<p>The download package includes icons that are scaled for various screen densities and suitable for
+use with the Holo Light and Holo Dark themes. The package also includes unstyled icons that you can
+modify to match your theme, plus source files.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/iconography_actionbar_style.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Action Bar Icon Pack</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="style">Style</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<h4>Roboto</h4>
+<p>Ice Cream Sandwich introduced a new type family named Roboto, created specifically for the
+requirements of UI and high-resolution screens.</p>
+<p><a href="../style/typography.html#actionbar">More on Roboto</a></p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_roboto_specimen_preview.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Hinted_20111129.zip">Roboto</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
+</p>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<h4>Color</h4>
+<p>Blue is the standard accent color in Android's color palette. Each color has a corresponding darker
+shade that can be used as a complement when needed.</p>
+<p><a href="../style/color.html">More on Color</a></p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_color_swatches.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Color_Swatches_20120229.zip">Color Swatches</a>
+</p>
+
+  </div>
+</div>
+
+
+
+          
+          <div class="layout-content-row content-footer">
+            <div class="paging-links layout-content-col span-9">&nbsp;</div>
+            <div class="paging-links layout-content-col span-4">
+              <a href="#" class="prev-page-link">Previous</a>
+              <a href="#" class="next-page-link">Next</a>
+            </div>
+          </div>
+          
+        </div>
+
+      </div>
+
+      <div id="page-footer">
+
+        <p id="copyright">
+          Except as noted, this content is licensed under
+          <a href="http://creativecommons.org/licenses/by/2.5/">
+          Creative Commons Attribution 2.5</a>.<br>
+          For details and restrictions, see the
+          <a href="http://developer.android.com/license.html">Content License</a>.
+        </p>
+
+        <p>
+          <a href="http://www.android.com/terms.html">Site Terms of Service</a> &ndash;
+          <a href="http://www.android.com/privacy.html">Privacy Policy</a> &ndash;
+          <a href="http://www.android.com/branding.html">Brand Guidelines</a>
+        </p>
+
+      </div>
+    </div>
+
+    <script src="../static/jquery-1.6.2.min.js"></script>
+    <script>
+    var SITE_ROOT = '../';
+    </script>
+    <script src="../static/default.js"></script>
+
+
+    <script type="text/javascript">
+    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+    </script>
+    <script type="text/javascript">
+    var pageTracker = _gat._getTracker("UA-5831155-1");
+    pageTracker._trackPageview();
+    </script>
+  </body>
+</html>
diff --git a/docs/html/design/get-started/creative-vision.html b/docs/html/design/get-started/creative-vision.html
index 11783c4..154f8d0 100644
--- a/docs/html/design/get-started/creative-vision.html
+++ b/docs/html/design/get-started/creative-vision.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/get-started/principles.html b/docs/html/design/get-started/principles.html
index 0d9ef20..f10a90d 100644
--- a/docs/html/design/get-started/principles.html
+++ b/docs/html/design/get-started/principles.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -106,7 +110,7 @@
 best interests in mind. Consider them as you apply your own creativity and design thinking. Deviate
 with purpose.</p>
 
-<h2>Enchant Me</h2>
+<h2 id="enchant-me">Enchant Me</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
@@ -176,7 +180,7 @@
   </div>
 </div>
 
-<h2>Simplify My Life</h2>
+<h2 id="simplify-my-life">Simplify My Life</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
@@ -312,7 +316,7 @@
   </div>
 </div>
 
-<h2>Make Me Amazing</h2>
+<h2 id="make-me-amazing">Make Me Amazing</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
diff --git a/docs/html/design/get-started/ui-overview.html b/docs/html/design/get-started/ui-overview.html
index bd5ff9c..a4881d5 100644
--- a/docs/html/design/get-started/ui-overview.html
+++ b/docs/html/design/get-started/ui-overview.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -109,7 +113,7 @@
 in your app.</p>
 <p>Read on for a quick overview of the most important aspects of the Android user interface.</p>
 
-<h2>Home, All Apps, and Recents</h2>
+<h2 id="home-all-apps-recents">Home, All Apps, and Recents</h2>
 
 <div class="vspace size-1">&nbsp;</div>
 
@@ -153,7 +157,7 @@
   </div>
 </div>
 
-<h2>System Bars</h2>
+<h2 id="system-bars">System Bars</h2>
 
 <p>The system bars are screen areas dedicated to the display of notifications, communication of device
 status, and device navigation. Typically the system bars are displayed concurrently with your app.
@@ -185,7 +189,7 @@
 
 </div>
 
-<h2>Notifications</h2>
+<h2 id="notifications">Notifications</h2>
 
 <p>Notifications are brief messages that users can access at any time from the status bar. They
 provide updates, reminders, or information that's important, but not critical enough to warrant
diff --git a/docs/html/design/index.html b/docs/html/design/index.html
index 14d7b63..8583aa4 100644
--- a/docs/html/design/index.html
+++ b/docs/html/design/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/actionbar.html b/docs/html/design/patterns/actionbar.html
index 911c549..1566d04 100644
--- a/docs/html/design/patterns/actionbar.html
+++ b/docs/html/design/patterns/actionbar.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -118,7 +122,8 @@
 <p>If you're new to writing Android apps, note that the action bar is one of the most important design
 elements you can implement. Following the guidelines described here will go a long way toward making
 your app's interface consistent with the core Android apps.</p>
-<h2>General Organization</h2>
+<h2 id="organization">General Organization</h2>
+
 <p>The action bar is split into four different functional areas that apply to most apps.</p>
 <img src="../static/content/action_bar_basics.png">
 
@@ -129,8 +134,8 @@
       <li class="value-1"><h4>App icon</h4>
         <p>
 
-The app icon establishes your app's identity. It can be replaced with a different logo or branding if
-you wish.
+The app icon establishes your app's identity. It can be replaced with a different logo or branding
+if you wish.
 Important: If the app is currently not displaying the top-level screen, be sure to display the Up
 caret to the left of the app icon, so the user can navigate up the hierarchy. For more discussion of
 Up navigation, see the <a href="../patterns/navigation.html">Navigation</a> pattern.
@@ -180,12 +185,11 @@
         </p>
       </li>
     </ol>
-
   </div>
 </div>
 
+<h2 id="adapting-rotation">Adapting to Rotation and Different Screen Sizes</h2>
 
-<h2>Adapting to Rotation and Different Screen Sizes</h2>
 <p>One of the most important UI issues to consider when creating an app is how to adjust to screen
 rotation on different screen sizes.</p>
 <p>You can adapt to such changes by using <em>split action bars</em>, which allow you to distribute action bar
@@ -196,7 +200,7 @@
   Split action bar showing action buttons at the bottom of the screen in vertical orientation.
 </div>
 
-<h2>Layout Considerations for Split Action Bars</h2>
+<h2 id="considerations-split-action-bars">Layout Considerations for Split Action Bars</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-8 with-callouts">
@@ -222,7 +226,8 @@
   </div>
 </div>
 
-<h2>Contextual Action Bars</h2>
+<h2 id="contextual">Contextual Action Bars</h2>
+
 <p>A <em>contextual action bar (CAB)</em> is a temporary action bar that overlays the app's action bar for the
 duration of a particular sub-task. CABs are most typically used for tasks that involve acting on
 selected data or text.</p>
@@ -244,7 +249,8 @@
 <p>Use CABs whenever you allow the user to select data via long press. You can control the action
 content of a CAB in order to insert the actions you would like the user to be able to perform.</p>
 <p>For more information, refer to the "Selection" pattern.</p>
-<h2>Action Bar Elements</h2>
+<h2 id="elements">Action Bar Elements</h2>
+
 <h4>Tabs</h4>
 <p><em>Tabs</em> display app views concurrently and make it easy to explore and switch between them. Use tabs
 if you expect your users to switch views frequently.</p>
@@ -326,7 +332,9 @@
 <p><em>Action buttons</em> on the action bar surface your app's most important activities. Think about which
 buttons will get used most often, and order them accordingly. Depending on available screen real
 estate, the system shows your most important actions as action buttons and moves the rest to the
-action overflow.</p>
+action overflow. The action bar and the action overflow should only present actions to the user that
+are available. If an action is unavailable in the current context, hide it. Do not show it as
+disabled.</p>
 
 <img src="../static/content/action_bar_pattern_action_icons.png">
 <div class="figure-caption">
@@ -380,8 +388,7 @@
 </p>
 <p>
 
-<a href="../static/download/action_bar_icons-v4.0.zip">Download the Action Bar Icon
-Pack</a>
+<a href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
 
 </p>
 
@@ -436,7 +443,8 @@
   The Gallery app's share action provider with extended spinner for additional sharing options.
 </div>
 
-<h2>Action Bar Checklist</h2>
+<h2 id="checklist">Action Bar Checklist</h2>
+
 <p>When planning your split action bars, ask yourself questions like these:</p>
 <h4>How important is view navigation to the task?</h4>
 <p>If view navigation is very important to your app, use tabs (for fastest view-switching) or spinners.</p>
diff --git a/docs/html/design/patterns/app-structure.html b/docs/html/design/patterns/app-structure.html
index fb9205b..1b48280 100644
--- a/docs/html/design/patterns/app-structure.html
+++ b/docs/html/design/patterns/app-structure.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -111,7 +115,8 @@
 <li>Apps such as Gmail or Market that combine a broad set of data views with deep navigation</li>
 </ul>
 <p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
-<h2>General Structure</h2>
+<h2 id="general-structure">General Structure</h2>
+
 <p>A typical Android app consists of top level and detail/edit views. If the navigation hierarchy is
 deep and complex, category views connect top level and detail views.</p>
 
@@ -139,7 +144,8 @@
   </div>
 </div>
 
-<h2>Top Level</h2>
+<h2 id="top-level">Top Level</h2>
+
 <p>The layout of your start screen requires special attention. This is the first screen people see
 after launching your app, so it should be an equally rewarding experience for new and frequent
 visitors alike.</p>
@@ -219,7 +225,8 @@
   </div>
 </div>
 
-<h2>Categories</h2>
+<h2 id="categories">Categories</h2>
+
 <p>Generally, the purpose of a deep, data-driven app is to navigate through organizational categories
 to the detail level, where data can be viewed and managed. Minimize perceived navigation effort by
 keeping your apps shallow.</p>
@@ -281,7 +288,8 @@
 delete multiple items in the category view. Analyze which detail view actions are applicable to
 collections of items. Then use multi-select to allow application of those actions to multiple items
 in a category view.</p>
-<h2>Details</h2>
+<h2 id="details">Details</h2>
+
 <p>The detail view allows you to view and act on your data. The layout of the detail view depends on
 the data type being displayed, and therefore differs widely among apps.</p>
 
@@ -330,7 +338,8 @@
   filmstrip control that lets people quickly jump to specific images.
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Find ways to display useful content on your start screen.</p>
diff --git a/docs/html/design/patterns/compatibility.html b/docs/html/design/patterns/compatibility.html
index f18c62d..d6e59f5 100644
--- a/docs/html/design/patterns/compatibility.html
+++ b/docs/html/design/patterns/compatibility.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -110,7 +114,7 @@
 </ul>
 <p>Android 4.0 brings these changes for tablets to the phone platform.</p>
 
-<h2>Adapting Android 4.0 to Older Hardware and Apps</h2>
+<h2 id="older-hardware">Adapting Android 4.0 to Older Hardware and Apps</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
diff --git a/docs/html/design/patterns/gestures.html b/docs/html/design/patterns/gestures.html
index f8585e4..c88817f 100644
--- a/docs/html/design/patterns/gestures.html
+++ b/docs/html/design/patterns/gestures.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/index.html b/docs/html/design/patterns/index.html
index ff797db..863baa9 100644
--- a/docs/html/design/patterns/index.html
+++ b/docs/html/design/patterns/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/multi-pane-layouts.html b/docs/html/design/patterns/multi-pane-layouts.html
index af05e31..7925c98 100644
--- a/docs/html/design/patterns/multi-pane-layouts.html
+++ b/docs/html/design/patterns/multi-pane-layouts.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -108,7 +112,8 @@
 <p><em>Panels</em> are a great way for your app to achieve this. They allow you to combine multiple views into
 one compound view when a lot of horizontal screen real estate is available and by splitting them up
 when less space is available.</p>
-<h2>Combining Multiple Views Into One</h2>
+<h2 id="combining-views">Combining Multiple Views Into One</h2>
+
 <p>On smaller devices your content is typically divided into a master grid or list view and a detail
 view. Touching an item in the master view opens a different screen showing that item's detail
 information.</p>
@@ -124,7 +129,8 @@
 <p>In general, use the pane on the right to present more information about the item you selected in the
 left pane. Make sure to keep the item in the left pane selected in order to establish the
 relationship between the panels.</p>
-<h2>Compound Views and Orientation Changes</h2>
+<h2 id="orientation">Compound Views and Orientation Changes</h2>
+
 <p>Screens should have the same functionality regardless of orientation. If you use a compound view in
 one orientation, don't split it up when the user rotates the screen. There are several techniques
 you can use to adjust the layout after orientation change while keeping functional parity intact.</p>
@@ -189,7 +195,8 @@
   </div>
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Plan in advance on how your app scales to different screen sizes and screen orientations.</p>
diff --git a/docs/html/design/patterns/navigation.html b/docs/html/design/patterns/navigation.html
index cad3682..6287b5e 100644
--- a/docs/html/design/patterns/navigation.html
+++ b/docs/html/design/patterns/navigation.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -112,7 +116,8 @@
 
 <img src="../static/content/navigation_with_back_and_up.png">
 
-<h2>Up vs. Back</h2>
+<h2 id="up-vs-back">Up vs. Back</h2>
+
 <p>The Up button is used to navigate within an application based on the hierarchical relationships
 between screens. For instance, if screen A displays a list of items, and selecting an item leads to
 screen B (which presents that item in more detail), then screen B should offer an Up button that
@@ -134,7 +139,8 @@
 <li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
 <li>Back hides the onscreen keyboard (IME)</li>
 </ul>
-<h2>Navigation Within Your App</h2>
+<h2 id="within-app">Navigation Within Your App</h2>
+
 <h4>Navigating to screens with multiple entry points</h4>
 <p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
 from multiple entry points&mdash;e.g., a settings screen which can be navigated to from any screen
@@ -176,7 +182,8 @@
 
 <img src="../static/content/navigation_between_siblings_market2.png">
 
-<h2>Navigation From Outside Your App</h2>
+<h2 id="from-outside">Navigation From Outside Your App</h2>
+
 <p>There are two categories of navigation from outside your app to screens deep within the app's
 hierarchy:</p>
 <ul>
diff --git a/docs/html/design/patterns/new-4-0.html b/docs/html/design/patterns/new-4-0.html
index 272b079..2e2cbc2 100644
--- a/docs/html/design/patterns/new-4-0.html
+++ b/docs/html/design/patterns/new-4-0.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/notifications.html b/docs/html/design/patterns/notifications.html
index c5045aed..99af418 100644
--- a/docs/html/design/patterns/notifications.html
+++ b/docs/html/design/patterns/notifications.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -173,7 +177,7 @@
   </div>
 </div>
 
-<h2>Design Guidelines</h2>
+<h2 id="design-guidelines">Design Guidelines</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
@@ -269,7 +273,7 @@
 <li>Use color to distinguish your app from others. Notification icons should generally be monochrome.</li>
 </ul>
 
-<h2>Interacting With Notifications</h2>
+<h2 id="interacting-with-notifications">Interacting With Notifications</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
diff --git a/docs/html/design/patterns/pure-android.html b/docs/html/design/patterns/pure-android.html
index 507558a..f5a8042 100644
--- a/docs/html/design/patterns/pure-android.html
+++ b/docs/html/design/patterns/pure-android.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -227,7 +231,7 @@
   </div>
 </div>
 
-<h2>Device Independence</h2>
+<h2 id="device-independence">Device Independence</h2>
 
 <p>Remember that your app will run on a wide variety of different screen sizes. Create visual assets
 for different screen sizes and densities and make use of concepts such as multi-pane layouts to
diff --git a/docs/html/design/patterns/selection.html b/docs/html/design/patterns/selection.html
index 37dcab5..44c6a84 100644
--- a/docs/html/design/patterns/selection.html
+++ b/docs/html/design/patterns/selection.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -180,7 +184,8 @@
   </div>
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Whenever your app supports the selection of multiple data items, make use of the contextual action
diff --git a/docs/html/design/patterns/swipe-views.html b/docs/html/design/patterns/swipe-views.html
index 4e8cd03..acc9f39 100644
--- a/docs/html/design/patterns/swipe-views.html
+++ b/docs/html/design/patterns/swipe-views.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -107,7 +111,8 @@
 vertical hierarchies and make access to related data items faster and more enjoyable. Swipe views
 allow the user to efficiently move from item to item using a simple gesture and thereby make
 browsing and consuming data a more fluent experience.</p>
-<h2>Swiping Between Detail Views</h2>
+<h2 id="detail-views">Swiping Between Detail Views</h2>
+
 <p>An app's data is often organized in a master/detail relationship: The user can view a list of
 related data items, such as images, chats, or emails, and then pick one of the items to see the
 detail contents in a separate screen.</p>
@@ -127,7 +132,7 @@
   Navigating between consecutive Email messages using the swipe gesture.
 </div>
 
-<h2>Swiping Between Tabs</h2>
+<h2 id="between-tabs">Swiping Between Tabs</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-5">
@@ -150,7 +155,8 @@
 <p>If your app uses action bar tabs, use swipe to navigate between the different views.</p>
 <div class="vspace size-2">&nbsp;</div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Use swipe to quickly navigate between detail views or tabs.</p>
diff --git a/docs/html/design/static/content/downloads_color_swatches.png b/docs/html/design/static/content/downloads_color_swatches.png
new file mode 100644
index 0000000..af2b24f
--- /dev/null
+++ b/docs/html/design/static/content/downloads_color_swatches.png
Binary files differ
diff --git a/docs/html/design/static/content/downloads_roboto_specimen_preview.png b/docs/html/design/static/content/downloads_roboto_specimen_preview.png
new file mode 100644
index 0000000..2954cbf
--- /dev/null
+++ b/docs/html/design/static/content/downloads_roboto_specimen_preview.png
Binary files differ
diff --git a/docs/html/design/static/content/downloads_stencils.png b/docs/html/design/static/content/downloads_stencils.png
new file mode 100644
index 0000000..9e09319
--- /dev/null
+++ b/docs/html/design/static/content/downloads_stencils.png
Binary files differ
diff --git a/docs/html/design/static/default.css b/docs/html/design/static/default.css
index 42ab527..3aa1db3 100644
--- a/docs/html/design/static/default.css
+++ b/docs/html/design/static/default.css
@@ -3,8 +3,8 @@
 /* clearfix idiom */
 /* common mixins */
 /* page layout + top-level styles */
-::-moz-selection,
 ::-webkit-selection,
+::-moz-selection,
 ::selection {
   background-color: #0099cc;
   color: #fff; }
@@ -256,6 +256,8 @@
       position: absolute;
       top: 10px;
       right: 10px; }
+    #nav .nav-section-header.empty:after {
+      display: none; }
   #nav li.expanded .nav-section-header {
     background: rgba(0, 0, 0, 0.05); }
     #nav li.expanded .nav-section-header:after {
@@ -268,9 +270,9 @@
     overflow: hidden;
     margin-bottom: 0; }
     #nav > li > ul.animate-height {
-      transition: height 0.25s ease-in;
       -webkit-transition: height 0.25s ease-in;
-      -moz-transition: height 0.25s ease-in; }
+      -moz-transition: height 0.25s ease-in;
+      transition: height 0.25s ease-in; }
     #nav > li > ul li {
       padding: 10px 10px 11px 10px; }
   #nav > li.expanded > ul {
@@ -330,6 +332,39 @@
       margin-left: 5px; }
 
 /* content body */
+@-webkit-keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
+@-moz-keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
+@keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
 #content p,
 #content ul,
 #content ol,
@@ -345,6 +380,16 @@
   color: #33b5e5;
   border-bottom: 1px solid #33b5e5;
   height: 30px; }
+  #content h2:target {
+    -webkit-animation-name: glowheader;
+    -moz-animation-name: glowheader;
+    animation-name: glowheader;
+    -webkit-animation-duration: 0.7s;
+    -moz-animation-duration: 0.7s;
+    animation-duration: 0.7s;
+    -webkit-animation-timing-function: ease-out;
+    -moz-animation-timing-function: ease-out;
+    animation-timing-function: ease-out; }
 #content hr {
   border: 0;
   border-bottom: 1px solid #33b5e5;
@@ -569,3 +614,20 @@
     margin-right: 8px; }
   .video-instructions:after {
     content: 'Click to replay movie.'; }
+
+/* download buttons */
+.download-button {
+  display: block;
+  margin-bottom: 5px;
+  text-decoration: none;
+  background-color: #33b5e5;
+  color: #fff !important;
+  font-weight: 500;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12);
+  padding: 6px 12px;
+  border-radius: 2px; }
+  .download-button:hover, .download-button:focus {
+    background-color: #0099cc;
+    color: #fff !important; }
+  .download-button:active {
+    background-color: #006699; }
diff --git a/docs/html/design/static/default.js b/docs/html/design/static/default.js
index 6721ab8..b213dd9 100644
--- a/docs/html/design/static/default.js
+++ b/docs/html/design/static/default.js
@@ -158,4 +158,12 @@
       $tooltip.hide();
     });
   });
+
+  // Set up <h2> deeplinks
+  $('h2').click(function() {
+    var id = $(this).attr('id');
+    if (id) {
+      document.location.hash = id;
+    }
+  });
 });
\ No newline at end of file
diff --git a/docs/html/design/static/download/RobotoSpecimenBook.pdf b/docs/html/design/static/download/RobotoSpecimenBook.pdf
deleted file mode 100644
index 594a366..0000000
--- a/docs/html/design/static/download/RobotoSpecimenBook.pdf
+++ /dev/null
Binary files differ