Merge "Prune unneeded density resources from APK" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 40c7bf5..054f1cd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -267,6 +267,7 @@
     field public static final int addPrintersActivity = 16843750; // 0x10103e6
     field public static final int addStatesFromChildren = 16842992; // 0x10100f0
     field public static final int adjustViewBounds = 16843038; // 0x101011e
+    field public static final int advancedPrintOptionsActivity = 16843761; // 0x10103f1
     field public static final int alertDialogIcon = 16843605; // 0x1010355
     field public static final int alertDialogStyle = 16842845; // 0x101005d
     field public static final int alertDialogTheme = 16843529; // 0x1010309
@@ -7497,11 +7498,13 @@
 
   public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
     ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long);
+    ctor public AssetFileDescriptor(android.os.ParcelFileDescriptor, long, long, android.os.Bundle);
     method public void close() throws java.io.IOException;
     method public java.io.FileInputStream createInputStream() throws java.io.IOException;
     method public java.io.FileOutputStream createOutputStream() throws java.io.IOException;
     method public int describeContents();
     method public long getDeclaredLength();
+    method public android.os.Bundle getExtras();
     method public java.io.FileDescriptor getFileDescriptor();
     method public long getLength();
     method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -19011,6 +19014,16 @@
     field public static final int STATE_STARTED = 3; // 0x3
   }
 
+  public static final class PrintJobInfo.Builder {
+    ctor public PrintJobInfo.Builder(android.print.PrintJobInfo);
+    method public android.print.PrintJobInfo build();
+    method public void putAdvancedOption(java.lang.String, java.lang.String);
+    method public void putAdvancedOption(java.lang.String, int);
+    method public void setAttributes(android.print.PrintAttributes);
+    method public void setCopies(int);
+    method public void setPages(android.print.PageRange[]);
+  }
+
   public final class PrintManager {
     method public java.util.List<android.print.PrintJob> getPrintJobs();
     method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
@@ -19093,10 +19106,13 @@
     method public boolean cancel();
     method public boolean complete();
     method public boolean fail(java.lang.String);
+    method public int getAdvancedIntOption(java.lang.String);
+    method public java.lang.String getAdvancedStringOption(java.lang.String);
     method public android.printservice.PrintDocument getDocument();
     method public android.print.PrintJobId getId();
     method public android.print.PrintJobInfo getInfo();
     method public java.lang.String getTag();
+    method public boolean hasAdvancedOption(java.lang.String);
     method public boolean isBlocked();
     method public boolean isCancelled();
     method public boolean isCompleted();
@@ -19118,6 +19134,7 @@
     method protected void onDisconnected();
     method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
     method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
+    field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
     field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
     field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
   }
@@ -25777,6 +25794,7 @@
     method public boolean equals(android.util.DisplayMetrics);
     method public void setTo(android.util.DisplayMetrics);
     method public void setToDefaults();
+    field public static final int DENSITY_400 = 400; // 0x190
     field public static final int DENSITY_DEFAULT = 160; // 0xa0
     field public static final int DENSITY_HIGH = 240; // 0xf0
     field public static final int DENSITY_LOW = 120; // 0x78
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e29f8ea..193724d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -58,6 +58,7 @@
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.ActionMode;
@@ -4846,36 +4847,19 @@
                 writer.println(mChangingConfigurations);
         writer.print(innerPrefix); writer.print("mCurrentConfig=");
                 writer.println(mCurrentConfig);
+
         if (mLoaderManager != null) {
             writer.print(prefix); writer.print("Loader Manager ");
                     writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
                     writer.println(":");
             mLoaderManager.dump(prefix + "  ", fd, writer, args);
         }
-        mFragments.dump(prefix, fd, writer, args);
-        writer.print(prefix); writer.println("View Hierarchy:");
-        dumpViewHierarchy(prefix + "  ", writer, getWindow().getDecorView());
-    }
 
-    private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
-        writer.print(prefix);
-        if (view == null) {
-            writer.println("null");
-            return;
-        }
-        writer.println(view.toString());
-        if (!(view instanceof ViewGroup)) {
-            return;
-        }
-        ViewGroup grp = (ViewGroup)view;
-        final int N = grp.getChildCount();
-        if (N <= 0) {
-            return;
-        }
-        prefix = prefix + "  ";
-        for (int i=0; i<N; i++) {
-            dumpViewHierarchy(prefix, writer, grp.getChildAt(i));
-        }
+        mFragments.dump(prefix, fd, writer, args);
+
+        getWindow().getDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
+
+        mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1067eb1..bb04063 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -68,9 +68,10 @@
     private final Handler mHandler;
 
     /**
-     * <meta-data> string for a 'home' Activity that names a package that is to be
+     * <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
+     * &lt;meta-data>}</a> name for a 'home' Activity that declares a package that is to be
      * uninstalled in lieu of the declaring one.  The package named here must be
-     * signed with the same certificate as the one declaring the <meta-data>.
+     * signed with the same certificate as the one declaring the {@code &lt;meta-data>}.
      */
     public static final String META_HOME_ALTERNATE = "android.app.home.alternate";
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index df63ab3..347850a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -558,7 +558,7 @@
 
         public final void schedulePauseActivity(IBinder token, boolean finished,
                 boolean userLeaving, int configChanges) {
-            queueOrSendMessage(
+            sendMessage(
                     finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                     token,
                     (userLeaving ? 1 : 0),
@@ -567,32 +567,32 @@
 
         public final void scheduleStopActivity(IBinder token, boolean showWindow,
                 int configChanges) {
-           queueOrSendMessage(
+           sendMessage(
                 showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
                 token, 0, configChanges);
         }
 
         public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
-            queueOrSendMessage(
+            sendMessage(
                 showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW,
                 token);
         }
 
         public final void scheduleSleeping(IBinder token, boolean sleeping) {
-            queueOrSendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
+            sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
         }
 
         public final void scheduleResumeActivity(IBinder token, int processState,
                 boolean isForward) {
             updateProcessState(processState, false);
-            queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
+            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
         }
 
         public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
             ResultData res = new ResultData();
             res.token = token;
             res.results = results;
-            queueOrSendMessage(H.SEND_RESULT, res);
+            sendMessage(H.SEND_RESULT, res);
         }
 
         // we use token to identify this activity without having to send the
@@ -626,7 +626,7 @@
 
             updatePendingConfiguration(curConfig);
 
-            queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
+            sendMessage(H.LAUNCH_ACTIVITY, r);
         }
 
         public final void scheduleRelaunchActivity(IBinder token,
@@ -641,12 +641,12 @@
             data.intents = intents;
             data.token = token;
 
-            queueOrSendMessage(H.NEW_INTENT, data);
+            sendMessage(H.NEW_INTENT, data);
         }
 
         public final void scheduleDestroyActivity(IBinder token, boolean finishing,
                 int configChanges) {
-            queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
+            sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
                     configChanges);
         }
 
@@ -658,7 +658,7 @@
                     sync, false, mAppThread.asBinder(), sendingUser);
             r.info = info;
             r.compatInfo = compatInfo;
-            queueOrSendMessage(H.RECEIVER, r);
+            sendMessage(H.RECEIVER, r);
         }
 
         public final void scheduleCreateBackupAgent(ApplicationInfo app,
@@ -668,7 +668,7 @@
             d.compatInfo = compatInfo;
             d.backupMode = backupMode;
 
-            queueOrSendMessage(H.CREATE_BACKUP_AGENT, d);
+            sendMessage(H.CREATE_BACKUP_AGENT, d);
         }
 
         public final void scheduleDestroyBackupAgent(ApplicationInfo app,
@@ -677,7 +677,7 @@
             d.appInfo = app;
             d.compatInfo = compatInfo;
 
-            queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d);
+            sendMessage(H.DESTROY_BACKUP_AGENT, d);
         }
 
         public final void scheduleCreateService(IBinder token,
@@ -688,7 +688,7 @@
             s.info = info;
             s.compatInfo = compatInfo;
 
-            queueOrSendMessage(H.CREATE_SERVICE, s);
+            sendMessage(H.CREATE_SERVICE, s);
         }
 
         public final void scheduleBindService(IBinder token, Intent intent,
@@ -702,7 +702,7 @@
             if (DEBUG_SERVICE)
                 Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                         + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
-            queueOrSendMessage(H.BIND_SERVICE, s);
+            sendMessage(H.BIND_SERVICE, s);
         }
 
         public final void scheduleUnbindService(IBinder token, Intent intent) {
@@ -710,7 +710,7 @@
             s.token = token;
             s.intent = intent;
 
-            queueOrSendMessage(H.UNBIND_SERVICE, s);
+            sendMessage(H.UNBIND_SERVICE, s);
         }
 
         public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
@@ -722,11 +722,11 @@
             s.flags = flags;
             s.args = args;
 
-            queueOrSendMessage(H.SERVICE_ARGS, s);
+            sendMessage(H.SERVICE_ARGS, s);
         }
 
         public final void scheduleStopService(IBinder token) {
-            queueOrSendMessage(H.STOP_SERVICE, token);
+            sendMessage(H.STOP_SERVICE, token);
         }
 
         public final void bindApplication(String processName,
@@ -763,24 +763,24 @@
             data.initProfileFile = profileFile;
             data.initProfileFd = profileFd;
             data.initAutoStopProfiler = false;
-            queueOrSendMessage(H.BIND_APPLICATION, data);
+            sendMessage(H.BIND_APPLICATION, data);
         }
 
         public final void scheduleExit() {
-            queueOrSendMessage(H.EXIT_APPLICATION, null);
+            sendMessage(H.EXIT_APPLICATION, null);
         }
 
         public final void scheduleSuicide() {
-            queueOrSendMessage(H.SUICIDE, null);
+            sendMessage(H.SUICIDE, null);
         }
 
         public void requestThumbnail(IBinder token) {
-            queueOrSendMessage(H.REQUEST_THUMBNAIL, token);
+            sendMessage(H.REQUEST_THUMBNAIL, token);
         }
 
         public void scheduleConfigurationChanged(Configuration config) {
             updatePendingConfiguration(config);
-            queueOrSendMessage(H.CONFIGURATION_CHANGED, config);
+            sendMessage(H.CONFIGURATION_CHANGED, config);
         }
 
         public void updateTimeZone() {
@@ -807,7 +807,7 @@
                 data.fd = ParcelFileDescriptor.dup(fd);
                 data.token = servicetoken;
                 data.args = args;
-                queueOrSendMessage(H.DUMP_SERVICE, data);
+                sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/);
             } catch (IOException e) {
                 Slog.w(TAG, "dumpService failed", e);
             }
@@ -825,11 +825,11 @@
         }
 
         public void scheduleLowMemory() {
-            queueOrSendMessage(H.LOW_MEMORY, null);
+            sendMessage(H.LOW_MEMORY, null);
         }
 
         public void scheduleActivityConfigurationChanged(IBinder token) {
-            queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
+            sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
         }
 
         public void profilerControl(boolean start, String path, ParcelFileDescriptor fd,
@@ -837,14 +837,14 @@
             ProfilerControlData pcd = new ProfilerControlData();
             pcd.path = path;
             pcd.fd = fd;
-            queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType);
+            sendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType);
         }
 
         public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
             DumpHeapData dhd = new DumpHeapData();
             dhd.path = path;
             dhd.fd = fd;
-            queueOrSendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0);
+            sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
         }
 
         public void setSchedulingGroup(int group) {
@@ -860,11 +860,11 @@
         }
 
         public void dispatchPackageBroadcast(int cmd, String[] packages) {
-            queueOrSendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
+            sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
         }
 
         public void scheduleCrash(String msg) {
-            queueOrSendMessage(H.SCHEDULE_CRASH, msg);
+            sendMessage(H.SCHEDULE_CRASH, msg);
         }
 
         public void dumpActivity(FileDescriptor fd, IBinder activitytoken,
@@ -875,7 +875,7 @@
                 data.token = activitytoken;
                 data.prefix = prefix;
                 data.args = args;
-                queueOrSendMessage(H.DUMP_ACTIVITY, data);
+                sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/);
             } catch (IOException e) {
                 Slog.w(TAG, "dumpActivity failed", e);
             }
@@ -888,7 +888,7 @@
                 data.fd = ParcelFileDescriptor.dup(fd);
                 data.token = providertoken;
                 data.args = args;
-                queueOrSendMessage(H.DUMP_PROVIDER, data);
+                sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/);
             } catch (IOException e) {
                 Slog.w(TAG, "dumpProvider failed", e);
             }
@@ -1225,7 +1225,7 @@
 
         @Override
         public void unstableProviderDied(IBinder provider) {
-            queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
+            sendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
         }
 
         @Override
@@ -1235,7 +1235,7 @@
             cmd.activityToken = activityToken;
             cmd.requestToken = requestToken;
             cmd.requestType = requestType;
-            queueOrSendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd);
+            sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd);
         }
 
         private void printRow(PrintWriter pw, String format, Object...objs) {
@@ -1243,22 +1243,22 @@
         }
 
         public void setCoreSettings(Bundle coreSettings) {
-            queueOrSendMessage(H.SET_CORE_SETTINGS, coreSettings);
+            sendMessage(H.SET_CORE_SETTINGS, coreSettings);
         }
 
         public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) {
             UpdateCompatibilityData ucd = new UpdateCompatibilityData();
             ucd.pkg = pkg;
             ucd.info = info;
-            queueOrSendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
+            sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
         }
 
         public void scheduleTrimMemory(int level) {
-            queueOrSendMessage(H.TRIM_MEMORY, null, level);
+            sendMessage(H.TRIM_MEMORY, null, level);
         }
 
         public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
-            queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
+            sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
         }
 
         public void setProcessState(int state) {
@@ -1281,7 +1281,7 @@
 
         @Override
         public void scheduleInstallProvider(ProviderInfo provider) {
-            queueOrSendMessage(H.INSTALL_PROVIDER, provider);
+            sendMessage(H.INSTALL_PROVIDER, provider);
         }
     }
 
@@ -2033,28 +2033,31 @@
         mAppThread.scheduleSendResult(token, list);
     }
 
-    // if the thread hasn't started yet, we don't have the handler, so just
-    // save the messages until we're ready.
-    private void queueOrSendMessage(int what, Object obj) {
-        queueOrSendMessage(what, obj, 0, 0);
+    private void sendMessage(int what, Object obj) {
+        sendMessage(what, obj, 0, 0, false);
     }
 
-    private void queueOrSendMessage(int what, Object obj, int arg1) {
-        queueOrSendMessage(what, obj, arg1, 0);
+    private void sendMessage(int what, Object obj, int arg1) {
+        sendMessage(what, obj, arg1, 0, false);
     }
 
-    private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
-        synchronized (this) {
-            if (DEBUG_MESSAGES) Slog.v(
-                TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
-                + ": " + arg1 + " / " + obj);
-            Message msg = Message.obtain();
-            msg.what = what;
-            msg.obj = obj;
-            msg.arg1 = arg1;
-            msg.arg2 = arg2;
-            mH.sendMessage(msg);
+    private void sendMessage(int what, Object obj, int arg1, int arg2) {
+        sendMessage(what, obj, arg1, arg2, false);
+    }
+
+    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
+        if (DEBUG_MESSAGES) Slog.v(
+            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+            + ": " + arg1 + " / " + obj);
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        if (async) {
+            msg.setAsynchronous(true);
         }
+        mH.sendMessage(msg);
     }
 
     final void scheduleContextCleanup(ContextImpl context, String who,
@@ -2063,7 +2066,7 @@
         cci.context = context;
         cci.who = who;
         cci.what = what;
-        queueOrSendMessage(H.CLEAN_UP_CONTEXT, cci);
+        sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
@@ -3592,7 +3595,7 @@
                     target.onlyLocalRequest = true;
                 }
                 mRelaunchingActivities.add(target);
-                queueOrSendMessage(H.RELAUNCH_ACTIVITY, target);
+                sendMessage(H.RELAUNCH_ACTIVITY, target);
             }
 
             if (fromServer) {
@@ -4900,7 +4903,7 @@
                                 mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                             mPendingConfiguration = newConfig;
                             
-                            queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
+                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);
                         }
                     }
                 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index c39415f..ced72f8 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -72,6 +73,7 @@
      * an intent; instead, use {@link #getCropAndSetWallpaperIntent}.
      * <p>Input:  {@link Intent#getData} is the URI of the image to crop and set as wallpaper.
      * <p>Output: RESULT_OK if user decided to crop/set the wallpaper, RESULT_CANCEL otherwise
+     * Activities that support this intent should specify a MIME filter of "image/*"
      */
     public static final String ACTION_CROP_AND_SET_WALLPAPER =
             "android.service.wallpaper.CROP_AND_SET_WALLPAPER";
@@ -278,6 +280,7 @@
             synchronized (this) {
                 mWallpaper = null;
                 mDefaultWallpaper = null;
+                mHandler.removeMessages(MSG_CLEAR_WALLPAPER);
             }
         }
 
@@ -459,7 +462,7 @@
                     float right = left + outWidth;
                     float top = (inHeight - outHeight) * verticalAlignment;
                     float bottom = top + outHeight;
-                    cropRectF = new RectF(bottom, left, right, top);
+                    cropRectF = new RectF(left, top, right, bottom);
                 }
                 Rect roundedTrueCrop = new Rect();
                 cropRectF.roundOut(roundedTrueCrop);
@@ -657,8 +660,19 @@
      * that supports cropping wallpapers, it will be preferred as the default.
      * Use this method instead of directly creating a {@link #ACTION_CROP_AND_SET_WALLPAPER}
      * intent.
+     *
+     * @param imageUri The image URI that will be set in the intent. The must be a content
+     *                 URI and its provider must resolve its type to "image/*"
+     *
+     * @throws IllegalArgumentException if the URI is not a content URI or its MIME type is
+     *         not "image/*"
      */
     public Intent getCropAndSetWallpaperIntent(Uri imageUri) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) {
+            throw new IllegalArgumentException("Image URI must be of the "
+                    + ContentResolver.SCHEME_CONTENT + " scheme type");
+        }
+
         final PackageManager packageManager = mContext.getPackageManager();
         Intent cropAndSetWallpaperIntent =
                 new Intent(ACTION_CROP_AND_SET_WALLPAPER, imageUri);
@@ -680,7 +694,15 @@
 
         // fallback crop activity
         cropAndSetWallpaperIntent.setPackage("com.android.wallpapercropper");
-        return cropAndSetWallpaperIntent;
+        List<ResolveInfo> cropAppList = packageManager.queryIntentActivities(
+                cropAndSetWallpaperIntent, 0);
+        if (cropAppList.size() > 0) {
+            return cropAndSetWallpaperIntent;
+        }
+        // If the URI is not of the right type, or for some reason the system wallpaper
+        // cropper doesn't exist, return null
+        throw new IllegalArgumentException("Cannot use passed URI to set wallpaper; " +
+            "check that the type returned by ContentProvider matches image/*");
     }
 
     /**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 461dc1d..a9d0559 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1078,6 +1078,7 @@
      * @see #openAssetFile(Uri, String)
      * @see #openFileHelper(Uri, String)
      * @see #getType(android.net.Uri)
+     * @see ParcelFileDescriptor#parseMode(String)
      */
     public ParcelFileDescriptor openFile(Uri uri, String mode)
             throws FileNotFoundException {
@@ -1147,6 +1148,7 @@
      * @see #openAssetFile(Uri, String)
      * @see #openFileHelper(Uri, String)
      * @see #getType(android.net.Uri)
+     * @see ParcelFileDescriptor#parseMode(String)
      */
     public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
             throws FileNotFoundException {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 04b4027..4e6cc92 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1627,9 +1627,9 @@
     }
 
     /**
-     * Take a persistable Uri permission grant that has been offered. Once
+     * Take a persistable URI permission grant that has been offered. Once
      * taken, the permission grant will be remembered across device reboots.
-     * Only Uri permissions granted with
+     * Only URI permissions granted with
      * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
      * the grant has already been persisted, taking it again will touch
      * {@link UriPermission#getPersistedTime()}.
@@ -1644,7 +1644,7 @@
     }
 
     /**
-     * Relinquish a persisted Uri permission grant. The Uri must have been
+     * Relinquish a persisted URI permission grant. The URI must have been
      * previously made persistent with
      * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
      * grants to the calling package will remain intact.
@@ -1659,7 +1659,7 @@
     }
 
     /**
-     * Return list of all Uri permission grants that have been persisted by the
+     * Return list of all URI permission grants that have been persisted by the
      * calling app. That is, the returned permissions have been granted
      * <em>to</em> the calling app. Only persistable grants taken with
      * {@link #takePersistableUriPermission(Uri, int)} are returned.
@@ -1677,7 +1677,7 @@
     }
 
     /**
-     * Return list of all persisted Uri permission grants that are hosted by the
+     * Return list of all persisted URI permission grants that are hosted by the
      * calling app. That is, the returned permissions have been granted
      * <em>from</em> the calling app. Only grants taken with
      * {@link #takePersistableUriPermission(Uri, int)} are returned.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fe079bc..2e4e209 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -30,8 +30,10 @@
 import android.media.MediaScannerConnection.OnScanCompletedListener;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.StatFs;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
@@ -594,7 +596,7 @@
     public abstract File getFilesDir();
 
     /**
-     * Returns the absolute path to the directory on the external filesystem
+     * Returns the absolute path to the directory on the primary external filesystem
      * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
      * Environment.getExternalStorageDirectory()}) where the application can
      * place persistent files it owns.  These files are internal to the
@@ -608,10 +610,18 @@
      * <li>External files are not always available: they will disappear if the
      * user mounts the external storage on a computer or removes it.  See the
      * APIs on {@link android.os.Environment} for information in the storage state.
-     * <li>There is no security enforced with these files.  All applications
-     * can read and write files placed here.
+     * <li>There is no security enforced with these files.  For example, any application
+     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      *
+     * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the returned path; it's always
+     * accessible to the calling app.  This only applies to paths generated for
+     * package name of the calling application.  To access paths belonging
+     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
+     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     *
      * <p>On devices with multiple users (as described by {@link UserManager}),
      * each user has their own isolated external storage. Applications only
      * have access to the external storage for the user they're running as.</p>
@@ -644,11 +654,6 @@
      *
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * private_picture}
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * this path. Otherwise, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
-     * or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
      *
      * @param type The type of files directory to return.  May be null for
      * the root of the files directory or one of
@@ -677,36 +682,61 @@
      * it owns. These files are internal to the application, and not typically
      * visible to the user as media.
      * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted when
+     * the application is uninstalled, however there are some important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.
+     * </ul>
+     * <p>
      * External storage devices returned here are considered a permanent part of
      * the device, including both emulated external storage and physical media
-     * slots. This does not include transient devices, such as USB flash drives.
+     * slots, such as SD cards in a battery compartment. The returned paths do
+     * not include transient devices, such as USB flash drives.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * these paths.
+     * An application may store data on any or all of the returned devices.  For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * The returned paths include any path that would be returned by
-     * {@link #getExternalFilesDir(String)}.
+     * No permissions are required to read or write to the returned paths; they
+     * are always accessible to the calling app.  Write access outside of these
+     * paths on secondary external storage devices is not available.
+     * <p>
+     * The first path returned is the same as {@link #getExternalFilesDir(String)}.
+     * Returned paths may be {@code null} if a storage device is unavailable.
      *
      * @see #getExternalFilesDir(String)
+     * @see Environment#getStorageState(File)
      */
     public abstract File[] getExternalFilesDirs(String type);
 
     /**
-     * Return the directory where this application's OBB files (if there are
-     * any) can be found. Note if the application does not have any OBB files,
-     * this directory may not exist.
+     * Return the primary external storage directory where this application's OBB
+     * files (if there are any) can be found. Note if the application does not have
+     * any OBB files, this directory may not exist.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted when
+     * the application is uninstalled, however there are some important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.  For example, any application
+     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
+     * </ul>
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the returned path; it's always
+     * accessible to the calling app.  This only applies to paths generated for
+     * package name of the calling application.  To access paths belonging
+     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
+     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
      * <p>
      * On devices with multiple users (as described by {@link UserManager}),
      * multiple users may share the same OBB storage location. Applications
      * should ensure that multiple instances running under different users don't
      * interfere with each other.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * this path. Otherwise,
-     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} or
-     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
      */
     public abstract File getObbDir();
 
@@ -716,18 +746,32 @@
      * any) can be found. Note if the application does not have any OBB files,
      * these directories may not exist.
      * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted when
+     * the application is uninstalled, however there are some important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.
+     * </ul>
+     * <p>
      * External storage devices returned here are considered a permanent part of
      * the device, including both emulated external storage and physical media
-     * slots. This does not include transient devices, such as USB flash drives.
+     * slots, such as SD cards in a battery compartment. The returned paths do
+     * not include transient devices, such as USB flash drives.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * this path.
+     * An application may store data on any or all of the returned devices.  For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * The returned paths include any path that would be returned by
-     * {@link #getObbDir()}
+     * No permissions are required to read or write to the returned paths; they
+     * are always accessible to the calling app.  Write access outside of these
+     * paths on secondary external storage devices is not available.
+     * <p>
+     * The first path returned is the same as {@link #getObbDir()}.
+     * Returned paths may be {@code null} if a storage device is unavailable.
      *
      * @see #getObbDir()
+     * @see Environment#getStorageState(File)
      */
     public abstract File[] getObbDirs();
 
@@ -751,7 +795,7 @@
     public abstract File getCacheDir();
 
     /**
-     * Returns the absolute path to the directory on the external filesystem
+     * Returns the absolute path to the directory on the primary external filesystem
      * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
      * Environment.getExternalStorageDirectory()} where the application can
      * place cache files it owns. These files are internal to the application, and
@@ -773,19 +817,21 @@
      * <li>External files are not always available: they will disappear if the
      * user mounts the external storage on a computer or removes it.  See the
      * APIs on {@link android.os.Environment} for information in the storage state.
-     * <li>There is no security enforced with these files.  All applications
-     * can read and write files placed here.
+     * <li>There is no security enforced with these files.  For example, any application
+     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      *
+     * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * are required to read or write to the returned path; it's always
+     * accessible to the calling app.  This only applies to paths generated for
+     * package name of the calling application.  To access paths belonging
+     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
+     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     *
      * <p>On devices with multiple users (as described by {@link UserManager}),
      * each user has their own isolated external storage. Applications only
      * have access to the external storage for the user they're running as.</p>
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * this path. Otherwise,
-     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} or
-     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
      *
      * @return The path of the directory holding application cache files
      * on external storage.  Returns null if external storage is not currently
@@ -802,18 +848,32 @@
      * owns. These files are internal to the application, and not typically
      * visible to the user as media.
      * <p>
+     * This is like {@link #getCacheDir()} in that these files will be deleted when
+     * the application is uninstalled, however there are some important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.
+     * </ul>
+     * <p>
      * External storage devices returned here are considered a permanent part of
      * the device, including both emulated external storage and physical media
-     * slots. This does not include transient devices, such as USB flash drives.
+     * slots, such as SD cards in a battery compartment. The returned paths do
+     * not include transient devices, such as USB flash drives.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no
-     * permissions are required for the owning application to read or write to
-     * these paths.
+     * An application may store data on any or all of the returned devices.  For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * The returned paths include any path that would be returned by
-     * {@link #getExternalCacheDir()}.
+     * No permissions are required to read or write to the returned paths; they
+     * are always accessible to the calling app.  Write access outside of these
+     * paths on secondary external storage devices is not available.
+     * <p>
+     * The first path returned is the same as {@link #getExternalCacheDir()}.
+     * Returned paths may be {@code null} if a storage device is unavailable.
      *
      * @see #getExternalCacheDir()
+     * @see Environment#getStorageState(File)
      */
     public abstract File[] getExternalCacheDirs();
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 047f175..a289649 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -36,6 +36,8 @@
 import android.os.Parcelable;
 import android.os.StrictMode;
 import android.provider.DocumentsContract;
+import android.provider.DocumentsProvider;
+import android.provider.OpenableColumns;
 import android.util.AttributeSet;
 import android.util.Log;
 
@@ -45,7 +47,6 @@
 import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
@@ -2621,49 +2622,76 @@
     public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
 
     /**
-     * Activity Action: Allow the user to select and open one or more existing
-     * documents. Both read and write access to the documents will be granted
-     * until explicitly revoked by the user.
+     * Activity Action: Allow the user to select and return one or more existing
+     * documents. When invoked, the system will display the various
+     * {@link DocumentsProvider} instances installed on the device, letting the
+     * user interactively navigate through them. These documents include local
+     * media, such as photos and video, and documents provided by installed
+     * cloud storage providers.
      * <p>
-     * Callers can restrict selection to a specific kind of data, such as
-     * photos, by setting one or more MIME types in {@link #EXTRA_MIME_TYPES}.
+     * Each document is represented as a {@code content://} URI backed by a
+     * {@link DocumentsProvider}, which can be opened as a stream with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}, or queried for
+     * {@link android.provider.DocumentsContract.Document} metadata.
+     * <p>
+     * All selected documents are returned to the calling application with
+     * persistable read and write permission grants. If you want to maintain
+     * access to the documents across device reboots, you need to explicitly
+     * take the persistable permissions using
+     * {@link ContentResolver#takePersistableUriPermission(Uri, int)}.
+     * <p>
+     * Callers can restrict document selection to a specific kind of data, such
+     * as photos, by setting one or more MIME types in
+     * {@link #EXTRA_MIME_TYPES}.
      * <p>
      * If the caller can handle multiple returned items (the user performing
-     * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to
-     * indicate this.
+     * multiple selection), then you can specify {@link #EXTRA_ALLOW_MULTIPLE}
+     * to indicate this.
      * <p>
      * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
      * returned URIs can be opened with
      * {@link ContentResolver#openFileDescriptor(Uri, String)}.
      * <p>
-     * Output: The URI of the item that was picked. This must be a content: URI
-     * so that any receiver can access it. If multiple documents were selected,
-     * they are returned in {@link #getClipData()}.
+     * Output: The URI of the item that was picked. This must be a
+     * {@code content://} URI so that any receiver can access it. If multiple
+     * documents were selected, they are returned in {@link #getClipData()}.
      *
      * @see DocumentsContract
-     * @see DocumentsContract#getOpenDocuments(Context)
+     * @see #ACTION_CREATE_DOCUMENT
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
 
     /**
-     * Activity Action: Allow the user to create a new document. Both read and
-     * write access to the document will be granted until explicitly revoked by
-     * the user.
+     * Activity Action: Allow the user to create a new document. When invoked,
+     * the system will display the various {@link DocumentsProvider} instances
+     * installed on the device, letting the user navigate through them. The
+     * returned document may be a newly created document with no content, or it
+     * may be an existing document with the requested MIME type.
      * <p>
-     * Callers can provide a hint document name by setting {@link #EXTRA_TITLE},
-     * but the user may change this value before creating the file. Callers can
-     * optionally hint at the MIME type being created by setting
-     * {@link #setType(String)}.
+     * Each document is represented as a {@code content://} URI backed by a
+     * {@link DocumentsProvider}, which can be opened as a stream with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}, or queried for
+     * {@link android.provider.DocumentsContract.Document} metadata.
+     * <p>
+     * Callers must indicate the concrete MIME type of the document being
+     * created by setting {@link #setType(String)}. This MIME type cannot be
+     * changed after the document is created.
+     * <p>
+     * Callers can provide an initial display name through {@link #EXTRA_TITLE},
+     * but the user may change this value before creating the file.
      * <p>
      * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
      * returned URIs can be opened with
      * {@link ContentResolver#openFileDescriptor(Uri, String)}.
      * <p>
-     * Output: The URI of the item that was created. This must be a content: URI
-     * so that any receiver can access it.
+     * Output: The URI of the item that was created. This must be a
+     * {@code content://} URI so that any receiver can access it.
      *
      * @see DocumentsContract
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
@@ -2787,11 +2815,16 @@
      * experience).
      */
     public static final String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
+
     /**
-     * Used to indicate that a GET_CONTENT intent only wants URIs that can be opened with
-     * ContentResolver.openInputStream. Openable URIs must support the columns in
-     * {@link android.provider.OpenableColumns}
-     * when queried, though it is allowable for those columns to be blank.
+     * Used to indicate that an intent only wants URIs that can be opened with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}. Openable URIs
+     * must support at least the columns defined in {@link OpenableColumns} when
+     * queried.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_CREATE_DOCUMENT
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
@@ -3218,27 +3251,32 @@
             "android.intent.extra.client_intent";
 
     /**
-     * Used to indicate that a {@link #ACTION_GET_CONTENT} intent should only return
-     * data that is on the local device.  This is a boolean extra; the default
-     * is false.  If true, an implementation of ACTION_GET_CONTENT should only allow
-     * the user to select media that is already on the device, not requiring it
-     * be downloaded from a remote service when opened.  Another way to look
-     * at it is that such content should generally have a "_data" column to the
-     * path of the content on local external storage.
+     * Extra used to indicate that an intent should only return data that is on
+     * the local device. This is a boolean extra; the default is false. If true,
+     * an implementation should only allow the user to select data that is
+     * already on the device, not requiring it be downloaded from a remote
+     * service when opened.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
+     * @see #ACTION_CREATE_DOCUMENT
      */
     public static final String EXTRA_LOCAL_ONLY =
-        "android.intent.extra.LOCAL_ONLY";
+            "android.intent.extra.LOCAL_ONLY";
 
     /**
-     * Used to indicate that a {@link #ACTION_GET_CONTENT} intent can allow the
-     * user to select and return multiple items.  This is a boolean extra; the default
-     * is false.  If true, an implementation of ACTION_GET_CONTENT is allowed to
-     * present the user with a UI where they can pick multiple items that are all
-     * returned to the caller.  When this happens, they should be returned as
-     * the {@link #getClipData()} part of the result Intent.
+     * Extra used to indicate that an intent can allow the user to select and
+     * return multiple items. This is a boolean extra; the default is false. If
+     * true, an implementation is allowed to present the user with a UI where
+     * they can pick multiple items that are all returned to the caller. When
+     * this happens, they should be returned as the {@link #getClipData()} part
+     * of the result Intent.
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
      */
     public static final String EXTRA_ALLOW_MULTIPLE =
-        "android.intent.extra.ALLOW_MULTIPLE";
+            "android.intent.extra.ALLOW_MULTIPLE";
 
     /**
      * The userHandle carried with broadcast intents related to addition, removal and switching of
@@ -3272,9 +3310,13 @@
             "android.intent.extra.restrictions_intent";
 
     /**
-     * Extra used to communicate set of acceptable MIME types for
-     * {@link #ACTION_GET_CONTENT} or {@link #ACTION_OPEN_DOCUMENT}. The type of the
-     * extra is <code>ArrayList&lt;String&gt;</code>.
+     * Extra used to communicate a set of acceptable MIME types. The type of the
+     * extra is {@code String[]}. Values may be a combination of concrete MIME
+     * types (such as "image/png") and/or partial MIME types (such as
+     * "audio/*").
+     *
+     * @see #ACTION_GET_CONTENT
+     * @see #ACTION_OPEN_DOCUMENT
      */
     public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
 
@@ -3295,7 +3337,7 @@
 
     /**
      * If set, the recipient of this Intent will be granted permission to
-     * perform read operations on the Uri in the Intent's data and any URIs
+     * perform read operations on the URI in the Intent's data and any URIs
      * specified in its ClipData.  When applying to an Intent's ClipData,
      * all URIs as well as recursive traversals through data or other ClipData
      * in Intent items will be granted; only the grant flags of the top-level
@@ -3304,7 +3346,7 @@
     public static final int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
     /**
      * If set, the recipient of this Intent will be granted permission to
-     * perform write operations on the Uri in the Intent's data and any URIs
+     * perform write operations on the URI in the Intent's data and any URIs
      * specified in its ClipData.  When applying to an Intent's ClipData,
      * all URIs as well as recursive traversals through data or other ClipData
      * in Intent items will be granted; only the grant flags of the top-level
@@ -3339,7 +3381,7 @@
 
     /**
      * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
-     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the Uri permission grant can be
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the URI permission grant can be
      * persisted across device reboots until explicitly revoked with
      * {@link Context#revokeUriPermission(Uri, int)}. This flag only offers the
      * grant for possible persisting; the receiving application must call
@@ -3349,6 +3391,7 @@
      * @see ContentResolver#takePersistableUriPermission(Uri, int)
      * @see ContentResolver#releasePersistableUriPermission(Uri, int)
      * @see ContentResolver#getPersistedUriPermissions()
+     * @see ContentResolver#getOutgoingPersistedUriPermissions()
      */
     public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
 
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index e4cc77f..28edde0 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -42,17 +43,35 @@
     private final ParcelFileDescriptor mFd;
     private final long mStartOffset;
     private final long mLength;
-    
+    private final Bundle mExtras;
+
     /**
      * Create a new AssetFileDescriptor from the given values.
+     *
      * @param fd The underlying file descriptor.
      * @param startOffset The location within the file that the asset starts.
-     * This must be 0 if length is UNKNOWN_LENGTH.
+     *            This must be 0 if length is UNKNOWN_LENGTH.
      * @param length The number of bytes of the asset, or
-     * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+     *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
      */
     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
             long length) {
+        this(fd, startOffset, length, null);
+    }
+
+    /**
+     * Create a new AssetFileDescriptor from the given values.
+     *
+     * @param fd The underlying file descriptor.
+     * @param startOffset The location within the file that the asset starts.
+     *            This must be 0 if length is UNKNOWN_LENGTH.
+     * @param length The number of bytes of the asset, or
+     *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+     * @param extras additional details that can be used to interpret the
+     *            underlying file descriptor. May be null.
+     */
+    public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
+            long length, Bundle extras) {
         if (fd == null) {
             throw new IllegalArgumentException("fd must not be null");
         }
@@ -63,8 +82,9 @@
         mFd = fd;
         mStartOffset = startOffset;
         mLength = length;
+        mExtras = extras;
     }
-    
+
     /**
      * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
      * in addition to the normal FileDescriptor object also allows you to close
@@ -88,7 +108,15 @@
     public long getStartOffset() {
         return mStartOffset;
     }
-    
+
+    /**
+     * Returns any additional details that can be used to interpret the
+     * underlying file descriptor. May be null.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
     /**
      * Returns the total number of bytes of this asset entry's data.  May be
      * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
@@ -307,25 +335,37 @@
             super.write(oneByte);
         }
     }
-    
-    
+
     /* Parcelable interface */
+    @Override
     public int describeContents() {
         return mFd.describeContents();
     }
 
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         mFd.writeToParcel(out, flags);
         out.writeLong(mStartOffset);
         out.writeLong(mLength);
+        if (mExtras != null) {
+            out.writeInt(1);
+            out.writeBundle(mExtras);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     AssetFileDescriptor(Parcel src) {
         mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
         mStartOffset = src.readLong();
         mLength = src.readLong();
+        if (src.readInt() != 0) {
+            mExtras = src.readBundle();
+        } else {
+            mExtras = null;
+        }
     }
-    
+
     public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
             = new Parcelable.Creator<AssetFileDescriptor>() {
         public AssetFileDescriptor createFromParcel(Parcel in) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index adccbc5..072c5bb 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -92,15 +92,6 @@
     @SuppressWarnings("unchecked")
     @Override
     public <T> T get(Key<T> key) {
-
-        if (key == CaptureResult.STATISTICS_FACES) {
-            /**
-             * FIXME: Workaround for HAL bug that's missing FACE_DETECT_MODE
-             */
-            Log.w(TAG, "Expected non-null android.statistics.faceDetectMode");
-            return null;
-        }
-
         T value = getOverride(key);
         if (value != null) {
             return value;
@@ -452,10 +443,12 @@
     // and managed sides.
     @SuppressWarnings("unchecked")
     private <T> T getOverride(Key<T> key) {
-        if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) {
+        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
             return (T) getAvailableFormats();
-        } else if (key == CaptureResult.STATISTICS_FACES) {
+        } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
             return (T) getFaces();
+        } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
+            return (T) fixFaceRectangles();
         }
 
         // For other keys, get() falls back to getBase()
@@ -476,66 +469,102 @@
     private Face[] getFaces() {
         final int FACE_LANDMARK_SIZE = 6;
 
-        Integer faceDetectMode = getBase(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+        Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
         if (faceDetectMode == null) {
-            throw new AssertionError("Expect non-null face detect mode");
-        }
-
-        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
-            return new Face[0];
-        }
-        if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
-                faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
-            throw new AssertionError("Unknown face detect mode: " + faceDetectMode);
-        }
-
-        // Face scores and rectangles are required by SIMPLE and FULL mode.
-        byte[] faceScores = getBase(CaptureResult.STATISTICS_FACE_SCORES);
-        Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
-        if (faceScores == null || faceRectangles == null) {
-            throw new AssertionError("Expect face scores and rectangles to be non-null");
-        } else if (faceScores.length != faceRectangles.length) {
-            throw new AssertionError(
-                    String.format("Face score size(%d) doesn match face rectangle size(%d)!",
-                            faceScores.length, faceRectangles.length));
-        }
-
-        // Face id and landmarks are only required by FULL mode.
-        int[] faceIds = getBase(CaptureResult.STATISTICS_FACE_IDS);
-        int[] faceLandmarks = getBase(CaptureResult.STATISTICS_FACE_LANDMARKS);
-        int numFaces = faceScores.length;
-        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
-            if (faceIds == null || faceLandmarks == null) {
-                throw new AssertionError("Expect face ids and landmarks to be non-null for " +
-                        "FULL mode");
-            } else if (faceIds.length != numFaces ||
-                    faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
-                throw new AssertionError(
-                        String.format("Face id size(%d), or face landmark size(%d) don't match " +
-                                "face number(%d)!",
-                                faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE,
-                                numFaces));
+            Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
+            faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
+        } else {
+            if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
+                return new Face[0];
+            }
+            if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
+                    faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+                Log.w(TAG, "Unknown face detect mode: " + faceDetectMode);
+                return new Face[0];
             }
         }
 
-        Face[] faces = new Face[numFaces];
+        // Face scores and rectangles are required by SIMPLE and FULL mode.
+        byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
+        Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
+        if (faceScores == null || faceRectangles == null) {
+            Log.w(TAG, "Expect face scores and rectangles to be non-null");
+            return new Face[0];
+        } else if (faceScores.length != faceRectangles.length) {
+            Log.w(TAG, String.format("Face score size(%d) doesn match face rectangle size(%d)!",
+                    faceScores.length, faceRectangles.length));
+        }
+
+        // To be safe, make number of faces is the minimal of all face info metadata length.
+        int numFaces = Math.min(faceScores.length, faceRectangles.length);
+        // Face id and landmarks are only required by FULL mode.
+        int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
+        int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
+        if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+            if (faceIds == null || faceLandmarks == null) {
+                Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
+                        "fallback to SIMPLE mode");
+                faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
+            } else {
+                if (faceIds.length != numFaces ||
+                        faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
+                    Log.w(TAG, String.format("Face id size(%d), or face landmark size(%d) don't" +
+                            "match face number(%d)!",
+                            faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, numFaces));
+                }
+                // To be safe, make number of faces is the minimal of all face info metadata length.
+                numFaces = Math.min(numFaces, faceIds.length);
+                numFaces = Math.min(numFaces, faceLandmarks.length / FACE_LANDMARK_SIZE);
+            }
+        }
+
+        ArrayList<Face> faceList = new ArrayList<Face>();
         if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
             for (int i = 0; i < numFaces; i++) {
-                faces[i] = new Face(faceRectangles[i], faceScores[i]);
+                if (faceScores[i] <= Face.SCORE_MAX &&
+                        faceScores[i] >= Face.SCORE_MIN) {
+                    faceList.add(new Face(faceRectangles[i], faceScores[i]));
+                }
             }
         } else {
             // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL
             for (int i = 0; i < numFaces; i++) {
-                Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
-                Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
-                Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
-                faces[i] = new Face(faceRectangles[i], faceScores[i], faceIds[i],
-                        leftEye, rightEye, mouth);
+                if (faceScores[i] <= Face.SCORE_MAX &&
+                        faceScores[i] >= Face.SCORE_MIN &&
+                        faceIds[i] >= 0) {
+                    Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
+                    Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
+                    Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
+                    Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
+                            leftEye, rightEye, mouth);
+                    faceList.add(face);
+                }
             }
         }
+        Face[] faces = new Face[faceList.size()];
+        faceList.toArray(faces);
         return faces;
     }
 
+    // Face rectangles are defined as (left, top, right, bottom) instead of
+    // (left, top, width, height) at the native level, so the normal Rect
+    // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo
+    // that conversion here for just the faces.
+    private Rect[] fixFaceRectangles() {
+        Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
+        if (faceRectangles == null) return null;
+
+        Rect[] fixedFaceRectangles = new Rect[faceRectangles.length];
+        for (int i = 0; i < faceRectangles.length; i++) {
+            fixedFaceRectangles[i] = new Rect(
+                    faceRectangles[i].left,
+                    faceRectangles[i].top,
+                    faceRectangles[i].right - faceRectangles[i].left,
+                    faceRectangles[i].bottom - faceRectangles[i].top);
+        }
+        return fixedFaceRectangles;
+    }
+
     private <T> void setBase(Key<T> key, T value) {
         int tag = key.getTag();
 
@@ -559,7 +588,7 @@
 
     // Set the camera metadata override.
     private <T> boolean setOverride(Key<T> key, T value) {
-        if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) {
+        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
             return setAvailableFormats((int[]) value);
         }
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 908aadd..01e5bac 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -19,7 +19,13 @@
 import android.view.Display;
 
 /**
- * Represents a virtual display.
+ * Represents a virtual display. The content of a virtual display is rendered to a
+ * {@link android.view.Surface} that you must provide to {@link DisplayManager#createVirtualDisplay
+ * createVirtualDisplay()}.
+ * <p>Because a virtual display renders to a surface provided by the application, it will be
+ * released automatically when the process terminates and all remaining windows on it will
+ * be forcibly removed. However, you should also explicitly call {@link #release} when you're
+ * done with it.
  *
  * @see DisplayManager#createVirtualDisplay
  */
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 486e75a..6743c6c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -49,6 +49,8 @@
  * <h3>Developer Guides</h3>
  * <p>For more information about using NFC, read the
  * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p>
+ * <p>To perform basic file sharing between devices, read
+ * <a href="{@docRoot}training/beam-files/index.html">Sharing Files with NFC</a>.
  * </div>
  */
 public final class NfcAdapter {
@@ -309,8 +311,12 @@
     final Context mContext;
 
     /**
-     * A callback to be invoked when the system has found a tag in
-     * reader mode.
+     * A callback to be invoked when the system finds a tag while the foreground activity is
+     * operating in reader mode.
+     * <p>Register your {@code ReaderCallback} implementation with {@link
+     * NfcAdapter#enableReaderMode} and disable it with {@link
+     * NfcAdapter#disableReaderMode}.
+     * @see NfcAdapter#enableReaderMode
      */
     public interface ReaderCallback {
         public void onTagDiscovered(Tag tag);
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index db5cf1c..b5413db 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -686,7 +686,8 @@
      *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
      */
     public static String getExternalStorageState() {
-        return getStorageState(getExternalStorageDirectory());
+        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        return getStorageState(externalDir);
     }
 
     /**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4d48fd4..ff3e277 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -144,13 +144,6 @@
         }
     }
 
-    /** returns the FAT file system volume ID for the volume mounted 
-     * at the given mount point, or -1 for failure
-     * @param mountPoint point for FAT volume
-     * @return volume ID or -1
-     */
-    public static native int getFatVolumeId(String mountPoint);
-
     /**
      * Perform an fsync on the given FileOutputStream.  The stream at this
      * point must be flushed but not yet closed.
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 78c859e..21e9f6b 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -59,7 +59,6 @@
 
     final MessageQueue mQueue;
     final Thread mThread;
-    volatile boolean mRun;
 
     private Printer mLogging;
 
@@ -187,7 +186,6 @@
 
     private Looper(boolean quitAllowed) {
         mQueue = new MessageQueue(quitAllowed);
-        mRun = true;
         mThread = Thread.currentThread();
     }
 
@@ -300,27 +298,12 @@
     }
 
     public void dump(Printer pw, String prefix) {
-        pw = PrefixPrinter.create(pw, prefix);
-        pw.println(this.toString());
-        pw.println("mRun=" + mRun);
-        pw.println("mThread=" + mThread);
-        pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null"));
-        if (mQueue != null) {
-            synchronized (mQueue) {
-                long now = SystemClock.uptimeMillis();
-                Message msg = mQueue.mMessages;
-                int n = 0;
-                while (msg != null) {
-                    pw.println("  Message " + n + ": " + msg.toString(now));
-                    n++;
-                    msg = msg.next;
-                }
-                pw.println("(Total messages: " + n + ")");
-            }
-        }
+        pw.println(prefix + toString());
+        mQueue.dump(pw, prefix + "  ");
     }
 
     public String toString() {
-        return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
+        return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+                + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
     }
 }
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 0abc149..51203a48 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -428,36 +428,48 @@
     public Message() {
     }
 
+    @Override
     public String toString() {
         return toString(SystemClock.uptimeMillis());
     }
 
     String toString(long now) {
-        StringBuilder   b = new StringBuilder();
-        
-        b.append("{ what=");
-        b.append(what);
+        StringBuilder b = new StringBuilder();
+        b.append("{ when=");
+        TimeUtils.formatDuration(when - now, b);
 
-        b.append(" when=");
-        TimeUtils.formatDuration(when-now, b);
+        if (target != null) {
+            if (callback != null) {
+                b.append(" callback=");
+                b.append(callback.getClass().getName());
+            } else {
+                b.append(" what=");
+                b.append(what);
+            }
 
-        if (arg1 != 0) {
-            b.append(" arg1=");
+            if (arg1 != 0) {
+                b.append(" arg1=");
+                b.append(arg1);
+            }
+
+            if (arg2 != 0) {
+                b.append(" arg2=");
+                b.append(arg2);
+            }
+
+            if (obj != null) {
+                b.append(" obj=");
+                b.append(obj);
+            }
+
+            b.append(" target=");
+            b.append(target.getClass().getName());
+        } else {
+            b.append(" barrier=");
             b.append(arg1);
         }
 
-        if (arg2 != 0) {
-            b.append(" arg2=");
-            b.append(arg2);
-        }
-
-        if (obj != null) {
-            b.append(" obj=");
-            b.append(obj);
-        }
-
         b.append(" }");
-        
         return b.toString();
     }
 
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index d1b8213..799de5c 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -18,6 +18,7 @@
 
 import android.util.AndroidRuntimeException;
 import android.util.Log;
+import android.util.Printer;
 
 import java.util.ArrayList;
 
@@ -252,6 +253,7 @@
         synchronized (this) {
             final int token = mNextBarrierToken++;
             final Message msg = Message.obtain();
+            msg.when = when;
             msg.arg1 = token;
 
             Message prev = null;
@@ -393,12 +395,16 @@
 
     boolean isIdling() {
         synchronized (this) {
-            // If the loop is quitting then it must not be idling.
-            // We can assume mPtr != 0 when mQuitting is false.
-            return !mQuitting && nativeIsIdling(mPtr);
+            return isIdlingLocked();
         }
     }
 
+    private boolean isIdlingLocked() {
+        // If the loop is quitting then it must not be idling.
+        // We can assume mPtr != 0 when mQuitting is false.
+        return !mQuitting && nativeIsIdling(mPtr);
+     }
+
     void removeMessages(Handler h, int what, Object object) {
         if (h == null) {
             return;
@@ -537,4 +543,17 @@
             }
         }
     }
+
+    void dump(Printer pw, String prefix) {
+        synchronized (this) {
+            long now = SystemClock.uptimeMillis();
+            int n = 0;
+            for (Message msg = mMessages; msg != null; msg = msg.next) {
+                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                n++;
+            }
+            pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked()
+                    + ", quitting=" + mQuitting + ")");
+        }
+    }
 }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 55c083b..1456387 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -192,6 +192,7 @@
      * @return a new ParcelFileDescriptor pointing to the given file.
      * @throws FileNotFoundException if the given file does not exist or can not
      *             be opened with the requested mode.
+     * @see #parseMode(String)
      */
     public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
         final FileDescriptor fd = openInternal(file, mode);
@@ -216,6 +217,7 @@
      * @return a new ParcelFileDescriptor pointing to the given file.
      * @throws FileNotFoundException if the given file does not exist or can not
      *             be opened with the requested mode.
+     * @see #parseMode(String)
      */
     public static ParcelFileDescriptor open(
             File file, int mode, Handler handler, OnCloseListener listener) throws IOException {
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 177a955..0285cb9 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -21,6 +21,9 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.CharArrayWriter;
 import java.io.File;
 
 /**
@@ -46,6 +49,10 @@
     /** When set, indicates exclusive ownership of this volume */
     private final UserHandle mOwner;
 
+    private String mUuid;
+    private String mUserLabel;
+    private String mState;
+
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
     // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
@@ -76,6 +83,9 @@
         mAllowMassStorage = in.readInt() != 0;
         mMaxFileSize = in.readLong();
         mOwner = in.readParcelable(null);
+        mUuid = in.readString();
+        mUserLabel = in.readString();
+        mState = in.readString();
     }
 
     public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
@@ -189,6 +199,45 @@
         return mOwner;
     }
 
+    public void setUuid(String uuid) {
+        mUuid = uuid;
+    }
+
+    public String getUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Parse and return volume UUID as FAT volume ID, or return -1 if unable to
+     * parse or UUID is unknown.
+     */
+    public int getFatVolumeId() {
+        if (mUuid == null || mUuid.length() != 9) {
+            return -1;
+        }
+        try {
+            return Integer.parseInt(mUuid.replace("-", ""), 16);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    public void setUserLabel(String userLabel) {
+        mUserLabel = userLabel;
+    }
+
+    public String getUserLabel() {
+        return mUserLabel;
+    }
+
+    public void setState(String state) {
+        mState = state;
+    }
+
+    public String getState() {
+        return mState;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
@@ -205,19 +254,28 @@
 
     @Override
     public String toString() {
-        final StringBuilder builder = new StringBuilder("StorageVolume [");
-        builder.append("mStorageId=").append(mStorageId);
-        builder.append(" mPath=").append(mPath);
-        builder.append(" mDescriptionId=").append(mDescriptionId);
-        builder.append(" mPrimary=").append(mPrimary);
-        builder.append(" mRemovable=").append(mRemovable);
-        builder.append(" mEmulated=").append(mEmulated);
-        builder.append(" mMtpReserveSpace=").append(mMtpReserveSpace);
-        builder.append(" mAllowMassStorage=").append(mAllowMassStorage);
-        builder.append(" mMaxFileSize=").append(mMaxFileSize);
-        builder.append(" mOwner=").append(mOwner);
-        builder.append("]");
-        return builder.toString();
+        final CharArrayWriter writer = new CharArrayWriter();
+        dump(new IndentingPrintWriter(writer, "    ", 80));
+        return writer.toString();
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("StorageVolume:");
+        pw.increaseIndent();
+        pw.printPair("mStorageId", mStorageId);
+        pw.printPair("mPath", mPath);
+        pw.printPair("mDescriptionId", mDescriptionId);
+        pw.printPair("mPrimary", mPrimary);
+        pw.printPair("mRemovable", mRemovable);
+        pw.printPair("mEmulated", mEmulated);
+        pw.printPair("mMtpReserveSpace", mMtpReserveSpace);
+        pw.printPair("mAllowMassStorage", mAllowMassStorage);
+        pw.printPair("mMaxFileSize", mMaxFileSize);
+        pw.printPair("mOwner", mOwner);
+        pw.printPair("mUuid", mUuid);
+        pw.printPair("mUserLabel", mUserLabel);
+        pw.printPair("mState", mState);
+        pw.decreaseIndent();
     }
 
     public static final Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() {
@@ -249,5 +307,8 @@
         parcel.writeInt(mAllowMassStorage ? 1 : 0);
         parcel.writeLong(mMaxFileSize);
         parcel.writeParcelable(mOwner, flags);
+        parcel.writeString(mUuid);
+        parcel.writeString(mUserLabel);
+        parcel.writeString(mState);
     }
 }
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 9f935c8..c6f0a68 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -439,7 +439,7 @@
     /**
      * Sets the included pages.
      *
-     * @return The included pages.
+     * @param pageRanges The included pages.
      *
      * @hide
      */
@@ -601,6 +601,81 @@
         }
     }
 
+    /**
+     * Builder for creating a {@link PrintJobInfo}.
+     */
+    public static final class Builder {
+        private final PrintJobInfo mPrototype;
+
+        /**
+         * Constructor.
+         *
+         * @param prototype Prototype to use as a starting point.
+         * Can be null.
+         */
+        public Builder(PrintJobInfo prototype) {
+            mPrototype = (prototype != null)
+                    ? new PrintJobInfo(prototype)
+                    : new PrintJobInfo();
+        }
+
+        /**
+         * Sets the number of copies.
+         *
+         * @param copies The number of copies.
+         */
+        public void setCopies(int copies) {
+            mPrototype.mCopies = copies;
+        }
+
+        /**
+         * Sets the print job attributes.
+         *
+         * @param attributes The attributes.
+         */
+        public void setAttributes(PrintAttributes attributes) {
+            mPrototype.mAttributes = attributes;
+        }
+
+        /**
+         * Sets the included pages.
+         *
+         * @param pages The included pages.
+         */
+        public void setPages(PageRange[] pages) {
+            mPrototype.mPageRanges = pages;
+        }
+
+        /**
+         * Puts an advanced (printer specific) option.
+         *
+         * @param key The option key.
+         * @param value The option value.
+         */
+        public void putAdvancedOption(String key, String value) {
+
+        }
+
+        /**
+         * Puts an advanced (printer specific) option.
+         *
+         * @param key The option key.
+         * @param value The option value.
+         */
+        public void putAdvancedOption(String key, int value) {
+
+        }
+
+        /**
+         * Creates a new {@link PrintJobInfo} instance.
+         *
+         * @return The new instance.
+         */
+        public PrintJobInfo build() {
+            return mPrototype;
+        }
+    }
+
     public static final Parcelable.Creator<PrintJobInfo> CREATOR =
             new Creator<PrintJobInfo>() {
         @Override
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index d1dbedf..fdeb373 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -304,7 +304,7 @@
     /**
      * Gets the print job tag.
      *
-     * @return tag The tag or null.
+     * @return The tag or null.
      *
      * @see #setTag(String)
      */
@@ -313,6 +313,40 @@
         return getInfo().getTag();
     }
 
+    /**
+     * Gets the value of an advanced (printer specific) print option.
+     *
+     * @param key The option key.
+     * @return The option value.
+     */
+    public String getAdvancedStringOption(String key) {
+        PrintService.throwIfNotCalledOnMainThread();
+        return null;
+    }
+
+    /**
+     * Gets whether this job has a given advanced (printer specific) print
+     * option.
+     *
+     * @param key The option key.
+     * @return Whether the option is present.
+     */
+    public boolean hasAdvancedOption(String key) {
+        PrintService.throwIfNotCalledOnMainThread();
+        return false;
+    }
+
+    /**
+     * Gets the value of an advanced (printer specific) print option.
+     *
+     * @param key The option key.
+     * @return The option value.
+     */
+    public int getAdvancedIntOption(String key) {
+        PrintService.throwIfNotCalledOnMainThread();
+        return 0;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index e73a53b..0fc5f7f 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -16,6 +16,7 @@
 
 package android.printservice;
 
+import android.R;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -189,6 +190,28 @@
      */
     public static final String SERVICE_META_DATA = "android.printservice";
 
+    /**
+     * If you declared an optional activity with advanced print options via the
+     * {@link R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
+     * attribute, this extra is used to pass in the currently constructed {@link
+     * PrintJobInfo} to your activity allowing you to modify it. After you are
+     * done, you must return the modified {@link PrintJobInfo} via the same extra.
+     * <p>
+     * You cannot modify the passed in {@link PrintJobInfo} directly, rather you
+     * should build another one using the {@link PrintJobInfo.Builder} class. You
+     * can specify any standard properties and add advanced, printer specific,
+     * ones via {@link PrintJobInfo.Builder#putAdvancedOption(String, String)
+     * PrintJobInfo.Builder#putAdvancedOption(String, String)} and {@link
+     * PrintJobInfo.Builder#putAdvancedOption(String, int)
+     * PrintJobInfo.Builder#putAdvancedOption(String, int)}. The advanced options
+     * are not interpreted by the system, they will not be visible to applications,
+     * and can only be accessed by your print service via {@link
+     * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)}
+     * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}.
+     * </p>
+     */
+    public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
+
     private Handler mHandler;
 
     private IPrintServiceClient mClient;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index c5e4f21..7f8dca2 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -28,7 +28,9 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
 import android.graphics.Point;
+import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -42,8 +44,10 @@
 import libcore.io.Libcore;
 
 import java.io.BufferedInputStream;
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.List;
 
@@ -69,13 +73,23 @@
     }
 
     /**
-     * Intent action used to identify {@link DocumentsProvider} instances.
+     * Intent action used to identify {@link DocumentsProvider} instances. This
+     * is used in the {@code <intent-filter>} of a {@code <provider>}.
      */
     public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
 
     /** {@hide} */
     public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
 
+    /**
+     * Included in {@link AssetFileDescriptor#getExtras()} when returned
+     * thumbnail should be rotated.
+     *
+     * @see MediaStore.Images.ImageColumns#ORIENTATION
+     * @hide
+     */
+    public static final String EXTRA_ORIENTATION = "android.content.extra.ORIENTATION";
+
     /** {@hide} */
     public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
     /** {@hide} */
@@ -87,12 +101,14 @@
     private static final int THUMBNAIL_BUFFER_SIZE = (int) (128 * KB_IN_BYTES);
 
     /**
-     * Constants related to a document, including {@link Cursor} columns names
+     * Constants related to a document, including {@link Cursor} column names
      * and flags.
      * <p>
-     * A document can be either an openable file (with a specific MIME type), or
-     * a directory containing additional documents (with the
-     * {@link #MIME_TYPE_DIR} MIME type).
+     * A document can be either an openable stream (with a specific MIME type),
+     * or a directory containing additional documents (with the
+     * {@link #MIME_TYPE_DIR} MIME type). A directory represents the top of a
+     * subtree containing zero or more documents, which can recursively contain
+     * even more documents and directories.
      * <p>
      * All columns are <em>read-only</em> to client applications.
      */
@@ -109,7 +125,7 @@
          * single document may be included as a child of multiple directories.
          * <p>
          * A provider must always return durable IDs, since they will be used to
-         * issue long-term Uri permission grants when an application interacts
+         * issue long-term URI permission grants when an application interacts
          * with {@link Intent#ACTION_OPEN_DOCUMENT} and
          * {@link Intent#ACTION_CREATE_DOCUMENT}.
          * <p>
@@ -277,8 +293,11 @@
     }
 
     /**
-     * Constants related to a root of documents, including {@link Cursor}
-     * columns names and flags.
+     * Constants related to a root of documents, including {@link Cursor} column
+     * names and flags. A root is the start of a tree of documents, such as a
+     * physical storage device, or an account. Each root starts at the directory
+     * referenced by {@link Root#COLUMN_DOCUMENT_ID}, which can recursively
+     * contain both documents and directories.
      * <p>
      * All columns are <em>read-only</em> to client applications.
      */
@@ -316,7 +335,8 @@
 
         /**
          * Title for a root, which will be shown to a user. This column is
-         * required.
+         * required. For a single storage service surfacing multiple accounts as
+         * different roots, this title should be the name of the service.
          * <p>
          * Type: STRING
          */
@@ -324,7 +344,9 @@
 
         /**
          * Summary for this root, which may be shown to a user. This column is
-         * optional, and may be {@code null}.
+         * optional, and may be {@code null}. For a single storage service
+         * surfacing multiple accounts as different roots, this summary should
+         * be the name of the account.
          * <p>
          * Type: STRING
          */
@@ -380,11 +402,12 @@
         public static final int FLAG_LOCAL_ONLY = 1 << 1;
 
         /**
-         * Flag indicating that this root can report recently modified
-         * documents.
+         * Flag indicating that this root can be queried to provide recently
+         * modified documents.
          *
          * @see #COLUMN_FLAGS
          * @see DocumentsContract#buildRecentDocumentsUri(String, String)
+         * @see DocumentsProvider#queryRecentDocuments(String, String[])
          */
         public static final int FLAG_SUPPORTS_RECENTS = 1 << 2;
 
@@ -392,6 +415,8 @@
          * Flag indicating that this root supports search.
          *
          * @see #COLUMN_FLAGS
+         * @see DocumentsContract#buildSearchDocumentsUri(String, String,
+         *      String)
          * @see DocumentsProvider#querySearchDocuments(String, String,
          *      String[])
          */
@@ -468,7 +493,7 @@
     private static final String PARAM_MANAGE = "manage";
 
     /**
-     * Build Uri representing the roots of a document provider. When queried, a
+     * Build URI representing the roots of a document provider. When queried, a
      * provider will return one or more rows with columns defined by
      * {@link Root}.
      *
@@ -480,7 +505,7 @@
     }
 
     /**
-     * Build Uri representing the given {@link Root#COLUMN_ROOT_ID} in a
+     * Build URI representing the given {@link Root#COLUMN_ROOT_ID} in a
      * document provider.
      *
      * @see #getRootId(Uri)
@@ -491,7 +516,7 @@
     }
 
     /**
-     * Build Uri representing the recently modified documents of a specific root
+     * Build URI representing the recently modified documents of a specific root
      * in a document provider. When queried, a provider will return zero or more
      * rows with columns defined by {@link Document}.
      *
@@ -505,7 +530,7 @@
     }
 
     /**
-     * Build Uri representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
+     * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
      * document provider. When queried, a provider will return a single row with
      * columns defined by {@link Document}.
      *
@@ -518,7 +543,7 @@
     }
 
     /**
-     * Build Uri representing the children of the given directory in a document
+     * Build URI representing the children of the given directory in a document
      * provider. When queried, a provider will return zero or more rows with
      * columns defined by {@link Document}.
      *
@@ -535,7 +560,7 @@
     }
 
     /**
-     * Build Uri representing a search for matching documents under a specific
+     * Build URI representing a search for matching documents under a specific
      * root in a document provider. When queried, a provider will return zero or
      * more rows with columns defined by {@link Document}.
      *
@@ -551,7 +576,7 @@
     }
 
     /**
-     * Test if the given Uri represents a {@link Document} backed by a
+     * Test if the given URI represents a {@link Document} backed by a
      * {@link DocumentsProvider}.
      */
     public static boolean isDocumentUri(Context context, Uri uri) {
@@ -575,7 +600,7 @@
     }
 
     /**
-     * Extract the {@link Root#COLUMN_ROOT_ID} from the given Uri.
+     * Extract the {@link Root#COLUMN_ROOT_ID} from the given URI.
      */
     public static String getRootId(Uri rootUri) {
         final List<String> paths = rootUri.getPathSegments();
@@ -589,7 +614,7 @@
     }
 
     /**
-     * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given Uri.
+     * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given URI.
      */
     public static String getDocumentId(Uri documentUri) {
         final List<String> paths = documentUri.getPathSegments();
@@ -603,7 +628,7 @@
     }
 
     /**
-     * Extract the search query from a Uri built by
+     * Extract the search query from a URI built by
      * {@link #buildSearchDocumentsUri(String, String, String)}.
      */
     public static String getSearchDocumentsQuery(Uri searchDocumentsUri) {
@@ -621,7 +646,7 @@
     }
 
     /**
-     * Return thumbnail representing the document at the given Uri. Callers are
+     * Return thumbnail representing the document at the given URI. Callers are
      * responsible for their own in-memory caching.
      *
      * @param documentUri document to return thumbnail for, which must have
@@ -629,7 +654,7 @@
      * @param size optimal thumbnail size desired. A provider may return a
      *            thumbnail of a different size, but never more than double the
      *            requested size.
-     * @param signal signal used to indicate that caller is no longer interested
+     * @param signal signal used to indicate if caller is no longer interested
      *            in the thumbnail.
      * @return decoded thumbnail, or {@code null} if problem was encountered.
      * @see DocumentsProvider#openDocumentThumbnail(String, Point,
@@ -657,6 +682,7 @@
         openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
 
         AssetFileDescriptor afd = null;
+        Bitmap bitmap = null;
         try {
             afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
 
@@ -688,21 +714,36 @@
 
             opts.inJustDecodeBounds = false;
             opts.inSampleSize = Math.min(widthSample, heightSample);
-            Log.d(TAG, "Decoding with sample size " + opts.inSampleSize);
             if (is != null) {
                 is.reset();
-                return BitmapFactory.decodeStream(is, null, opts);
+                bitmap = BitmapFactory.decodeStream(is, null, opts);
             } else {
                 try {
                     Libcore.os.lseek(fd, offset, SEEK_SET);
                 } catch (ErrnoException e) {
                     e.rethrowAsIOException();
                 }
-                return BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+            }
+
+            // Transform the bitmap if requested. We use a side-channel to
+            // communicate the orientation, since EXIF thumbnails don't contain
+            // the rotation flags of the original image.
+            final Bundle extras = afd.getExtras();
+            final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
+            if (orientation != 0) {
+                final int width = bitmap.getWidth();
+                final int height = bitmap.getHeight();
+
+                final Matrix m = new Matrix();
+                m.setRotate(orientation, width / 2, height / 2);
+                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
             }
         } finally {
             IoUtils.closeQuietly(afd);
         }
+
+        return bitmap;
     }
 
     /**
@@ -770,4 +811,44 @@
 
         client.call(METHOD_DELETE_DOCUMENT, null, in);
     }
+
+    /**
+     * Open the given image for thumbnail purposes, using any embedded EXIF
+     * thumbnail if available, and providing orientation hints from the parent
+     * image.
+     *
+     * @hide
+     */
+    public static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException {
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                file, ParcelFileDescriptor.MODE_READ_ONLY);
+        Bundle extras = null;
+
+        try {
+            final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
+
+            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    extras = new Bundle(1);
+                    extras.putInt(EXTRA_ORIENTATION, 90);
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    extras = new Bundle(1);
+                    extras.putInt(EXTRA_ORIENTATION, 180);
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    extras = new Bundle(1);
+                    extras.putInt(EXTRA_ORIENTATION, 270);
+                    break;
+            }
+
+            final long[] thumb = exif.getThumbnailRange();
+            if (thumb != null) {
+                return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras);
+            }
+        } catch (IOException e) {
+        }
+
+        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
+    }
 }
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 337b735..c9efb53 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -24,6 +24,7 @@
 import static android.provider.DocumentsContract.getSearchDocumentsQuery;
 
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -39,6 +40,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFileDescriptor.OnCloseListener;
 import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
 import android.util.Log;
 
 import libcore.io.IoUtils;
@@ -46,25 +48,70 @@
 import java.io.FileNotFoundException;
 
 /**
- * Base class for a document provider. A document provider should extend this
- * class and implement the abstract methods.
+ * Base class for a document provider. A document provider offers read and write
+ * access to durable files, such as files stored on a local disk, or files in a
+ * cloud storage service. To create a document provider, extend this class,
+ * implement the abstract methods, and add it to your manifest like this:
+ *
+ * <pre class="prettyprint">&lt;manifest&gt;
+ *    ...
+ *    &lt;application&gt;
+ *        ...
+ *        &lt;provider
+ *            android:name="com.example.MyCloudProvider"
+ *            android:authorities="com.example.mycloudprovider"
+ *            android:exported="true"
+ *            android:grantUriPermissions="true"
+ *            android:permission="android.permission.MANAGE_DOCUMENTS"&gt;
+ *            &lt;intent-filter&gt;
+ *                &lt;action android:name="android.content.action.DOCUMENTS_PROVIDER" /&gt;
+ *            &lt;/intent-filter&gt;
+ *        &lt;/provider&gt;
+ *        ...
+ *    &lt;/application&gt;
+ *&lt;/manifest&gt;</pre>
  * <p>
- * Each document provider expresses one or more "roots" which each serve as the
- * top-level of a tree. For example, a root could represent an account, or a
- * physical storage device. Under each root, documents are referenced by
- * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned.
+ * When defining your provider, you must protect it with
+ * {@link android.Manifest.permission#MANAGE_DOCUMENTS}, which is a permission
+ * only the system can obtain. Applications cannot use a documents provider
+ * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or
+ * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively
+ * navigate and select documents. When a user selects documents through that
+ * UI, the system issues narrow URI permission grants to the requesting
+ * application.
+ * </p>
+ * <h3>Documents</h3>
  * <p>
- * Documents can be either an openable file (with a specific MIME type), or a
+ * A document can be either an openable stream (with a specific MIME type), or a
  * directory containing additional documents (with the
- * {@link Document#MIME_TYPE_DIR} MIME type). Each document can have different
- * capabilities, as described by {@link Document#COLUMN_FLAGS}. The same
- * {@link Document#COLUMN_DOCUMENT_ID} can be included in multiple directories.
+ * {@link Document#MIME_TYPE_DIR} MIME type). Each directory represents the top
+ * of a subtree containing zero or more documents, which can recursively contain
+ * even more documents and directories.
+ * </p>
  * <p>
- * Document providers must be protected with the
- * {@link android.Manifest.permission#MANAGE_DOCUMENTS} permission, which can
- * only be requested by the system. The system-provided UI then issues narrow
- * Uri permission grants for individual documents when the user explicitly picks
- * documents.
+ * Each document can have different capabilities, as described by
+ * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented
+ * as a thumbnail, a provider can set {@link Document#FLAG_SUPPORTS_THUMBNAIL}
+ * and implement
+ * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return
+ * that thumbnail.
+ * </p>
+ * <p>
+ * Each document under a provider is uniquely referenced by its
+ * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned. A
+ * single document can be included in multiple directories when responding to
+ * {@link #queryChildDocuments(String, String[], String)}. For example, a
+ * provider might surface a single photo in multiple locations: once in a
+ * directory of locations, and again in a directory of dates.
+ * </p>
+ * <h3>Roots</h3>
+ * <p>
+ * All documents are surfaced through one or more "roots." Each root represents
+ * the top of a document tree that a user can navigate. For example, a root
+ * could represent an account or a physical storage device. Similar to
+ * documents, each root can have capabilities expressed through
+ * {@link Root#COLUMN_FLAGS}.
+ * </p>
  *
  * @see Intent#ACTION_OPEN_DOCUMENT
  * @see Intent#ACTION_CREATE_DOCUMENT
@@ -114,25 +161,30 @@
     }
 
     /**
-     * Create a new document and return its {@link Document#COLUMN_DOCUMENT_ID}.
-     * A provider must allocate a new {@link Document#COLUMN_DOCUMENT_ID} to
-     * represent the document, which must not change once returned.
+     * Create a new document and return its newly generated
+     * {@link Document#COLUMN_DOCUMENT_ID}. A provider must allocate a new
+     * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must
+     * not change once returned.
      *
-     * @param documentId the parent directory to create the new document under.
-     * @param mimeType the MIME type associated with the new document.
-     * @param displayName the display name of the new document.
+     * @param parentDocumentId the parent directory to create the new document
+     *            under.
+     * @param mimeType the concrete MIME type associated with the new document.
+     *            If the MIME type is not supported, the provider must throw.
+     * @param displayName the display name of the new document. The provider may
+     *            alter this name to meet any internal constraints, such as
+     *            conflicting names.
      */
     @SuppressWarnings("unused")
-    public String createDocument(String documentId, String mimeType, String displayName)
+    public String createDocument(String parentDocumentId, String mimeType, String displayName)
             throws FileNotFoundException {
         throw new UnsupportedOperationException("Create not supported");
     }
 
     /**
-     * Delete the given document. Upon returning, any Uri permission grants for
-     * the given document will be revoked. If additional documents were deleted
-     * as a side effect of this call, such as documents inside a directory, the
-     * implementor is responsible for revoking those permissions.
+     * Delete the requested document. Upon returning, any URI permission grants
+     * for the requested document will be revoked. If additional documents were
+     * deleted as a side effect of this call, such as documents inside a
+     * directory, the implementor is responsible for revoking those permissions.
      *
      * @param documentId the document to delete.
      */
@@ -141,8 +193,35 @@
         throw new UnsupportedOperationException("Delete not supported");
     }
 
+    /**
+     * Return all roots currently provided. A provider must define at least one
+     * root to display to users, and it should avoid making network requests to
+     * keep this request fast.
+     * <p>
+     * Each root is defined by the metadata columns described in {@link Root},
+     * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory
+     * representing a tree of documents to display under that root.
+     * <p>
+     * If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri,
+     * android.database.ContentObserver)} to notify the system.
+     *
+     * @param projection list of {@link Root} columns to put into the cursor. If
+     *            {@code null} all supported columns should be included.
+     */
     public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException;
 
+    /**
+     * Return recently modified documents under the requested root. This will
+     * only be called for roots that advertise
+     * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be
+     * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
+     * limited to only return the 64 most recently modified documents.
+     *
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
+     * @see DocumentsContract#EXTRA_LOADING
+     */
     @SuppressWarnings("unused")
     public Cursor queryRecentDocuments(String rootId, String[] projection)
             throws FileNotFoundException {
@@ -150,18 +229,43 @@
     }
 
     /**
-     * Return metadata for the given document. A provider should avoid making
-     * network requests to keep this request fast.
+     * Return metadata for the single requested document. A provider should
+     * avoid making network requests to keep this request fast.
      *
      * @param documentId the document to return.
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
      */
     public abstract Cursor queryDocument(String documentId, String[] projection)
             throws FileNotFoundException;
 
     /**
-     * Return the children of the given document which is a directory.
+     * Return the children documents contained in the requested directory. This
+     * must only return immediate descendants, as additional queries will be
+     * issued to recursively explore the tree.
+     * <p>
+     * If your provider is cloud-based, and you have some data cached or pinned
+     * locally, you may return the local data immediately, setting
+     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
+     * your provider is still fetching additional data. Then, when the network
+     * data is available, you can call {@link ContentResolver#notifyChange(Uri,
+     * android.database.ContentObserver)} to trigger a requery and return the
+     * complete contents.
      *
      * @param parentDocumentId the directory to return children for.
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
+     * @param sortOrder how to order the rows, formatted as an SQL
+     *            {@code ORDER BY} clause (excluding the ORDER BY itself).
+     *            Passing {@code null} will use the default sort order, which
+     *            may be unordered. This ordering is a hint that can be used to
+     *            prioritize how data is fetched from the network, but UI may
+     *            always enforce a specific ordering.
+     * @see DocumentsContract#EXTRA_LOADING
+     * @see DocumentsContract#EXTRA_INFO
+     * @see DocumentsContract#EXTRA_ERROR
      */
     public abstract Cursor queryChildDocuments(
             String parentDocumentId, String[] projection, String sortOrder)
@@ -176,9 +280,24 @@
     }
 
     /**
-     * Return documents that that match the given query.
+     * Return documents that that match the given query under the requested
+     * root. The returned documents should be sorted by relevance in descending
+     * order. How documents are matched against the query string is an
+     * implementation detail left to each provider, but it's suggested that at
+     * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
+     * case-insensitive fashion.
+     * <p>
+     * Only documents may be returned; directories are not supported in search
+     * results.
      *
      * @param rootId the root to search under.
+     * @param query string to match documents against.
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
+     * @see DocumentsContract#EXTRA_LOADING
+     * @see DocumentsContract#EXTRA_INFO
+     * @see DocumentsContract#EXTRA_ERROR
      */
     @SuppressWarnings("unused")
     public Cursor querySearchDocuments(String rootId, String query, String[] projection)
@@ -187,8 +306,10 @@
     }
 
     /**
-     * Return MIME type for the given document. Must match the value of
-     * {@link Document#COLUMN_MIME_TYPE} for this document.
+     * Return concrete MIME type of the requested document. Must match the value
+     * of {@link Document#COLUMN_MIME_TYPE} for this document. The default
+     * implementation queries {@link #queryDocument(String, String[])}, so
+     * providers may choose to override this as an optimization.
      */
     public String getDocumentType(String documentId) throws FileNotFoundException {
         final Cursor cursor = queryDocument(documentId, null);
@@ -204,18 +325,21 @@
     }
 
     /**
-     * Open and return the requested document. A provider should return a
-     * reliable {@link ParcelFileDescriptor} to detect when the remote caller
-     * has finished reading or writing the document. A provider may return a
-     * pipe or socket pair if the mode is exclusively
-     * {@link ParcelFileDescriptor#MODE_READ_ONLY} or
+     * Open and return the requested document.
+     * <p>
+     * A provider should return a reliable {@link ParcelFileDescriptor} to
+     * detect when the remote caller has finished reading or writing the
+     * document. A provider may return a pipe or socket pair if the mode is
+     * exclusively {@link ParcelFileDescriptor#MODE_READ_ONLY} or
      * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, but complex modes like
      * {@link ParcelFileDescriptor#MODE_READ_WRITE} require a normal file on
-     * disk. If a provider blocks while downloading content, it should
-     * periodically check {@link CancellationSignal#isCanceled()} to abort
-     * abandoned open requests.
+     * disk.
+     * <p>
+     * If a provider blocks while downloading content, it should periodically
+     * check {@link CancellationSignal#isCanceled()} to abort abandoned open
+     * requests.
      *
-     * @param docId the document to return.
+     * @param documentId the document to return.
      * @param mode the mode to open with, such as 'r', 'w', or 'rw'.
      * @param signal used by the caller to signal if the request should be
      *            cancelled.
@@ -223,20 +347,24 @@
      *      OnCloseListener)
      * @see ParcelFileDescriptor#createReliablePipe()
      * @see ParcelFileDescriptor#createReliableSocketPair()
+     * @see ParcelFileDescriptor#parseMode(String)
      */
     public abstract ParcelFileDescriptor openDocument(
-            String docId, String mode, CancellationSignal signal) throws FileNotFoundException;
+            String documentId, String mode, CancellationSignal signal) throws FileNotFoundException;
 
     /**
-     * Open and return a thumbnail of the requested document. A provider should
-     * return a thumbnail closely matching the hinted size, attempting to serve
-     * from a local cache if possible. A provider should never return images
-     * more than double the hinted size. If a provider performs expensive
-     * operations to download or generate a thumbnail, it should periodically
-     * check {@link CancellationSignal#isCanceled()} to abort abandoned
-     * thumbnail requests.
+     * Open and return a thumbnail of the requested document.
+     * <p>
+     * A provider should return a thumbnail closely matching the hinted size,
+     * attempting to serve from a local cache if possible. A provider should
+     * never return images more than double the hinted size.
+     * <p>
+     * If a provider performs expensive operations to download or generate a
+     * thumbnail, it should periodically check
+     * {@link CancellationSignal#isCanceled()} to abort abandoned thumbnail
+     * requests.
      *
-     * @param docId the document to return.
+     * @param documentId the document to return.
      * @param sizeHint hint of the optimal thumbnail dimensions.
      * @param signal used by the caller to signal if the request should be
      *            cancelled.
@@ -244,7 +372,8 @@
      */
     @SuppressWarnings("unused")
     public AssetFileDescriptor openDocumentThumbnail(
-            String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+            String documentId, Point sizeHint, CancellationSignal signal)
+            throws FileNotFoundException {
         throw new UnsupportedOperationException("Thumbnails not supported");
     }
 
@@ -362,7 +491,7 @@
         final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
         final Uri documentUri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
 
-        // Require that caller can manage given document
+        // Require that caller can manage requested document
         final boolean callerHasManage =
                 context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS)
                 == PackageManager.PERMISSION_GRANTED;
@@ -408,7 +537,7 @@
     }
 
     /**
-     * Implementation is provided by the parent class.
+     * Implementation is provided by the parent class. Cannot be overriden.
      *
      * @see #openDocument(String, String, CancellationSignal)
      */
@@ -418,7 +547,7 @@
     }
 
     /**
-     * Implementation is provided by the parent class.
+     * Implementation is provided by the parent class. Cannot be overriden.
      *
      * @see #openDocument(String, String, CancellationSignal)
      */
@@ -429,7 +558,7 @@
     }
 
     /**
-     * Implementation is provided by the parent class.
+     * Implementation is provided by the parent class. Cannot be overriden.
      *
      * @see #openDocumentThumbnail(String, Point, CancellationSignal)
      */
@@ -445,7 +574,7 @@
     }
 
     /**
-     * Implementation is provided by the parent class.
+     * Implementation is provided by the parent class. Cannot be overriden.
      *
      * @see #openDocumentThumbnail(String, Point, CancellationSignal)
      */
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index dae47b8..6cda905 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -67,6 +67,14 @@
     public static final int DENSITY_XHIGH = 320;
 
     /**
+     * Intermediate density for screens that sit somewhere between
+     * {@link #DENSITY_XHIGH} (320dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+     * This is not a density that applications should target, instead relying
+     * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+     */
+    public static final int DENSITY_400 = 400;
+
+    /**
      * Standard quantized DPI for extra-extra-high-density screens.  Applications
      * should not generally worry about this density; relying on XHIGH graphics
      * being scaled up to it should be sufficient for almost all cases.
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8c1cf5f..abd173a 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -253,7 +253,7 @@
      * @param msg The message you would like logged.
      */
     public static int wtf(String tag, String msg) {
-        return wtf(tag, msg, null);
+        return wtf(LOG_ID_MAIN, tag, msg, null, false);
     }
 
     /**
@@ -262,7 +262,7 @@
      * @hide
      */
     public static int wtfStack(String tag, String msg) {
-        return wtfStack(LOG_ID_MAIN, tag, msg);
+        return wtf(LOG_ID_MAIN, tag, msg, null, true);
     }
 
     /**
@@ -272,7 +272,7 @@
      * @param tr An exception to log.
      */
     public static int wtf(String tag, Throwable tr) {
-        return wtf(tag, tr.getMessage(), tr);
+        return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false);
     }
 
     /**
@@ -283,18 +283,13 @@
      * @param tr An exception to log.  May be null.
      */
     public static int wtf(String tag, String msg, Throwable tr) {
-        return wtf(LOG_ID_MAIN, tag, msg, tr);
+        return wtf(LOG_ID_MAIN, tag, msg, tr, false);
     }
 
-    static int wtfStack(int logId, String tag, String msg) {
-        TerribleFailure here = new TerribleFailure("here", null);
-        here.fillInStackTrace();
-        return wtf(logId, tag, msg, here);
-    }
-
-    static int wtf(int logId, String tag, String msg, Throwable tr) {
+    static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack) {
         TerribleFailure what = new TerribleFailure(msg, tr);
-        int bytes = println_native(logId, ASSERT, tag, msg + '\n' + getStackTraceString(tr));
+        int bytes = println_native(logId, ASSERT, tag, msg + '\n'
+                + getStackTraceString(localStack ? what : tr));
         sWtfHandler.onTerribleFailure(tag, what);
         return bytes;
     }
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index a5c22ff..70795bb 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -79,19 +79,19 @@
     }
 
     public static int wtf(String tag, String msg) {
-        return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null);
+        return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, false);
     }
 
     public static int wtfStack(String tag, String msg) {
-        return Log.wtfStack(Log.LOG_ID_SYSTEM, tag, msg);
+        return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, null, true);
     }
 
     public static int wtf(String tag, Throwable tr) {
-        return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr);
+        return Log.wtf(Log.LOG_ID_SYSTEM, tag, tr.getMessage(), tr, false);
     }
 
     public static int wtf(String tag, String msg, Throwable tr) {
-        return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr);
+        return Log.wtf(Log.LOG_ID_SYSTEM, tag, msg, tr, false);
     }
 
     public static int println(int priority, String tag, String msg) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index f28e4b5..f1523ae 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -23,6 +23,9 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
 
 /**
  * Coordinates the timing of animations, input and drawing.
@@ -256,6 +259,15 @@
         return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
     }
 
+    void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        writer.print(prefix); writer.println("Choreographer:");
+        writer.print(innerPrefix); writer.print("mFrameScheduled=");
+                writer.println(mFrameScheduled);
+        writer.print(innerPrefix); writer.print("mLastFrameTime=");
+                writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000));
+    }
+
     /**
      * Posts a callback to run on the next frame.
      * <p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c86bc45..ab93084 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -77,8 +77,10 @@
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -1179,7 +1181,8 @@
             mFullRedrawNeeded = true;
             mLayoutRequested = true;
 
-            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
+            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
+                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
                 // NOTE -- system code, won't try to do compat mode.
                 Point size = new Point();
                 mDisplay.getRealSize(size);
@@ -1273,7 +1276,8 @@
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
 
-                    if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL) {
+                    if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
+                            || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
                         // NOTE -- system code, won't try to do compat mode.
                         Point size = new Point();
                         mDisplay.getRealSize(size);
@@ -3456,6 +3460,12 @@
                 finishInputEvent(q);
             }
         }
+
+        void dump(String prefix, PrintWriter writer) {
+            if (mNext != null) {
+                mNext.dump(prefix, writer);
+            }
+        }
     }
 
     /**
@@ -3593,6 +3603,16 @@
             mQueueLength -= 1;
             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
         }
+
+        @Override
+        void dump(String prefix, PrintWriter writer) {
+            writer.print(prefix);
+            writer.print(getClass().getName());
+            writer.print(": mQueueLength=");
+            writer.println(mQueueLength);
+
+            super.dump(prefix, writer);
+        }
     }
 
     /**
@@ -5199,6 +5219,53 @@
         mView.debug();
     }
 
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        String innerPrefix = prefix + "  ";
+        writer.print(prefix); writer.println("ViewRoot:");
+        writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded);
+                writer.print(" mRemoved="); writer.println(mRemoved);
+        writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
+                writer.println(mConsumeBatchedInputScheduled);
+        writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
+                writer.println(mPendingInputEventCount);
+        writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
+                writer.println(mProcessInputEventsScheduled);
+        writer.print(innerPrefix); writer.print("mTraversalScheduled=");
+                writer.print(mTraversalScheduled);
+        if (mTraversalScheduled) {
+            writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
+        } else {
+            writer.println();
+        }
+        mFirstInputStage.dump(innerPrefix, writer);
+
+        mChoreographer.dump(prefix, writer);
+
+        writer.print(prefix); writer.println("View Hierarchy:");
+        dumpViewHierarchy(innerPrefix, writer, mView);
+    }
+
+    private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
+        writer.print(prefix);
+        if (view == null) {
+            writer.println("null");
+            return;
+        }
+        writer.println(view.toString());
+        if (!(view instanceof ViewGroup)) {
+            return;
+        }
+        ViewGroup grp = (ViewGroup)view;
+        final int N = grp.getChildCount();
+        if (N <= 0) {
+            return;
+        }
+        prefix = prefix + "  ";
+        for (int i=0; i<N; i++) {
+            dumpViewHierarchy(prefix, writer, grp.getChildAt(i));
+        }
+    }
+
     public void dumpGfxInfo(int[] info) {
         info[0] = info[1] = 0;
         if (mView != null) {
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
deleted file mode 100644
index abc078b..0000000
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ /dev/null
@@ -1,976 +0,0 @@
-/*
- * 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.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.speech.tts.TextToSpeech;
-import android.speech.tts.TextToSpeech.Engine;
-import android.speech.tts.TextToSpeech.OnInitListener;
-import android.speech.tts.UtteranceProgressListener;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.webkit.WebViewCore.EventHub;
-
-import org.apache.http.NameValuePair;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Handles injecting accessibility JavaScript and related JavaScript -> Java
- * APIs.
- */
-class AccessibilityInjector {
-    private static final String TAG = AccessibilityInjector.class.getSimpleName();
-
-    private static boolean DEBUG = false;
-
-    // The WebViewClassic this injector is responsible for managing.
-    private final WebViewClassic mWebViewClassic;
-
-    // Cached reference to mWebViewClassic.getContext(), for convenience.
-    private final Context mContext;
-
-    // Cached reference to mWebViewClassic.getWebView(), for convenience.
-    private final WebView mWebView;
-
-    // The Java objects that are exposed to JavaScript.
-    private TextToSpeechWrapper mTextToSpeech;
-    private CallbackHandler mCallback;
-
-    // Lazily loaded helper objects.
-    private AccessibilityManager mAccessibilityManager;
-    private AccessibilityInjectorFallback mAccessibilityInjectorFallback;
-    private JSONObject mAccessibilityJSONObject;
-
-    // Whether the accessibility script has been injected into the current page.
-    private boolean mAccessibilityScriptInjected;
-
-    // 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;
-    @SuppressWarnings("unused")
-    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
-
-    // Alias for TTS API exposed to JavaScript.
-    private static final String ALIAS_TTS_JS_INTERFACE = "accessibility";
-
-    // Alias for traversal callback exposed to JavaScript.
-    private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal";
-
-    // 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);" +
-                    "  })();";
-
-    // Template for JavaScript that performs AndroidVox actions.
-    private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
-            "(function() {" +
-                    "  if ((typeof(cvox) != 'undefined')" +
-                    "      && (cvox != null)" +
-                    "      && (typeof(cvox.ChromeVox) != 'undefined')" +
-                    "      && (cvox.ChromeVox != null)" +
-                    "      && (typeof(cvox.AndroidVox) != 'undefined')" +
-                    "      && (cvox.AndroidVox != null)" +
-                    "      && cvox.ChromeVox.isActive) {" +
-                    "    return cvox.AndroidVox.performAction('%1s');" +
-                    "  } else {" +
-                    "    return false;" +
-                    "  }" +
-                    "})()";
-
-    // JS code used to shut down an active AndroidVox instance.
-    private static final String TOGGLE_CVOX_TEMPLATE =
-            "javascript:(function() {" +
-                    "  if ((typeof(cvox) != 'undefined')" +
-                    "      && (cvox != null)" +
-                    "      && (typeof(cvox.ChromeVox) != 'undefined')" +
-                    "      && (cvox.ChromeVox != null)" +
-                    "      && (typeof(cvox.ChromeVox.host) != 'undefined')" +
-                    "      && (cvox.ChromeVox.host != null)) {" +
-                    "    cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" +
-                    "  }" +
-                    "})();";
-
-    /**
-     * Creates an instance of the AccessibilityInjector based on
-     * {@code webViewClassic}.
-     *
-     * @param webViewClassic The WebViewClassic that this AccessibilityInjector
-     *            manages.
-     */
-    public AccessibilityInjector(WebViewClassic webViewClassic) {
-        mWebViewClassic = webViewClassic;
-        mWebView = webViewClassic.getWebView();
-        mContext = webViewClassic.getContext();
-        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-    }
-
-    /**
-     * If JavaScript is enabled, pauses or resumes AndroidVox.
-     *
-     * @param enabled Whether feedback should be enabled.
-     */
-    public void toggleAccessibilityFeedback(boolean enabled) {
-        if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
-            return;
-        }
-
-        toggleAndroidVox(enabled);
-
-        if (!enabled && (mTextToSpeech != null)) {
-            mTextToSpeech.stop();
-        }
-    }
-
-    /**
-     * Attempts to load scripting interfaces for accessibility.
-     * <p>
-     * This should only be called before a page loads.
-     */
-    public void addAccessibilityApisIfNecessary() {
-        if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
-            return;
-        }
-
-        addTtsApis();
-        addCallbackApis();
-    }
-
-    /**
-     * Attempts to unload scripting interfaces for accessibility.
-     * <p>
-     * This should only be called before a page loads.
-     */
-    private void removeAccessibilityApisIfNecessary() {
-        removeTtsApis();
-        removeCallbackApis();
-    }
-
-    /**
-     * Destroys this accessibility injector.
-     */
-    public void destroy() {
-        if (mTextToSpeech != null) {
-            mTextToSpeech.shutdown();
-            mTextToSpeech = null;
-        }
-
-        if (mCallback != null) {
-            mCallback = null;
-        }
-    }
-
-    private void toggleAndroidVox(boolean state) {
-        if (!mAccessibilityScriptInjected) {
-            return;
-        }
-
-        final String code = String.format(TOGGLE_CVOX_TEMPLATE, state);
-        mWebView.loadUrl(code);
-    }
-
-    /**
-     * Initializes an {@link AccessibilityNodeInfo} with the actions and
-     * movement granularity levels supported by this
-     * {@link AccessibilityInjector}.
-     * <p>
-     * If an action identifier is added in this method, this
-     * {@link AccessibilityInjector} should also return {@code true} from
-     * {@link #supportsAccessibilityAction(int)}.
-     * </p>
-     *
-     * @param info The info to initialize.
-     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
-     */
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
-                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
-                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
-                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
-                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
-        info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
-        info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
-        info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
-        info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
-        info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
-        info.setClickable(true);
-    }
-
-    /**
-     * Returns {@code true} if this {@link AccessibilityInjector} should handle
-     * the specified action.
-     *
-     * @param action An accessibility action identifier.
-     * @return {@code true} if this {@link AccessibilityInjector} should handle
-     *         the specified action.
-     */
-    public boolean supportsAccessibilityAction(int action) {
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
-            case AccessibilityNodeInfo.ACTION_CLICK:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Performs the specified accessibility action.
-     *
-     * @param action The identifier of the action to perform.
-     * @param arguments The action arguments, or {@code null} if no arguments.
-     * @return {@code true} if the action was successful.
-     * @see View#performAccessibilityAction(int, Bundle)
-     */
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (!isAccessibilityEnabled()) {
-            mAccessibilityScriptInjected = false;
-            toggleFallbackAccessibilityInjector(false);
-            return false;
-        }
-
-        if (mAccessibilityScriptInjected) {
-            return sendActionToAndroidVox(action, arguments);
-        }
-
-        if (mAccessibilityInjectorFallback != null) {
-            return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments);
-        }
-
-        return false;
-    }
-
-    /**
-     * Attempts to handle key events when accessibility is turned on.
-     *
-     * @param event The key event to handle.
-     * @return {@code true} if the event was handled.
-     */
-    public boolean handleKeyEventIfNecessary(KeyEvent event) {
-        if (!isAccessibilityEnabled()) {
-            mAccessibilityScriptInjected = false;
-            toggleFallbackAccessibilityInjector(false);
-            return false;
-        }
-
-        if (mAccessibilityScriptInjected) {
-            // 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.
-            if (event.getAction() == KeyEvent.ACTION_UP) {
-                mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
-            } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
-            } else {
-                return false;
-            }
-
-            return true;
-        }
-
-        if (mAccessibilityInjectorFallback != null) {
-            // 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 mAccessibilityInjectorFallback.onKeyEvent(event);
-        }
-
-        return false;
-    }
-
-    /**
-     * Attempts to handle selection change events when accessibility is using a
-     * non-JavaScript method.
-     * <p>
-     * This must not be called from the main thread.
-     *
-     * @param selection The selection string.
-     * @param token The selection request token.
-     */
-    public void onSelectionStringChangedWebCoreThread(String selection, int token) {
-        if (mAccessibilityInjectorFallback != null) {
-            mAccessibilityInjectorFallback.onSelectionStringChangedWebCoreThread(selection, token);
-        }
-    }
-
-    /**
-     * Prepares for injecting accessibility scripts into a new page.
-     *
-     * @param url The URL that will be loaded.
-     */
-    public void onPageStarted(String url) {
-        mAccessibilityScriptInjected = false;
-        if (DEBUG) {
-            Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page");
-        }
-        addAccessibilityApisIfNecessary();
-    }
-
-    /**
-     * Attempts to inject the accessibility script using a {@code <script>} tag.
-     * <p>
-     * This should be called after a page has finished loading.
-     * </p>
-     *
-     * @param url The URL that just finished loading.
-     */
-    public void onPageFinished(String url) {
-        if (!isAccessibilityEnabled()) {
-            toggleFallbackAccessibilityInjector(false);
-            return;
-        }
-
-        toggleFallbackAccessibilityInjector(true);
-
-        if (shouldInjectJavaScript(url)) {
-            // If we're supposed to use the JS screen reader, request a
-            // callback to confirm that CallbackHandler is working.
-            if (DEBUG) {
-                Log.d(TAG, "[" + mWebView.hashCode() + "] Request callback ");
-            }
-
-            mCallback.requestCallback(mWebView, mInjectScriptRunnable);
-        }
-    }
-
-    /**
-     * Runnable used to inject the JavaScript-based screen reader if the
-     * {@link CallbackHandler} API was successfully exposed to JavaScript.
-     */
-    private Runnable mInjectScriptRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG) {
-                Log.d(TAG, "[" + mWebView.hashCode() + "] Received callback");
-            }
-
-            injectJavaScript();
-        }
-    };
-
-    /**
-     * Called by {@link #mInjectScriptRunnable} to inject the JavaScript-based
-     * screen reader after confirming that the {@link CallbackHandler} API is
-     * functional.
-     */
-    private void injectJavaScript() {
-        toggleFallbackAccessibilityInjector(false);
-
-        if (!mAccessibilityScriptInjected) {
-            mAccessibilityScriptInjected = true;
-            final String injectionUrl = getScreenReaderInjectionUrl();
-            mWebView.loadUrl(injectionUrl);
-            if (DEBUG) {
-                Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView");
-            }
-        } else {
-            if (DEBUG) {
-                Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice");
-            }
-        }
-    }
-
-    /**
-     * Adjusts the accessibility injection state to reflect changes in the
-     * JavaScript enabled state.
-     *
-     * @param enabled Whether JavaScript is enabled.
-     */
-    public void updateJavaScriptEnabled(boolean enabled) {
-        if (enabled) {
-            addAccessibilityApisIfNecessary();
-        } else {
-            removeAccessibilityApisIfNecessary();
-        }
-
-        // We have to reload the page after adding or removing APIs.
-        mWebView.reload();
-    }
-
-    /**
-     * Toggles the non-JavaScript method for handling accessibility.
-     *
-     * @param enabled {@code true} to enable the non-JavaScript method, or
-     *            {@code false} to disable it.
-     */
-    private void toggleFallbackAccessibilityInjector(boolean enabled) {
-        if (enabled && (mAccessibilityInjectorFallback == null)) {
-            mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic);
-        } else {
-            mAccessibilityInjectorFallback = null;
-        }
-    }
-
-    /**
-     * Determines whether it's okay to inject JavaScript into a given URL.
-     *
-     * @param url The URL to check.
-     * @return {@code true} if JavaScript should be injected, {@code false} if a
-     *         non-JavaScript method should be used.
-     */
-    private boolean shouldInjectJavaScript(String url) {
-        // Respect the WebView's JavaScript setting.
-        if (!isJavaScriptEnabled()) {
-            return false;
-        }
-
-        // Allow the page to opt out of Accessibility script injection.
-        if (getAxsUrlParameterValue(url) == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
-            return false;
-        }
-
-        // The user must explicitly enable Accessibility script injection.
-        if (!isScriptInjectionEnabled()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @return {@code true} if the user has explicitly enabled Accessibility
-     *         script injection.
-     */
-    private boolean isScriptInjectionEnabled() {
-        final int injectionSetting = Settings.Secure.getInt(
-                mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0);
-        return (injectionSetting == 1);
-    }
-
-    /**
-     * Attempts to initialize and add interfaces for TTS, if that hasn't already
-     * been done.
-     */
-    private void addTtsApis() {
-        if (mTextToSpeech == null) {
-            mTextToSpeech = new TextToSpeechWrapper(mContext);
-        }
-
-        mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
-    }
-
-    /**
-     * Attempts to shutdown and remove interfaces for TTS, if that hasn't
-     * already been done.
-     */
-    private void removeTtsApis() {
-        if (mTextToSpeech != null) {
-            mTextToSpeech.stop();
-            mTextToSpeech.shutdown();
-            mTextToSpeech = null;
-        }
-
-        mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE);
-    }
-
-    private void addCallbackApis() {
-        if (mCallback == null) {
-            mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
-        }
-
-        mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE);
-    }
-
-    private void removeCallbackApis() {
-        if (mCallback != null) {
-            mCallback = null;
-        }
-
-        mWebView.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE);
-    }
-
-    /**
-     * Returns the script injection preference requested by the URL, or
-     * {@link #ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED} if the page has no
-     * preference.
-     *
-     * @param url The URL to check.
-     * @return A script injection preference.
-     */
-    private int getAxsUrlParameterValue(String url) {
-        if (url == null) {
-            return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
-        }
-
-        try {
-            final List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), null);
-
-            for (NameValuePair param : params) {
-                if ("axs".equals(param.getName())) {
-                    return verifyInjectionValue(param.getValue());
-                }
-            }
-        } catch (URISyntaxException e) {
-            // Do nothing.
-        } catch (IllegalArgumentException e) {
-            // Catch badly-formed URLs.
-        }
-
-        return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
-    }
-
-    private int verifyInjectionValue(String value) {
-        try {
-            final int parsed = Integer.parseInt(value);
-
-            switch (parsed) {
-                case ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT:
-                    return ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT;
-                case ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED:
-                    return ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED;
-            }
-        } catch (NumberFormatException e) {
-            // Do nothing.
-        }
-
-        return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
-    }
-
-    /**
-     * @return The URL for injecting the screen reader.
-     */
-    private String getScreenReaderInjectionUrl() {
-        final String screenReaderUrl = Settings.Secure.getString(
-                mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
-        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
-    }
-
-    /**
-     * @return {@code true} if JavaScript is enabled in the {@link WebView}
-     *         settings.
-     */
-    private boolean isJavaScriptEnabled() {
-        final WebSettings settings = mWebView.getSettings();
-        if (settings == null) {
-            return false;
-        }
-
-        return settings.getJavaScriptEnabled();
-    }
-
-    /**
-     * @return {@code true} if accessibility is enabled.
-     */
-    private boolean isAccessibilityEnabled() {
-        return mAccessibilityManager.isEnabled();
-    }
-
-    /**
-     * Packs an accessibility action into a JSON object and sends it to AndroidVox.
-     *
-     * @param action The action identifier.
-     * @param arguments The action arguments, if applicable.
-     * @return The result of the action.
-     */
-    private boolean sendActionToAndroidVox(int action, Bundle arguments) {
-        if (mAccessibilityJSONObject == null) {
-            mAccessibilityJSONObject = new JSONObject();
-        } else {
-            // Remove all keys from the object.
-            final Iterator<?> keys = mAccessibilityJSONObject.keys();
-            while (keys.hasNext()) {
-                keys.next();
-                keys.remove();
-            }
-        }
-
-        try {
-            mAccessibilityJSONObject.accumulate("action", action);
-
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-                case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-                    if (arguments != null) {
-                        final int granularity = arguments.getInt(
-                                AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
-                        mAccessibilityJSONObject.accumulate("granularity", granularity);
-                    }
-                    break;
-                case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
-                case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
-                    if (arguments != null) {
-                        final String element = arguments.getString(
-                                AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
-                        mAccessibilityJSONObject.accumulate("element", element);
-                    }
-                    break;
-            }
-        } catch (JSONException e) {
-            return false;
-        }
-
-        final String jsonString = mAccessibilityJSONObject.toString();
-        final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString);
-        return mCallback.performAction(mWebView, jsCode);
-    }
-
-    /**
-     * Used to protect the TextToSpeech class, only exposing the methods we want to expose.
-     */
-    private static class TextToSpeechWrapper {
-        private static final String WRAP_TAG = TextToSpeechWrapper.class.getSimpleName();
-
-        /** Lock used to control access to the TextToSpeech object. */
-        private final Object mTtsLock = new Object();
-
-        private final HashMap<String, String> mTtsParams;
-        private final TextToSpeech mTextToSpeech;
-
-        /**
-         * Whether this wrapper is ready to speak. If this is {@code true} then
-         * {@link #mShutdown} is guaranteed to be {@code false}.
-         */
-        private volatile boolean mReady;
-
-        /**
-         * Whether this wrapper was shut down. If this is {@code true} then
-         * {@link #mReady} is guaranteed to be {@code false}.
-         */
-        private volatile boolean mShutdown;
-
-        public TextToSpeechWrapper(Context context) {
-            if (DEBUG) {
-                Log.d(WRAP_TAG, "[" + hashCode() + "] Initializing text-to-speech on thread "
-                        + Thread.currentThread().getId() + "...");
-            }
-
-            final String pkgName = context.getPackageName();
-
-            mReady = false;
-            mShutdown = false;
-
-            mTtsParams = new HashMap<String, String>();
-            mTtsParams.put(Engine.KEY_PARAM_UTTERANCE_ID, WRAP_TAG);
-
-            mTextToSpeech = new TextToSpeech(
-                    context, mInitListener, null, pkgName + ".**webview**", true);
-            mTextToSpeech.setOnUtteranceProgressListener(mErrorListener);
-        }
-
-        @JavascriptInterface
-        @SuppressWarnings("unused")
-        public boolean isSpeaking() {
-            synchronized (mTtsLock) {
-                if (!mReady) {
-                    return false;
-                }
-
-                return mTextToSpeech.isSpeaking();
-            }
-        }
-
-        @JavascriptInterface
-        @SuppressWarnings("unused")
-        public int speak(String text, int queueMode, HashMap<String, String> params) {
-            synchronized (mTtsLock) {
-                if (!mReady) {
-                    if (DEBUG) {
-                        Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to speak before TTS init");
-                    }
-                    return TextToSpeech.ERROR;
-                } else {
-                    if (DEBUG) {
-                        Log.i(WRAP_TAG, "[" + hashCode() + "] Speak called from JS binder");
-                    }
-                }
-
-                return mTextToSpeech.speak(text, queueMode, params);
-            }
-        }
-
-        @JavascriptInterface
-        @SuppressWarnings("unused")
-        public int stop() {
-            synchronized (mTtsLock) {
-                if (!mReady) {
-                    if (DEBUG) {
-                        Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to stop before initialize");
-                    }
-                    return TextToSpeech.ERROR;
-                } else {
-                    if (DEBUG) {
-                        Log.i(WRAP_TAG, "[" + hashCode() + "] Stop called from JS binder");
-                    }
-                }
-
-                return mTextToSpeech.stop();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        protected void shutdown() {
-            synchronized (mTtsLock) {
-                if (!mReady) {
-                    if (DEBUG) {
-                        Log.w(WRAP_TAG, "[" + hashCode() + "] Called shutdown before initialize");
-                    }
-                } else {
-                    if (DEBUG) {
-                        Log.i(WRAP_TAG, "[" + hashCode() + "] Shutting down text-to-speech from "
-                                + "thread " + Thread.currentThread().getId() + "...");
-                    }
-                }
-                mShutdown = true;
-                mReady = false;
-                mTextToSpeech.shutdown();
-            }
-        }
-
-        private final OnInitListener mInitListener = new OnInitListener() {
-            @Override
-            public void onInit(int status) {
-                synchronized (mTtsLock) {
-                    if (!mShutdown && (status == TextToSpeech.SUCCESS)) {
-                        if (DEBUG) {
-                            Log.d(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
-                                    + "] Initialized successfully");
-                        }
-                        mReady = true;
-                    } else {
-                        if (DEBUG) {
-                            Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
-                                    + "] Failed to initialize");
-                        }
-                        mReady = false;
-                    }
-                }
-            }
-        };
-
-        private final UtteranceProgressListener mErrorListener = new UtteranceProgressListener() {
-            @Override
-            public void onStart(String utteranceId) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onError(String utteranceId) {
-                if (DEBUG) {
-                    Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
-                            + "] Failed to speak utterance");
-                }
-            }
-
-            @Override
-            public void onDone(String utteranceId) {
-                // Do nothing.
-            }
-        };
-    }
-
-    /**
-     * Exposes result interface to JavaScript.
-     */
-    private static class CallbackHandler {
-        private static final String JAVASCRIPT_ACTION_TEMPLATE =
-                "javascript:(function() { %s.onResult(%d, %s); })();";
-
-        // Time in milliseconds to wait for a result before failing.
-        private static final long RESULT_TIMEOUT = 5000;
-
-        private final AtomicInteger mResultIdCounter = new AtomicInteger();
-        private final Object mResultLock = new Object();
-        private final String mInterfaceName;
-        private final Handler mMainHandler;
-
-        private Runnable mCallbackRunnable;
-
-        private boolean mResult = false;
-        private int mResultId = -1;
-
-        private CallbackHandler(String interfaceName) {
-            mInterfaceName = interfaceName;
-            mMainHandler = new Handler();
-        }
-
-        /**
-         * Performs an action and attempts to wait for a result.
-         *
-         * @param webView The WebView to perform the action on.
-         * @param code JavaScript code that evaluates to a result.
-         * @return The result of the action, or false if it timed out.
-         */
-        private boolean performAction(WebView webView, String code) {
-            final int resultId = mResultIdCounter.getAndIncrement();
-            final String url = String.format(
-                    JAVASCRIPT_ACTION_TEMPLATE, mInterfaceName, resultId, code);
-            webView.loadUrl(url);
-
-            return getResultAndClear(resultId);
-        }
-
-        /**
-         * Gets the result of a request to perform an accessibility action.
-         *
-         * @param resultId The result id to match the result with the request.
-         * @return The result of the request.
-         */
-        private boolean getResultAndClear(int resultId) {
-            synchronized (mResultLock) {
-                final boolean success = waitForResultTimedLocked(resultId);
-                final boolean result = success ? mResult : false;
-                clearResultLocked();
-                return result;
-            }
-        }
-
-        /**
-         * Clears the result state.
-         */
-        private void clearResultLocked() {
-            mResultId = -1;
-            mResult = false;
-        }
-
-        /**
-         * Waits up to a given bound for a result of a request and returns it.
-         *
-         * @param resultId The result id to match the result with the request.
-         * @return Whether the result was received.
-         */
-        private boolean waitForResultTimedLocked(int resultId) {
-            final long startTimeMillis = SystemClock.uptimeMillis();
-
-            if (DEBUG) {
-                Log.d(TAG, "Waiting for CVOX result with ID " + resultId + "...");
-            }
-
-            while (true) {
-                // Fail if we received a callback from the future.
-                if (mResultId > resultId) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Aborted CVOX result");
-                    }
-                    return false;
-                }
-
-                final long elapsedTimeMillis = (SystemClock.uptimeMillis() - startTimeMillis);
-
-                // Succeed if we received the callback we were expecting.
-                if (DEBUG) {
-                    Log.w(TAG, "Check " + mResultId + " versus expected " + resultId);
-                }
-                if (mResultId == resultId) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Received CVOX result after " + elapsedTimeMillis + " ms");
-                    }
-                    return true;
-                }
-
-                final long waitTimeMillis = (RESULT_TIMEOUT - elapsedTimeMillis);
-
-                // Fail if we've already exceeded the timeout.
-                if (waitTimeMillis <= 0) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Timed out while waiting for CVOX result");
-                    }
-                    return false;
-                }
-
-                try {
-                    if (DEBUG) {
-                        Log.w(TAG, "Start waiting...");
-                    }
-                    mResultLock.wait(waitTimeMillis);
-                } catch (InterruptedException ie) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Interrupted while waiting for CVOX result");
-                    }
-                }
-            }
-        }
-
-        /**
-         * Callback exposed to JavaScript. Handles returning the result of a
-         * request to a waiting (or potentially timed out) thread.
-         *
-         * @param id The result id of the request as a {@link String}.
-         * @param result The result of the request as a {@link String}.
-         */
-        @JavascriptInterface
-        @SuppressWarnings("unused")
-        public void onResult(String id, String result) {
-            if (DEBUG) {
-                Log.w(TAG, "Saw CVOX result of '" + result + "' for ID " + id);
-            }
-            final int resultId;
-
-            try {
-                resultId = Integer.parseInt(id);
-            } catch (NumberFormatException e) {
-                return;
-            }
-
-            synchronized (mResultLock) {
-                if (resultId > mResultId) {
-                    mResult = Boolean.parseBoolean(result);
-                    mResultId = resultId;
-                } else {
-                    if (DEBUG) {
-                        Log.w(TAG, "Result with ID " + resultId + " was stale vesus " + mResultId);
-                    }
-                }
-                mResultLock.notifyAll();
-            }
-        }
-
-        /**
-         * Requests a callback to ensure that the JavaScript interface for this
-         * object has been added successfully.
-         *
-         * @param webView The web view to request a callback from.
-         * @param callbackRunnable Runnable to execute if a callback is received.
-         */
-        public void requestCallback(WebView webView, Runnable callbackRunnable) {
-            mCallbackRunnable = callbackRunnable;
-
-            webView.loadUrl("javascript:(function() { " + mInterfaceName + ".callback(); })();");
-        }
-
-        @JavascriptInterface
-        @SuppressWarnings("unused")
-        public void callback() {
-            if (mCallbackRunnable != null) {
-                mMainHandler.post(mCallbackRunnable);
-                mCallbackRunnable = null;
-            }
-        }
-    }
-}
diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java
deleted file mode 100644
index 40cc4e9..0000000
--- a/core/java/android/webkit/AccessibilityInjectorFallback.java
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.os.Bundle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.webkit.WebViewCore.EventHub;
-
-import com.android.internal.os.SomeArgs;
-
-import java.util.ArrayList;
-
-/**
- * This class injects accessibility into WebViews with disabled JavaScript or
- * WebViews with enabled JavaScript but for which we have no accessibility
- * script to inject.
- * </p>
- * Note: To avoid changes in the framework upon changing the available
- *       navigation axis, or reordering the navigation axis, or changing
- *       the key bindings, or defining sequence of actions to be bound to
- *       a given key this class is navigation axis agnostic. It is only
- *       aware of one navigation axis which is in fact the default behavior
- *       of webViews while using the DPAD/TrackBall.
- * </p>
- * In general a key binding is a mapping from modifiers + key code to
- * a sequence of actions. For more detail how to specify key bindings refer to
- * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}.
- * </p>
- * The possible actions are invocations to
- * {@link #setCurrentAxis(int, boolean, String)}, or
- * {@link #traverseGivenAxis(int, int, boolean, String, boolean)}
- * {@link #performAxisTransition(int, int, boolean, String)}
- * referred via the values of:
- * {@link #ACTION_SET_CURRENT_AXIS},
- * {@link #ACTION_TRAVERSE_CURRENT_AXIS},
- * {@link #ACTION_TRAVERSE_GIVEN_AXIS},
- * {@link #ACTION_PERFORM_AXIS_TRANSITION},
- * respectively.
- * The arguments for the action invocation are specified as offset
- * hexademical pairs. Note the last argument of the invocation
- * should NOT be specified in the binding as it is provided by
- * this class. For details about the key binding implementation
- * refer to {@link AccessibilityWebContentKeyBinding}.
- */
-class AccessibilityInjectorFallback {
-    private static final String LOG_TAG = "AccessibilityInjector";
-
-    private static final boolean DEBUG = true;
-
-    private static final int ACTION_SET_CURRENT_AXIS = 0;
-    private static final int ACTION_TRAVERSE_CURRENT_AXIS = 1;
-    private static final int ACTION_TRAVERSE_GIVEN_AXIS = 2;
-    private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
-    private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4;
-
-    /** Timeout after which asynchronous granular movement is aborted. */
-    private static final int MODIFY_SELECTION_TIMEOUT = 500;
-
-    // WebView navigation axes from WebViewCore.h, plus an additional axis for
-    // the default behavior.
-    private static final int NAVIGATION_AXIS_CHARACTER = 0;
-    private static final int NAVIGATION_AXIS_WORD = 1;
-    private static final int NAVIGATION_AXIS_SENTENCE = 2;
-    @SuppressWarnings("unused")
-    private static final int NAVIGATION_AXIS_HEADING = 3;
-    @SuppressWarnings("unused")
-    private static final int NAVIGATION_AXIS_SIBLING = 4;
-    @SuppressWarnings("unused")
-    private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5;
-    private static final int NAVIGATION_AXIS_DOCUMENT = 6;
-    private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;
-
-    // WebView navigation directions from WebViewCore.h.
-    private static final int NAVIGATION_DIRECTION_BACKWARD = 0;
-    private static final int NAVIGATION_DIRECTION_FORWARD = 1;
-
-    // these are the same for all instances so make them process wide
-    private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
-        new ArrayList<AccessibilityWebContentKeyBinding>();
-
-    // handle to the WebViewClassic this injector is associated with.
-    private final WebViewClassic mWebView;
-    private final WebView mWebViewInternal;
-
-    // Event scheduled for sending as soon as we receive the selected text.
-    private AccessibilityEvent mScheduledEvent;
-
-    // Token required to send the scheduled event.
-    private int mScheduledToken = 0;
-
-    // the current traversal axis
-    private int mCurrentAxis = 2; // sentence
-
-    // we need to consume the up if we have handled the last down
-    private boolean mLastDownEventHandled;
-
-    // getting two empty selection strings in a row we let the WebView handle the event
-    private boolean mIsLastSelectionStringNull;
-
-    // keep track of last direction
-    private int mLastDirection;
-
-    // Lock used for asynchronous selection callback.
-    private final Object mCallbackLock = new Object();
-
-    // Whether the asynchronous selection callback was received.
-    private boolean mCallbackReceived;
-
-    // Whether the asynchronous selection callback succeeded.
-    private boolean mCallbackResult;
-
-    /**
-     * Creates a new injector associated with a given {@link WebViewClassic}.
-     *
-     * @param webView The associated WebViewClassic.
-     */
-    public AccessibilityInjectorFallback(WebViewClassic webView) {
-        mWebView = webView;
-        mWebViewInternal = mWebView.getWebView();
-        ensureWebContentKeyBindings();
-    }
-
-    /**
-     * Processes a key down <code>event</code>.
-     *
-     * @return True if the event was processed.
-     */
-    public boolean onKeyEvent(KeyEvent event) {
-        // We do not handle ENTER in any circumstances.
-        if (isEnterActionKey(event.getKeyCode())) {
-            return false;
-        }
-
-        if (event.getAction() == KeyEvent.ACTION_UP) {
-            return mLastDownEventHandled;
-        }
-
-        mLastDownEventHandled = false;
-
-        AccessibilityWebContentKeyBinding binding = null;
-        for (AccessibilityWebContentKeyBinding candidate : sBindings) {
-            if (event.getKeyCode() == candidate.getKeyCode()
-                    && event.hasModifiers(candidate.getModifiers())) {
-                binding = candidate;
-                break;
-            }
-        }
-
-        if (binding == null) {
-            return false;
-        }
-
-        for (int i = 0, count = binding.getActionCount(); i < count; i++) {
-            int actionCode = binding.getActionCode(i);
-            String contentDescription = Integer.toHexString(binding.getAction(i));
-            switch (actionCode) {
-                case ACTION_SET_CURRENT_AXIS:
-                    int axis = binding.getFirstArgument(i);
-                    boolean sendEvent = (binding.getSecondArgument(i) == 1);
-                    setCurrentAxis(axis, sendEvent, contentDescription);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_TRAVERSE_CURRENT_AXIS:
-                    int direction = binding.getFirstArgument(i);
-                    // on second null selection string in same direction - WebView handles the event
-                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
-                        mIsLastSelectionStringNull = false;
-                        return false;
-                    }
-                    mLastDirection = direction;
-                    sendEvent = (binding.getSecondArgument(i) == 1);
-                    mLastDownEventHandled = traverseGivenAxis(
-                            direction, mCurrentAxis, sendEvent, contentDescription, false);
-                    break;
-                case ACTION_TRAVERSE_GIVEN_AXIS:
-                    direction = binding.getFirstArgument(i);
-                    // on second null selection string in same direction => WebView handle the event
-                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
-                        mIsLastSelectionStringNull = false;
-                        return false;
-                    }
-                    mLastDirection = direction;
-                    axis =  binding.getSecondArgument(i);
-                    sendEvent = (binding.getThirdArgument(i) == 1);
-                    traverseGivenAxis(direction, axis, sendEvent, contentDescription, false);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_PERFORM_AXIS_TRANSITION:
-                    int fromAxis = binding.getFirstArgument(i);
-                    int toAxis = binding.getSecondArgument(i);
-                    sendEvent = (binding.getThirdArgument(i) == 1);
-                    performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
-                    mLastDownEventHandled = true;
-                    break;
-                case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS:
-                    // This is a special case since we treat the default WebView navigation
-                    // behavior as one of the possible navigation axis the user can use.
-                    // If we are not on the default WebView navigation axis this is NOP.
-                    if (mCurrentAxis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
-                        // While WebVew handles navigation we do not get null selection
-                        // strings so do not check for that here as the cases above.
-                        mLastDirection = binding.getFirstArgument(i);
-                        sendEvent = (binding.getSecondArgument(i) == 1);
-                        traverseGivenAxis(mLastDirection, NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR,
-                            sendEvent, contentDescription, false);
-                        mLastDownEventHandled = false;
-                    } else {
-                        mLastDownEventHandled = true;
-                    }
-                    break;
-                default:
-                    Log.w(LOG_TAG, "Unknown action code: " + actionCode);
-            }
-        }
-
-        return mLastDownEventHandled;
-    }
-
-    /**
-     * Set the current navigation axis.
-     *
-     * @param axis The axis to set.
-     * @param sendEvent Whether to send an accessibility event to
-     *        announce the change.
-     */
-    private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
-        mCurrentAxis = axis;
-        if (sendEvent) {
-            final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(
-                    AccessibilityEvent.TYPE_ANNOUNCEMENT);
-            event.getText().add(String.valueOf(axis));
-            event.setContentDescription(contentDescription);
-            sendAccessibilityEvent(event);
-        }
-    }
-
-    /**
-     * Performs conditional transition one axis to another.
-     *
-     * @param fromAxis The axis which must be the current for the transition to occur.
-     * @param toAxis The axis to which to transition.
-     * @param sendEvent Flag if to send an event to announce successful transition.
-     * @param contentDescription A description of the performed action.
-     */
-    private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
-            String contentDescription) {
-        if (mCurrentAxis == fromAxis) {
-            setCurrentAxis(toAxis, sendEvent, contentDescription);
-        }
-    }
-
-    boolean performAccessibilityAction(int action, Bundle arguments) {
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
-                final int direction = getDirectionForAction(action);
-                final int axis = getAxisForGranularity(arguments.getInt(
-                        AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT));
-                return traverseGivenAxis(direction, axis, true, null, true);
-            }
-            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: {
-                final int direction = getDirectionForAction(action);
-                // TODO: Add support for moving by object.
-                final int axis = NAVIGATION_AXIS_SENTENCE;
-                return traverseGivenAxis(direction, axis, true, null, true);
-            }
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Returns the {@link WebView}-defined direction for the given
-     * {@link AccessibilityNodeInfo}-defined action.
-     *
-     * @param action An accessibility action identifier.
-     * @return A web view navigation direction.
-     */
-    private static int getDirectionForAction(int action) {
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
-            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-                return NAVIGATION_DIRECTION_FORWARD;
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-                return NAVIGATION_DIRECTION_BACKWARD;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Returns the {@link WebView}-defined axis for the given
-     * {@link AccessibilityNodeInfo}-defined granularity.
-     *
-     * @param granularity An accessibility granularity identifier.
-     * @return A web view navigation axis.
-     */
-    private static int getAxisForGranularity(int granularity) {
-        switch (granularity) {
-            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER:
-                return NAVIGATION_AXIS_CHARACTER;
-            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD:
-                return NAVIGATION_AXIS_WORD;
-            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE:
-                return NAVIGATION_AXIS_SENTENCE;
-            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH:
-                // TODO: This should map to object once we implement it.
-                return NAVIGATION_AXIS_SENTENCE;
-            case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE:
-                return NAVIGATION_AXIS_DOCUMENT;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Traverse the document along the given navigation axis.
-     *
-     * @param direction The direction of traversal.
-     * @param axis The axis along which to traverse.
-     * @param sendEvent Whether to send an accessibility event to
-     *        announce the change.
-     * @param contentDescription A description of the performed action.
-     */
-    private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent,
-            String contentDescription, boolean sychronous) {
-        final WebViewCore webViewCore = mWebView.getWebViewCore();
-        if (webViewCore == null) {
-            return false;
-        }
-
-        if (sendEvent) {
-            final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(
-                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
-            // The text will be set upon receiving the selection string.
-            event.setContentDescription(contentDescription);
-            mScheduledEvent = event;
-            mScheduledToken++;
-        }
-
-        // if the axis is the default let WebView handle the event which will
-        // result in cursor ring movement and selection of its content
-        if (axis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
-            return false;
-        }
-
-        final SomeArgs args = SomeArgs.obtain();
-        args.argi1 = direction;
-        args.argi2 = axis;
-        args.argi3 = mScheduledToken;
-
-        // If we don't need synchronous results, just return true.
-        if (!sychronous) {
-            webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args);
-            return true;
-        }
-
-        final boolean callbackResult;
-
-        synchronized (mCallbackLock) {
-            mCallbackReceived = false;
-
-            // Asynchronously changes the selection in WebView, which responds by
-            // calling onSelectionStringChanged().
-            webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args);
-
-            try {
-                mCallbackLock.wait(MODIFY_SELECTION_TIMEOUT);
-            } catch (InterruptedException e) {
-                // Do nothing.
-            }
-
-            callbackResult = mCallbackResult;
-        }
-
-        return (mCallbackReceived && callbackResult);
-    }
-
-    /* package */ void onSelectionStringChangedWebCoreThread(
-            final String selection, final int token) {
-        synchronized (mCallbackLock) {
-            mCallbackReceived = true;
-            mCallbackResult = (selection != null);
-            mCallbackLock.notifyAll();
-        }
-
-        // Managing state and sending events must take place on the UI thread.
-        mWebViewInternal.post(new Runnable() {
-            @Override
-            public void run() {
-                onSelectionStringChangedMainThread(selection, token);
-            }
-        });
-    }
-
-    private void onSelectionStringChangedMainThread(String selection, int token) {
-        if (DEBUG) {
-            Log.d(LOG_TAG, "Selection string: " + selection);
-        }
-
-        if (token != mScheduledToken) {
-            if (DEBUG) {
-                Log.d(LOG_TAG, "Selection string has incorrect token: " + token);
-            }
-            return;
-        }
-
-        mIsLastSelectionStringNull = (selection == null);
-
-        final AccessibilityEvent event = mScheduledEvent;
-        mScheduledEvent = null;
-
-        if ((event != null) && (selection != null)) {
-            event.getText().add(selection);
-            event.setFromIndex(0);
-            event.setToIndex(selection.length());
-            sendAccessibilityEvent(event);
-        }
-    }
-
-    /**
-     * Sends an {@link AccessibilityEvent}.
-     *
-     * @param event The event to send.
-     */
-    private void sendAccessibilityEvent(AccessibilityEvent event) {
-        if (DEBUG) {
-            Log.d(LOG_TAG, "Dispatching: " + event);
-        }
-        // accessibility may be disabled while waiting for the selection string
-        AccessibilityManager accessibilityManager =
-            AccessibilityManager.getInstance(mWebView.getContext());
-        if (accessibilityManager.isEnabled()) {
-            accessibilityManager.sendAccessibilityEvent(event);
-        }
-    }
-
-    /**
-     * @return An accessibility event whose members are populated except its
-     *         text and content description.
-     */
-    private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) {
-        AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        mWebViewInternal.onInitializeAccessibilityEvent(event);
-        return event;
-    }
-
-    /**
-     * Ensures that the Web content key bindings are loaded.
-     */
-    private void ensureWebContentKeyBindings() {
-        if (sBindings.size() > 0) {
-            return;
-        }
-
-        String webContentKeyBindingsString  = Settings.Secure.getString(
-                mWebView.getContext().getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
-
-        SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';');
-        semiColonSplitter.setString(webContentKeyBindingsString);
-
-        while (semiColonSplitter.hasNext()) {
-            String bindingString = semiColonSplitter.next();
-            if (TextUtils.isEmpty(bindingString)) {
-                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: "
-                        + webContentKeyBindingsString);
-                continue;
-            }
-            String[] keyValueArray = bindingString.split("=");
-            if (keyValueArray.length != 2) {
-                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " + bindingString);
-                continue;
-            }
-            try {
-                long keyCodeAndModifiers = Long.decode(keyValueArray[0].trim());
-                String[] actionStrings = keyValueArray[1].split(":");
-                int[] actions = new int[actionStrings.length];
-                for (int i = 0, count = actions.length; i < count; i++) {
-                    actions[i] = Integer.decode(actionStrings[i].trim());
-                }
-                sBindings.add(new AccessibilityWebContentKeyBinding(keyCodeAndModifiers, actions));
-            } catch (NumberFormatException nfe) {
-                Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString);
-            }
-        }
-    }
-
-    private boolean isEnterActionKey(int keyCode) {
-        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER
-                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
-    }
-
-    /**
-     * Represents a web content key-binding.
-     */
-    private static final class AccessibilityWebContentKeyBinding {
-
-        private static final int MODIFIERS_OFFSET = 32;
-        private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L;
-
-        private static final int KEY_CODE_OFFSET = 0;
-        private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL;
-
-        private static final int ACTION_OFFSET = 24;
-        private static final int ACTION_MASK = 0xFF000000;
-
-        private static final int FIRST_ARGUMENT_OFFSET = 16;
-        private static final int FIRST_ARGUMENT_MASK = 0x00FF0000;
-
-        private static final int SECOND_ARGUMENT_OFFSET = 8;
-        private static final int SECOND_ARGUMENT_MASK = 0x0000FF00;
-
-        private static final int THIRD_ARGUMENT_OFFSET = 0;
-        private static final int THIRD_ARGUMENT_MASK = 0x000000FF;
-
-        private final long mKeyCodeAndModifiers;
-
-        private final int [] mActionSequence;
-
-        /**
-         * @return The key code of the binding key.
-         */
-        public int getKeyCode() {
-            return (int) ((mKeyCodeAndModifiers & KEY_CODE_MASK) >> KEY_CODE_OFFSET);
-        }
-
-        /**
-         * @return The meta state of the binding key.
-         */
-        public int getModifiers() {
-            return (int) ((mKeyCodeAndModifiers & MODIFIERS_MASK) >> MODIFIERS_OFFSET);
-        }
-
-        /**
-         * @return The number of actions in the key binding.
-         */
-        public int getActionCount() {
-            return mActionSequence.length;
-        }
-
-        /**
-         * @param index The action for a given action <code>index</code>.
-         */
-        public int getAction(int index) {
-            return mActionSequence[index];
-        }
-
-        /**
-         * @param index The action code for a given action <code>index</code>.
-         */
-        public int getActionCode(int index) {
-            return (mActionSequence[index] & ACTION_MASK) >> ACTION_OFFSET;
-        }
-
-        /**
-         * @param index The first argument for a given action <code>index</code>.
-         */
-        public int getFirstArgument(int index) {
-            return (mActionSequence[index] & FIRST_ARGUMENT_MASK) >> FIRST_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * @param index The second argument for a given action <code>index</code>.
-         */
-        public int getSecondArgument(int index) {
-            return (mActionSequence[index] & SECOND_ARGUMENT_MASK) >> SECOND_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * @param index The third argument for a given action <code>index</code>.
-         */
-        public int getThirdArgument(int index) {
-            return (mActionSequence[index] & THIRD_ARGUMENT_MASK) >> THIRD_ARGUMENT_OFFSET;
-        }
-
-        /**
-         * Creates a new instance.
-         * @param keyCodeAndModifiers The key for the binding (key and modifiers).
-         * @param actionSequence The sequence of action for the binding.
-         */
-        public AccessibilityWebContentKeyBinding(long keyCodeAndModifiers, int[] actionSequence) {
-            mKeyCodeAndModifiers = keyCodeAndModifiers;
-            mActionSequence = actionSequence;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("modifiers: ");
-            builder.append(getModifiers());
-            builder.append(", keyCode: ");
-            builder.append(getKeyCode());
-            builder.append(", actions[");
-            for (int i = 0, count = getActionCount(); i < count; i++) {
-                builder.append("{actionCode");
-                builder.append(i);
-                builder.append(": ");
-                builder.append(getActionCode(i));
-                builder.append(", firstArgument: ");
-                builder.append(getFirstArgument(i));
-                builder.append(", secondArgument: ");
-                builder.append(getSecondArgument(i));
-                builder.append(", thirdArgument: ");
-                builder.append(getThirdArgument(i));
-                builder.append("}");
-            }
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-}
diff --git a/core/java/android/webkit/AutoCompletePopup.java b/core/java/android/webkit/AutoCompletePopup.java
deleted file mode 100644
index c624ce4..0000000
--- a/core/java/android/webkit/AutoCompletePopup.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * 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.os.Handler;
-import android.os.Message;
-import android.text.Editable;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.AbsoluteLayout;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Filter;
-import android.widget.Filterable;
-import android.widget.ListAdapter;
-import android.widget.ListPopupWindow;
-import android.widget.PopupWindow.OnDismissListener;
-
-class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener,
-        OnDismissListener{
-    private static class AnchorView extends View {
-        AnchorView(Context context) {
-            super(context);
-            setFocusable(false);
-            setVisibility(INVISIBLE);
-        }
-    }
-    private static final int AUTOFILL_FORM = 100;
-    private boolean mIsAutoFillProfileSet;
-    private Handler mHandler;
-    private int mQueryId;
-    private ListPopupWindow mPopup;
-    private Filter mFilter;
-    private CharSequence mText;
-    private ListAdapter mAdapter;
-    private View mAnchor;
-    private WebViewClassic.WebViewInputConnection mInputConnection;
-    private WebViewClassic mWebView;
-
-    public AutoCompletePopup(WebViewClassic webView,
-            WebViewClassic.WebViewInputConnection inputConnection) {
-        mInputConnection = inputConnection;
-        mWebView = webView;
-        mHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                case AUTOFILL_FORM:
-                    mWebView.autoFillForm(mQueryId);
-                    break;
-                }
-            }
-        };
-    }
-
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (mPopup == null) {
-            return false;
-        }
-        if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) {
-            // special case for the back key, we do not even try to send it
-            // to the drop down list but instead, consume it immediately
-            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
-                KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
-                if (state != null) {
-                    state.startTracking(event, this);
-                }
-                return true;
-            } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
-                if (state != null) {
-                    state.handleUpEvent(event);
-                }
-                if (event.isTracking() && !event.isCanceled()) {
-                    mPopup.dismiss();
-                    return true;
-                }
-            }
-        }
-        if (mPopup.isShowing()) {
-            return mPopup.onKeyPreIme(keyCode, event);
-        }
-        return false;
-    }
-
-    public void setText(CharSequence text) {
-        mText = text;
-        if (mFilter != null) {
-            mFilter.filter(text, this);
-        }
-    }
-
-    public void setAutoFillQueryId(int queryId) {
-        mQueryId = queryId;
-    }
-
-    public void clearAdapter() {
-        mAdapter = null;
-        mFilter = null;
-        if (mPopup != null) {
-            mPopup.dismiss();
-            mPopup.setAdapter(null);
-        }
-    }
-
-    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
-        ensurePopup();
-        mPopup.setAdapter(adapter);
-        mAdapter = adapter;
-        if (adapter != null) {
-            mFilter = adapter.getFilter();
-            mFilter.filter(mText, this);
-        } else {
-            mFilter = null;
-        }
-        resetRect();
-    }
-
-    public void resetRect() {
-        ensurePopup();
-        int left = mWebView.contentToViewX(mWebView.mEditTextContentBounds.left);
-        int right = mWebView.contentToViewX(mWebView.mEditTextContentBounds.right);
-        int width = right - left;
-        mPopup.setWidth(width);
-
-        int bottom = mWebView.contentToViewY(mWebView.mEditTextContentBounds.bottom);
-        int top = mWebView.contentToViewY(mWebView.mEditTextContentBounds.top);
-        int height = bottom - top;
-
-        AbsoluteLayout.LayoutParams lp =
-                (AbsoluteLayout.LayoutParams) mAnchor.getLayoutParams();
-        boolean needsUpdate = false;
-        if (null == lp) {
-            lp = new AbsoluteLayout.LayoutParams(width, height, left, top);
-        } else {
-            if ((lp.x != left) || (lp.y != top) || (lp.width != width)
-                    || (lp.height != height)) {
-                needsUpdate = true;
-                lp.x = left;
-                lp.y = top;
-                lp.width = width;
-                lp.height = height;
-            }
-        }
-        if (needsUpdate) {
-            mAnchor.setLayoutParams(lp);
-        }
-        if (mPopup.isShowing()) {
-            mPopup.show(); // update its position
-        }
-    }
-
-    // AdapterView.OnItemClickListener implementation
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        if (mPopup == null) {
-            return;
-        }
-        if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) {
-            mText = "";
-            pushTextToInputConnection();
-            // Blank out the text box while we wait for WebCore to fill the form.
-            if (mIsAutoFillProfileSet) {
-                // Call a webview method to tell WebCore to autofill the form.
-                mWebView.autoFillForm(mQueryId);
-            } else {
-                // There is no autofill profile setup yet and the user has
-                // elected to try and set one up. Call through to the
-                // embedder to action that.
-                WebChromeClient webChromeClient = mWebView.getWebChromeClient();
-                if (webChromeClient != null) {
-                    webChromeClient.setupAutoFill(
-                        mHandler.obtainMessage(AUTOFILL_FORM));
-                }
-            }
-        } else {
-            Object selectedItem;
-            if (position < 0) {
-                selectedItem = mPopup.getSelectedItem();
-            } else {
-                selectedItem = mAdapter.getItem(position);
-            }
-            if (selectedItem != null) {
-                setText(mFilter.convertResultToString(selectedItem));
-                pushTextToInputConnection();
-            }
-        }
-        mPopup.dismiss();
-    }
-
-    public void setIsAutoFillProfileSet(boolean isAutoFillProfileSet) {
-        mIsAutoFillProfileSet = isAutoFillProfileSet;
-    }
-
-    private void pushTextToInputConnection() {
-        Editable oldText = mInputConnection.getEditable();
-        mInputConnection.setSelection(0, oldText.length());
-        mInputConnection.replaceSelection(mText);
-        mInputConnection.setSelection(mText.length(), mText.length());
-    }
-
-    @Override
-    public void onFilterComplete(int count) {
-        ensurePopup();
-        boolean showDropDown = (count > 0) &&
-                (mInputConnection.getIsAutoFillable() || mText.length() > 0);
-        if (showDropDown) {
-            if (!mPopup.isShowing()) {
-                // Make sure the list does not obscure the IME when shown for the first time.
-                mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
-            }
-            mPopup.show();
-            mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS);
-        } else {
-            mPopup.dismiss();
-        }
-    }
-
-    @Override
-    public void onDismiss() {
-        mWebView.getWebView().removeView(mAnchor);
-    }
-
-    private void ensurePopup() {
-        if (mPopup == null) {
-            mPopup = new ListPopupWindow(mWebView.getContext());
-            mAnchor = new AnchorView(mWebView.getContext());
-            mWebView.getWebView().addView(mAnchor);
-            mPopup.setOnItemClickListener(this);
-            mPopup.setAnchorView(mAnchor);
-            mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
-        } else if (mWebView.getWebView().indexOfChild(mAnchor) < 0) {
-            mWebView.getWebView().addView(mAnchor);
-        }
-    }
-}
-
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
deleted file mode 100644
index 6955d14..0000000
--- a/core/java/android/webkit/BrowserFrame.java
+++ /dev/null
@@ -1,1351 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.app.ActivityManager;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Bitmap;
-import android.net.ParseException;
-import android.net.Uri;
-import android.net.WebAddress;
-import android.net.http.ErrorStrings;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Surface;
-import android.view.ViewRootImpl;
-import android.view.WindowManager;
-
-import junit.framework.Assert;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-import java.net.URLEncoder;
-import java.security.PrivateKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import com.android.org.conscrypt.OpenSSLKey;
-import com.android.org.conscrypt.OpenSSLKeyHolder;
-
-class BrowserFrame extends Handler {
-
-    private static final String LOGTAG = "webkit";
-
-    /**
-     * Cap the number of LoadListeners that will be instantiated, so
-     * we don't blow the GREF count.  Attempting to queue more than
-     * this many requests will prompt an error() callback on the
-     * request's LoadListener
-     */
-    private final static int MAX_OUTSTANDING_REQUESTS = 300;
-
-    private final CallbackProxy mCallbackProxy;
-    private final WebSettingsClassic mSettings;
-    private final Context mContext;
-    private final WebViewDatabaseClassic mDatabase;
-    private final WebViewCore mWebViewCore;
-    /* package */ boolean mLoadInitFromJava;
-    private int mLoadType;
-    private boolean mFirstLayoutDone = true;
-    private boolean mCommitted = true;
-    // Flag for blocking messages. This is used during destroy() so
-    // that if the UI thread posts any messages after the message
-    // queue has been cleared,they are ignored.
-    private boolean mBlockMessages = false;
-    private int mOrientation = -1;
-
-    // Is this frame the main frame?
-    private boolean mIsMainFrame;
-
-    // Javascript interface object
-    private class JSObject {
-        Object object;
-        boolean requireAnnotation;
-
-        public JSObject(Object object, boolean requireAnnotation) {
-            this.object = object;
-            this.requireAnnotation = requireAnnotation;
-        }
-    }
-
-    // Attached Javascript interfaces
-    private Map<String, JSObject> mJavaScriptObjects;
-    private Set<Object> mRemovedJavaScriptObjects;
-
-    // Key store handler when Chromium HTTP stack is used.
-    private KeyStoreHandler mKeyStoreHandler = null;
-
-    // message ids
-    // a message posted when a frame loading is completed
-    static final int FRAME_COMPLETED = 1001;
-    // orientation change message
-    static final int ORIENTATION_CHANGED = 1002;
-    // a message posted when the user decides the policy
-    static final int POLICY_FUNCTION = 1003;
-
-    // Note: need to keep these in sync with FrameLoaderTypes.h in native
-    static final int FRAME_LOADTYPE_STANDARD = 0;
-    static final int FRAME_LOADTYPE_BACK = 1;
-    static final int FRAME_LOADTYPE_FORWARD = 2;
-    static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3;
-    static final int FRAME_LOADTYPE_RELOAD = 4;
-    static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5;
-    static final int FRAME_LOADTYPE_SAME = 6;
-    static final int FRAME_LOADTYPE_REDIRECT = 7;
-    static final int FRAME_LOADTYPE_REPLACE = 8;
-
-    // A progress threshold to switch from history Picture to live Picture
-    private static final int TRANSITION_SWITCH_THRESHOLD = 75;
-
-    // This is a field accessed by native code as well as package classes.
-    /*package*/ int mNativeFrame;
-
-    // Static instance of a JWebCoreJavaBridge to handle timer and cookie
-    // requests from WebCore.
-    static JWebCoreJavaBridge sJavaBridge;
-
-    private static class ConfigCallback implements ComponentCallbacks {
-        private final ArrayList<WeakReference<Handler>> mHandlers =
-                new ArrayList<WeakReference<Handler>>();
-        private final WindowManager mWindowManager;
-
-        ConfigCallback(WindowManager wm) {
-            mWindowManager = wm;
-        }
-
-        public synchronized void addHandler(Handler h) {
-            // No need to ever remove a Handler. If the BrowserFrame is
-            // destroyed, it will be collected and the WeakReference set to
-            // null. If it happens to still be around during a configuration
-            // change, the message will be ignored.
-            mHandlers.add(new WeakReference<Handler>(h));
-        }
-
-        public void onConfigurationChanged(Configuration newConfig) {
-            if (mHandlers.size() == 0) {
-                return;
-            }
-            int orientation =
-                    mWindowManager.getDefaultDisplay().getOrientation();
-            switch (orientation) {
-                case Surface.ROTATION_90:
-                    orientation = 90;
-                    break;
-                case Surface.ROTATION_180:
-                    orientation = 180;
-                    break;
-                case Surface.ROTATION_270:
-                    orientation = -90;
-                    break;
-                case Surface.ROTATION_0:
-                    orientation = 0;
-                    break;
-                default:
-                    break;
-            }
-            synchronized (this) {
-                // Create a list of handlers to remove. Go ahead and make it
-                // the same size to avoid resizing.
-                ArrayList<WeakReference> handlersToRemove =
-                        new ArrayList<WeakReference>(mHandlers.size());
-                for (WeakReference<Handler> wh : mHandlers) {
-                    Handler h = wh.get();
-                    if (h != null) {
-                        h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED,
-                                    orientation, 0));
-                    } else {
-                        handlersToRemove.add(wh);
-                    }
-                }
-                // Now remove all the null references.
-                for (WeakReference weak : handlersToRemove) {
-                    mHandlers.remove(weak);
-                }
-            }
-        }
-
-        public void onLowMemory() {}
-    }
-    static ConfigCallback sConfigCallback;
-
-    /**
-     * Create a new BrowserFrame to be used in an application.
-     * @param context An application context to use when retrieving assets.
-     * @param w A WebViewCore used as the view for this frame.
-     * @param proxy A CallbackProxy for posting messages to the UI thread and
-     *              querying a client for information.
-     * @param settings A WebSettings object that holds all settings.
-     * XXX: Called by WebCore thread.
-     */
-    public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
-            WebSettingsClassic settings, Map<String, Object> javascriptInterfaces) {
-
-        Context appContext = context.getApplicationContext();
-
-        // Create a global JWebCoreJavaBridge to handle timers and
-        // cookies in the WebCore thread.
-        if (sJavaBridge == null) {
-            sJavaBridge = new JWebCoreJavaBridge();
-            // set WebCore native cache size
-            ActivityManager am = (ActivityManager) context
-                    .getSystemService(Context.ACTIVITY_SERVICE);
-            if (am.getMemoryClass() > 16) {
-                sJavaBridge.setCacheSize(8 * 1024 * 1024);
-            } else {
-                sJavaBridge.setCacheSize(4 * 1024 * 1024);
-            }
-            // create CookieSyncManager with current Context
-            CookieSyncManager.createInstance(appContext);
-            // create PluginManager with current Context
-            PluginManager.getInstance(appContext);
-        }
-
-        if (sConfigCallback == null) {
-            sConfigCallback = new ConfigCallback(
-                    (WindowManager) appContext.getSystemService(
-                            Context.WINDOW_SERVICE));
-            ViewRootImpl.addConfigCallback(sConfigCallback);
-        }
-        sConfigCallback.addHandler(this);
-
-        mJavaScriptObjects = new HashMap<String, JSObject>();
-        addJavaScriptObjects(javascriptInterfaces);
-        mRemovedJavaScriptObjects = new HashSet<Object>();
-
-        mSettings = settings;
-        mContext = context;
-        mCallbackProxy = proxy;
-        mDatabase = WebViewDatabaseClassic.getInstance(appContext);
-        mWebViewCore = w;
-
-        AssetManager am = context.getAssets();
-        nativeCreateFrame(w, am, proxy.getBackForwardList());
-
-        if (DebugFlags.BROWSER_FRAME) {
-            Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
-        }
-    }
-
-    /**
-     * Load a url from the network or the filesystem into the main frame.
-     * Following the same behaviour as Safari, javascript: URLs are not passed
-     * to the main frame, instead they are evaluated immediately.
-     * @param url The url to load.
-     * @param extraHeaders The extra headers sent with this url. This should not
-     *            include the common headers like "user-agent". If it does, it
-     *            will be replaced by the intrinsic value of the WebView.
-     */
-    public void loadUrl(String url, Map<String, String> extraHeaders) {
-        mLoadInitFromJava = true;
-        if (URLUtil.isJavaScriptUrl(url)) {
-            // strip off the scheme and evaluate the string
-            stringByEvaluatingJavaScriptFromString(
-                    url.substring("javascript:".length()));
-        } else {
-            nativeLoadUrl(url, extraHeaders);
-        }
-        mLoadInitFromJava = false;
-    }
-
-    /**
-     * Load a url with "POST" method from the network into the main frame.
-     * @param url The url to load.
-     * @param data The data for POST request.
-     */
-    public void postUrl(String url, byte[] data) {
-        mLoadInitFromJava = true;
-        nativePostUrl(url, data);
-        mLoadInitFromJava = false;
-    }
-
-    /**
-     * Load the content as if it was loaded by the provided base URL. The
-     * historyUrl is used as the history entry for the load data.
-     * 
-     * @param baseUrl Base URL used to resolve relative paths in the content
-     * @param data Content to render in the browser
-     * @param mimeType Mimetype of the data being passed in
-     * @param encoding Character set encoding of the provided data.
-     * @param historyUrl URL to use as the history entry.
-     */
-    public void loadData(String baseUrl, String data, String mimeType,
-            String encoding, String historyUrl) {
-        mLoadInitFromJava = true;
-        if (historyUrl == null || historyUrl.length() == 0) {
-            historyUrl = "about:blank";
-        }
-        if (data == null) {
-            data = "";
-        }
-        
-        // Setup defaults for missing values. These defaults where taken from
-        // WebKit's WebFrame.mm
-        if (baseUrl == null || baseUrl.length() == 0) {
-            baseUrl = "about:blank";
-        }
-        if (mimeType == null || mimeType.length() == 0) {
-            mimeType = "text/html";
-        }
-        nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl);
-        mLoadInitFromJava = false;
-    }
-
-    /**
-     * Saves the contents of the frame as a web archive.
-     *
-     * @param basename The filename where the archive should be placed.
-     * @param autoname If false, takes filename to be a file. If true, filename
-     *                 is assumed to be a directory in which a filename will be
-     *                 chosen according to the url of the current page.
-     */
-    /* package */ String saveWebArchive(String basename, boolean autoname) {
-        return nativeSaveWebArchive(basename, autoname);
-    }
-
-    /**
-     * Go back or forward the number of steps given.
-     * @param steps A negative or positive number indicating the direction
-     *              and number of steps to move.
-     */
-    public void goBackOrForward(int steps) {
-        mLoadInitFromJava = true;
-        nativeGoBackOrForward(steps);
-        mLoadInitFromJava = false;
-    }
-
-    /**
-     * native callback
-     * Report an error to an activity.
-     * @param errorCode The HTTP error code.
-     * @param description Optional human-readable description. If no description
-     *     is given, we'll use a standard localized error message.
-     * @param failingUrl The URL that was being loaded when the error occurred.
-     * TODO: Report all errors including resource errors but include some kind
-     * of domain identifier. Change errorCode to an enum for a cleaner
-     * interface.
-     */
-    private void reportError(int errorCode, String description, String failingUrl) {
-        // As this is called for the main resource and loading will be stopped
-        // after, reset the state variables.
-        resetLoadingStates();
-        if (description == null || description.isEmpty()) {
-            description = ErrorStrings.getString(errorCode, mContext);
-        }
-        mCallbackProxy.onReceivedError(errorCode, description, failingUrl);
-    }
-
-    private void resetLoadingStates() {
-        mCommitted = true;
-        mFirstLayoutDone = true;
-    }
-
-    /* package */boolean committed() {
-        return mCommitted;
-    }
-
-    /* package */boolean firstLayoutDone() {
-        return mFirstLayoutDone;
-    }
-
-    /* package */int loadType() {
-        return mLoadType;
-    }
-
-    /* package */void didFirstLayout() {
-        if (!mFirstLayoutDone) {
-            mFirstLayoutDone = true;
-            // ensure {@link WebViewCore#webkitDraw} is called as we were
-            // blocking the update in {@link #loadStarted}
-            mWebViewCore.contentDraw();
-        }
-    }
-
-    /**
-     * native callback
-     * Indicates the beginning of a new load.
-     * This method will be called once for the main frame.
-     */
-    private void loadStarted(String url, Bitmap favicon, int loadType,
-            boolean isMainFrame) {
-        mIsMainFrame = isMainFrame;
-
-        if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
-            mLoadType = loadType;
-
-            if (isMainFrame) {
-                // Call onPageStarted for main frames.
-                mCallbackProxy.onPageStarted(url, favicon);
-                // as didFirstLayout() is only called for the main frame, reset 
-                // mFirstLayoutDone only for the main frames
-                mFirstLayoutDone = false;
-                mCommitted = false;
-                // remove pending draw to block update until mFirstLayoutDone is
-                // set to true in didFirstLayout()
-                mWebViewCore.clearContent();
-                mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW);
-            }
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private void saveFormData(HashMap<String, String> data) {
-        if (mSettings.getSaveFormData()) {
-            final WebHistoryItem h = mCallbackProxy.getBackForwardList()
-                    .getCurrentItem();
-            if (h != null) {
-                String url = WebTextView.urlForAutoCompleteData(h.getUrl());
-                if (url != null) {
-                    mDatabase.setFormData(url, data);
-                }
-            }
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private boolean shouldSaveFormData() {
-        if (mSettings.getSaveFormData()) {
-            final WebHistoryItem h = mCallbackProxy.getBackForwardList()
-                    .getCurrentItem();
-            return h != null && h.getUrl() != null;
-        }
-        return false;
-    }
-
-    /**
-     * native callback
-     * Indicates the WebKit has committed to the new load
-     */
-    private void transitionToCommitted(int loadType, boolean isMainFrame) {
-        // loadType is not used yet
-        if (isMainFrame) {
-            mCommitted = true;
-            mWebViewCore.getWebViewClassic().mViewManager.postResetStateAll();
-        }
-    }
-
-    /**
-     * native callback
-     * <p>
-     * Indicates the end of a new load.
-     * This method will be called once for the main frame.
-     */
-    private void loadFinished(String url, int loadType, boolean isMainFrame) {
-        // mIsMainFrame and isMainFrame are better be equal!!!
-
-        if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
-            if (isMainFrame) {
-                resetLoadingStates();
-                mCallbackProxy.switchOutDrawHistory();
-                mCallbackProxy.onPageFinished(url);
-            }
-        }
-    }
-
-    /**
-     * Destroy all native components of the BrowserFrame.
-     */
-    public void destroy() {
-        nativeDestroyFrame();
-        mBlockMessages = true;
-        removeCallbacksAndMessages(null);
-    }
-
-    /**
-     * Handle messages posted to us.
-     * @param msg The message to handle.
-     */
-    @Override
-    public void handleMessage(Message msg) {
-        if (mBlockMessages) {
-            return;
-        }
-        switch (msg.what) {
-            case FRAME_COMPLETED: {
-                if (mSettings.getSavePassword() && hasPasswordField()) {
-                    WebHistoryItem item = mCallbackProxy.getBackForwardList()
-                            .getCurrentItem();
-                    if (item != null) {
-                        WebAddress uri = new WebAddress(item.getUrl());
-                        String schemePlusHost = uri.getScheme() + uri.getHost();
-                        String[] up =
-                                WebViewDatabaseClassic.getInstance(mContext)
-                                        .getUsernamePassword(schemePlusHost);
-                        if (up != null && up[0] != null) {
-                            setUsernamePassword(up[0], up[1]);
-                        }
-                    }
-                }
-                break;
-            }
-
-            case POLICY_FUNCTION: {
-                nativeCallPolicyFunction(msg.arg1, msg.arg2);
-                break;
-            }
-
-            case ORIENTATION_CHANGED: {
-                if (mOrientation != msg.arg1) {
-                    mOrientation = msg.arg1;
-                    nativeOrientationChanged(msg.arg1);
-                }
-                break;
-            }
-
-            default:
-                break;
-        }
-    }
-
-    /**
-     * Punch-through for WebCore to set the document
-     * title. Inform the Activity of the new title.
-     * @param title The new title of the document.
-     */
-    private void setTitle(String title) {
-        // FIXME: The activity must call getTitle (a native method) to get the
-        // title. We should try and cache the title if we can also keep it in
-        // sync with the document.
-        mCallbackProxy.onReceivedTitle(title);
-    }
-
-    /**
-     * Retrieves the render tree of this frame and puts it as the object for
-     * the message and sends the message.
-     * @param callback the message to use to send the render tree
-     */
-    public void externalRepresentation(Message callback) {
-        callback.obj = externalRepresentation();;
-        callback.sendToTarget();
-    }
-
-    /**
-     * Return the render tree as a string
-     */
-    private native String externalRepresentation();
-
-    /**
-     * Retrieves the visual text of the frames, puts it as the object for
-     * the message and sends the message.
-     * @param callback the message to use to send the visual text
-     */
-    public void documentAsText(Message callback) {
-        StringBuilder text = new StringBuilder();
-        if (callback.arg1 != 0) {
-            // Dump top frame as text.
-            text.append(documentAsText());
-        }
-        if (callback.arg2 != 0) {
-            // Dump child frames as text.
-            text.append(childFramesAsText());
-        }
-        callback.obj = text.toString();
-        callback.sendToTarget();
-    }
-
-    /**
-     * Return the text drawn on the screen as a string
-     */
-    private native String documentAsText();
-
-    /**
-     * Return the text drawn on the child frames as a string
-     */
-    private native String childFramesAsText();
-
-    /*
-     * This method is called by WebCore to inform the frame that
-     * the Javascript window object has been cleared.
-     * We should re-attach any attached js interfaces.
-     */
-    private void windowObjectCleared(int nativeFramePointer) {
-        Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
-        while (iter.hasNext())  {
-            String interfaceName = iter.next();
-            JSObject jsobject = mJavaScriptObjects.get(interfaceName);
-            if (jsobject != null && jsobject.object != null) {
-                nativeAddJavascriptInterface(nativeFramePointer,
-                        jsobject.object, interfaceName, jsobject.requireAnnotation);
-            }
-        }
-        mRemovedJavaScriptObjects.clear();
-    }
-
-    /*
-     * Add javascript objects to the internal list of objects. The default behavior
-     * is to allow access to inherited methods (no annotation needed). This is only
-     * used when js objects are passed through a constructor (via a hidden constructor).
-     *
-     * @TODO change the default behavior to be compatible with the public addjavascriptinterface
-     */
-    private void addJavaScriptObjects(Map<String, Object> javascriptInterfaces) {
-
-        // TODO in a separate CL provide logic to enable annotations for API level JB_MR1 and above.
-        if (javascriptInterfaces == null) return;
-        Iterator<String> iter = javascriptInterfaces.keySet().iterator();
-        while (iter.hasNext())  {
-            String interfaceName = iter.next();
-            Object object = javascriptInterfaces.get(interfaceName);
-            if (object != null) {
-                mJavaScriptObjects.put(interfaceName, new JSObject(object, false));
-            }
-        }
-    }
-
-    /**
-     * This method is called by WebCore to check whether application
-     * wants to hijack url loading
-     */
-    public boolean handleUrl(String url) {
-        if (mLoadInitFromJava == true) {
-            return false;
-        }
-        if (mCallbackProxy.shouldOverrideUrlLoading(url)) {
-            // if the url is hijacked, reset the state of the BrowserFrame
-            didFirstLayout();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    public void addJavascriptInterface(Object obj, String interfaceName,
-            boolean requireAnnotation) {
-        assert obj != null;
-        removeJavascriptInterface(interfaceName);
-        mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation));
-    }
-
-    public void removeJavascriptInterface(String interfaceName) {
-        // We keep a reference to the removed object because the native side holds only a weak
-        // reference and we need to allow the object to continue to be used until the page has been
-        // navigated.
-        if (mJavaScriptObjects.containsKey(interfaceName)) {
-            mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName));
-        }
-    }
-
-    /**
-     * Called by JNI.  Given a URI, find the associated file and return its size
-     * @param uri A String representing the URI of the desired file.
-     * @return int The size of the given file.
-     */
-    private int getFileSize(String uri) {
-        int size = 0;
-        try {
-            InputStream stream = mContext.getContentResolver()
-                            .openInputStream(Uri.parse(uri));
-            size = stream.available();
-            stream.close();
-        } catch (Exception e) {}
-        return size;
-    }
-
-    /**
-     * Called by JNI.  Given a URI, a buffer, and an offset into the buffer,
-     * copy the resource into buffer.
-     * @param uri A String representing the URI of the desired file.
-     * @param buffer The byte array to copy the data into.
-     * @param offset The offet into buffer to place the data.
-     * @param expectedSize The size that the buffer has allocated for this file.
-     * @return int The size of the given file, or zero if it fails.
-     */
-    private int getFile(String uri, byte[] buffer, int offset,
-            int expectedSize) {
-        int size = 0;
-        try {
-            InputStream stream = mContext.getContentResolver()
-                            .openInputStream(Uri.parse(uri));
-            size = stream.available();
-            if (size <= expectedSize && buffer != null
-                    && buffer.length - offset >= size) {
-                stream.read(buffer, offset, size);
-            } else {
-                size = 0;
-            }
-            stream.close();
-        } catch (java.io.FileNotFoundException e) {
-            Log.e(LOGTAG, "FileNotFoundException:" + e);
-            size = 0;
-        } catch (java.io.IOException e2) {
-            Log.e(LOGTAG, "IOException: " + e2);
-            size = 0;
-        }
-        return size;
-    }
-
-    /**
-     * Get the InputStream for an Android resource
-     * There are three different kinds of android resources:
-     * - file:///android_res
-     * - file:///android_asset
-     * - content://
-     * @param url The url to load.
-     * @return An InputStream to the android resource
-     */
-    private InputStream inputStreamForAndroidResource(String url) {
-        final String ANDROID_ASSET = URLUtil.ASSET_BASE;
-        final String ANDROID_RESOURCE = URLUtil.RESOURCE_BASE;
-        final String ANDROID_CONTENT = URLUtil.CONTENT_BASE;
-
-        if (url.startsWith(ANDROID_RESOURCE)) {
-            url = url.replaceFirst(ANDROID_RESOURCE, "");
-            if (url == null || url.length() == 0) {
-                Log.e(LOGTAG, "url has length 0 " + url);
-                return null;
-            }
-            int slash = url.indexOf('/');
-            int dot = url.indexOf('.', slash);
-            if (slash == -1 || dot == -1) {
-                Log.e(LOGTAG, "Incorrect res path: " + url);
-                return null;
-            }
-            String subClassName = url.substring(0, slash);
-            String fieldName = url.substring(slash + 1, dot);
-            String errorMsg = null;
-            try {
-                final Class<?> d = mContext.getApplicationContext()
-                        .getClassLoader().loadClass(
-                                mContext.getPackageName() + ".R$"
-                                        + subClassName);
-                final java.lang.reflect.Field field = d.getField(fieldName);
-                final int id = field.getInt(null);
-                TypedValue value = new TypedValue();
-                mContext.getResources().getValue(id, value, true);
-                if (value.type == TypedValue.TYPE_STRING) {
-                    return mContext.getAssets().openNonAsset(
-                            value.assetCookie, value.string.toString(),
-                            AssetManager.ACCESS_STREAMING);
-                } else {
-                    // Old stack only supports TYPE_STRING for res files
-                    Log.e(LOGTAG, "not of type string: " + url);
-                    return null;
-                }
-            } catch (Exception e) {
-                Log.e(LOGTAG, "Exception: " + url);
-                return null;
-            }
-        } else if (url.startsWith(ANDROID_ASSET)) {
-            String assetUrl = url.replaceFirst(ANDROID_ASSET, "");
-            try {
-                AssetManager assets = mContext.getAssets();
-                Uri uri = Uri.parse(assetUrl);
-                return assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
-            } catch (IOException e) {
-                return null;
-            } catch (Exception e) {
-                Log.w(LOGTAG, "Problem loading url: " + url, e);
-                return null;
-            }
-        } else if (mSettings.getAllowContentAccess() &&
-                   url.startsWith(ANDROID_CONTENT)) {
-            try {
-                // Strip off MIME type. If we don't do this, we can fail to
-                // load Gmail attachments, because the URL being loaded doesn't
-                // exactly match the URL we have permission to read.
-                int mimeIndex = url.lastIndexOf('?');
-                if (mimeIndex != -1) {
-                    url = url.substring(0, mimeIndex);
-                }
-                Uri uri = Uri.parse(url);
-                return mContext.getContentResolver().openInputStream(uri);
-            } catch (Exception e) {
-                Log.e(LOGTAG, "Exception: " + url);
-                return null;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * If this looks like a POST request (form submission) containing a username
-     * and password, give the user the option of saving them. Will either do
-     * nothing, or block until the UI interaction is complete.
-     *
-     * Called directly by WebKit.
-     *
-     * @param postData The data about to be sent as the body of a POST request.
-     * @param username The username entered by the user (sniffed from the DOM).
-     * @param password The password entered by the user (sniffed from the DOM).
-     */
-    private void maybeSavePassword(
-            byte[] postData, String username, String password) {
-        if (postData == null
-                || username == null || username.isEmpty()
-                || password == null || password.isEmpty()) {
-            return; // No password to save.
-        }
-
-        if (!mSettings.getSavePassword()) {
-            return; // User doesn't want to save passwords.
-        }
-
-        try {
-            if (DebugFlags.BROWSER_FRAME) {
-                Assert.assertNotNull(mCallbackProxy.getBackForwardList()
-                        .getCurrentItem());
-            }
-            WebAddress uri = new WebAddress(mCallbackProxy
-                    .getBackForwardList().getCurrentItem().getUrl());
-            String schemePlusHost = uri.getScheme() + uri.getHost();
-            // Check to see if the username & password appear in
-            // the post data (there could be another form on the
-            // page and that was posted instead.
-            String postString = new String(postData);
-            if (postString.contains(URLEncoder.encode(username)) &&
-                    postString.contains(URLEncoder.encode(password))) {
-                String[] saved = mDatabase.getUsernamePassword(
-                        schemePlusHost);
-                if (saved != null) {
-                    // null username implies that user has chosen not to
-                    // save password
-                    if (saved[0] != null) {
-                        // non-null username implies that user has
-                        // chosen to save password, so update the
-                        // recorded password
-                        mDatabase.setUsernamePassword(schemePlusHost, username, password);
-                    }
-                } else {
-                    // CallbackProxy will handle creating the resume
-                    // message
-                    mCallbackProxy.onSavePassword(schemePlusHost, username,
-                            password, null);
-                }
-            }
-        } catch (ParseException ex) {
-            // if it is bad uri, don't save its password
-        }
-    }
-
-    // Called by jni from the chrome network stack.
-    private WebResourceResponse shouldInterceptRequest(String url) {
-        InputStream androidResource = inputStreamForAndroidResource(url);
-        if (androidResource != null) {
-            return new WebResourceResponse(null, null, androidResource);
-        }
-
-        // Note that we check this after looking for an android_asset or
-        // android_res URL, as we allow those even if file access is disabled.
-        if (!mSettings.getAllowFileAccess() && url.startsWith("file://")) {
-            return new WebResourceResponse(null, null, null);
-        }
-
-        WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url);
-        if (response == null && "browser:incognito".equals(url)) {
-            try {
-                Resources res = mContext.getResources();
-                InputStream ins = res.openRawResource(
-                        com.android.internal.R.raw.incognito_mode_start_page);
-                response = new WebResourceResponse("text/html", "utf8", ins);
-            } catch (NotFoundException ex) {
-                // This shouldn't happen, but try and gracefully handle it jic
-                Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex);
-            }
-        }
-        return response;
-    }
-
-    /**
-     * Set the progress for the browser activity.  Called by native code.
-     * Uses a delay so it does not happen too often.
-     * @param newProgress An int between zero and one hundred representing
-     *                    the current progress percentage of loading the page.
-     */
-    private void setProgress(int newProgress) {
-        mCallbackProxy.onProgressChanged(newProgress);
-        if (newProgress == 100) {
-            sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100);
-        }
-        // FIXME: Need to figure out a better way to switch out of the history
-        // drawing mode. Maybe we can somehow compare the history picture with 
-        // the current picture, and switch when it contains more content.
-        if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) {
-            mCallbackProxy.switchOutDrawHistory();
-        }
-    }
-
-    /**
-     * Send the icon to the activity for display.
-     * @param icon A Bitmap representing a page's favicon.
-     */
-    private void didReceiveIcon(Bitmap icon) {
-        mCallbackProxy.onReceivedIcon(icon);
-    }
-
-    // Called by JNI when an apple-touch-icon attribute was found.
-    private void didReceiveTouchIconUrl(String url, boolean precomposed) {
-        mCallbackProxy.onReceivedTouchIconUrl(url, precomposed);
-    }
-
-    /**
-     * Request a new window from the client.
-     * @return The BrowserFrame object stored in the new WebView.
-     */
-    private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
-        return mCallbackProxy.createWindow(dialog, userGesture);
-    }
-
-    /**
-     * Try to focus this WebView.
-     */
-    private void requestFocus() {
-        mCallbackProxy.onRequestFocus();
-    }
-
-    /**
-     * Close this frame and window.
-     */
-    private void closeWindow(WebViewCore w) {
-        mCallbackProxy.onCloseWindow(w.getWebViewClassic());
-    }
-
-    // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore
-    static final int POLICY_USE = 0;
-    static final int POLICY_IGNORE = 2;
-
-    private void decidePolicyForFormResubmission(int policyFunction) {
-        Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction,
-                POLICY_IGNORE);
-        Message resend = obtainMessage(POLICY_FUNCTION, policyFunction,
-                POLICY_USE);
-        mCallbackProxy.onFormResubmission(dontResend, resend);
-    }
-
-    /**
-     * Tell the activity to update its global history.
-     */
-    private void updateVisitedHistory(String url, boolean isReload) {
-        mCallbackProxy.doUpdateVisitedHistory(url, isReload);
-    }
-
-    /**
-     * Get the CallbackProxy for sending messages to the UI thread.
-     */
-    /* package */ CallbackProxy getCallbackProxy() {
-        return mCallbackProxy;
-    }
-
-    /**
-     * Returns the User Agent used by this frame
-     */
-    String getUserAgentString() {
-        return mSettings.getUserAgentString();
-    }
-
-    // These ids need to be in sync with enum rawResId in PlatformBridge.h
-    private static final int NODOMAIN = 1;
-    private static final int LOADERROR = 2;
-    /* package */ static final int DRAWABLEDIR = 3;
-    private static final int FILE_UPLOAD_LABEL = 4;
-    private static final int RESET_LABEL = 5;
-    private static final int SUBMIT_LABEL = 6;
-    private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
-
-    private String getRawResFilename(int id) {
-        return getRawResFilename(id, mContext);
-    }
-    /* package */ static String getRawResFilename(int id, Context context) {
-        int resid;
-        switch (id) {
-            case NODOMAIN:
-                resid = com.android.internal.R.raw.nodomain;
-                break;
-
-            case LOADERROR:
-                resid = com.android.internal.R.raw.loaderror;
-                break;
-
-            case DRAWABLEDIR:
-                // use one known resource to find the drawable directory
-                resid = com.android.internal.R.drawable.btn_check_off;
-                break;
-
-            case FILE_UPLOAD_LABEL:
-                return context.getResources().getString(
-                        com.android.internal.R.string.upload_file);
-
-            case RESET_LABEL:
-                return context.getResources().getString(
-                        com.android.internal.R.string.reset);
-
-            case SUBMIT_LABEL:
-                return context.getResources().getString(
-                        com.android.internal.R.string.submit);
-
-            case FILE_UPLOAD_NO_FILE_CHOSEN:
-                return context.getResources().getString(
-                        com.android.internal.R.string.no_file_chosen);
-
-            default:
-                Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
-                return "";
-        }
-        TypedValue value = new TypedValue();
-        context.getResources().getValue(resid, value, true);
-        if (id == DRAWABLEDIR) {
-            String path = value.string.toString();
-            int index = path.lastIndexOf('/');
-            if (index < 0) {
-                Log.e(LOGTAG, "Can't find drawable directory.");
-                return "";
-            }
-            return path.substring(0, index + 1);
-        }
-        return value.string.toString();
-    }
-
-    private float density() {
-        return WebViewCore.getFixedDisplayDensity(mContext);
-    }
-
-    /**
-     * Called by JNI when the native HTTP stack gets an authentication request.
-     *
-     * We delegate the request to CallbackProxy, and route its response to
-     * {@link #nativeAuthenticationProceed(int, String, String)} or
-     * {@link #nativeAuthenticationCancel(int)}.
-     *
-     * We don't care what thread the callback is invoked on. All threading is
-     * handled on the C++ side, because the WebKit thread may be blocked on a
-     * synchronous call and unable to pump our MessageQueue.
-     */
-    private void didReceiveAuthenticationChallenge(
-            final int handle, String host, String realm, final boolean useCachedCredentials,
-            final boolean suppressDialog) {
-
-        HttpAuthHandler handler = new HttpAuthHandler() {
-
-            @Override
-            public boolean useHttpAuthUsernamePassword() {
-                return useCachedCredentials;
-            }
-
-            @Override
-            public void proceed(String username, String password) {
-                nativeAuthenticationProceed(handle, username, password);
-            }
-
-            @Override
-            public void cancel() {
-                nativeAuthenticationCancel(handle);
-            }
-
-            @Override
-            public boolean suppressDialog() {
-                return suppressDialog;
-            }
-        };
-        mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
-    }
-
-    /**
-     * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain.
-     *
-     * We delegate the request to CallbackProxy, and route its response to
-     * {@link #nativeSslCertErrorProceed(int)} or
-     * {@link #nativeSslCertErrorCancel(int, int)}.
-     */
-    private void reportSslCertError(final int handle, final int certError, byte certDER[],
-            String url) {
-        final SslError sslError;
-        try {
-            CertificateFactory cf = CertificateFactory.getInstance("X.509");
-            X509Certificate cert = (X509Certificate) cf.generateCertificate(
-                    new ByteArrayInputStream(certDER));
-            SslCertificate sslCert = new SslCertificate(cert);
-            sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url);
-        } catch (Exception e) {
-            // Can't get the certificate, not much to do.
-            Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
-            nativeSslCertErrorCancel(handle, certError);
-            return;
-        }
-
-        if (SslCertLookupTable.getInstance().isAllowed(sslError)) {
-            nativeSslCertErrorProceed(handle);
-            mCallbackProxy.onProceededAfterSslError(sslError);
-            return;
-        }
-
-        SslErrorHandler handler = new SslErrorHandler() {
-            @Override
-            public void proceed() {
-                SslCertLookupTable.getInstance().setIsAllowed(sslError);
-                post(new Runnable() {
-                        public void run() {
-                            nativeSslCertErrorProceed(handle);
-                        }
-                    });
-            }
-            @Override
-            public void cancel() {
-                post(new Runnable() {
-                        public void run() {
-                            nativeSslCertErrorCancel(handle, certError);
-                        }
-                    });
-            }
-        };
-        mCallbackProxy.onReceivedSslError(handler, sslError);
-    }
-
-    /**
-     * Called by JNI when the native HTTPS stack gets a client
-     * certificate request.
-     *
-     * We delegate the request to CallbackProxy, and route its response to
-     * {@link #nativeSslClientCert(int, X509Certificate)}.
-     */
-    private void requestClientCert(int handle, String hostAndPort) {
-        SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
-        if (table.IsAllowed(hostAndPort)) {
-            // previously allowed
-            PrivateKey pkey = table.PrivateKey(hostAndPort);
-            if (pkey instanceof OpenSSLKeyHolder) {
-                OpenSSLKey sslKey = ((OpenSSLKeyHolder) pkey).getOpenSSLKey();
-                nativeSslClientCert(handle,
-                                    sslKey.getPkeyContext(),
-                                    table.CertificateChain(hostAndPort));
-            } else {
-                nativeSslClientCert(handle,
-                                    pkey.getEncoded(),
-                                    table.CertificateChain(hostAndPort));
-            }
-        } else if (table.IsDenied(hostAndPort)) {
-            // previously denied
-            nativeSslClientCert(handle, 0, null);
-        } else {
-            // previously ignored or new
-            mCallbackProxy.onReceivedClientCertRequest(
-                    new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort);
-        }
-    }
-
-    /**
-     * Called by JNI when the native HTTP stack needs to download a file.
-     *
-     * We delegate the request to CallbackProxy, which owns the current app's
-     * DownloadListener.
-     */
-    private void downloadStart(String url, String userAgent,
-            String contentDisposition, String mimeType, String referer, long contentLength) {
-        // This will only work if the url ends with the filename
-        if (mimeType.isEmpty()) {
-            try {
-                String extension = url.substring(url.lastIndexOf('.') + 1);
-                mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension);
-                // MimeUtils might return null, not sure if downloadmanager is happy with that
-                if (mimeType == null)
-                    mimeType = "";
-            } catch(IndexOutOfBoundsException exception) {
-                // mimeType string end with a '.', not much to do
-            }
-        }
-        mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
-                mimeType, url, contentDisposition);
-
-        if (CertTool.getCertType(mimeType) != null) {
-            mKeyStoreHandler = new KeyStoreHandler(mimeType);
-        } else {
-            mCallbackProxy.onDownloadStart(url, userAgent,
-                contentDisposition, mimeType, referer, contentLength);
-        }
-    }
-
-    /**
-     * Called by JNI for Chrome HTTP stack when the Java side needs to access the data.
-     */
-    private void didReceiveData(byte data[], int size) {
-        if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
-    }
-
-    private void didFinishLoading() {
-      if (mKeyStoreHandler != null) {
-          mKeyStoreHandler.installCert(mContext);
-          mKeyStoreHandler = null;
-      }
-    }
-
-    /**
-     * Called by JNI when we recieve a certificate for the page's main resource.
-     * Used by the Chromium HTTP stack only.
-     */
-    private void setCertificate(byte cert_der[]) {
-        try {
-            CertificateFactory cf = CertificateFactory.getInstance("X.509");
-            X509Certificate cert = (X509Certificate) cf.generateCertificate(
-                    new ByteArrayInputStream(cert_der));
-            mCallbackProxy.onReceivedCertificate(new SslCertificate(cert));
-        } catch (Exception e) {
-            // Can't get the certificate, not much to do.
-            Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
-            return;
-        }
-    }
-
-    /**
-     * Called by JNI when processing the X-Auto-Login header.
-     */
-    private void autoLogin(String realm, String account, String args) {
-        mCallbackProxy.onReceivedLoginRequest(realm, account, args);
-    }
-
-    //==========================================================================
-    // native functions
-    //==========================================================================
-
-    /**
-     * Create a new native frame for a given WebView
-     * @param w     A WebView that the frame draws into.
-     * @param am    AssetManager to use to get assets.
-     * @param list  The native side will add and remove items from this list as
-     *              the native list changes.
-     */
-    private native void nativeCreateFrame(WebViewCore w, AssetManager am,
-            WebBackForwardList list);
-
-    /**
-     * Destroy the native frame.
-     */
-    public native void nativeDestroyFrame();
-
-    private native void nativeCallPolicyFunction(int policyFunction,
-            int decision);
-
-    /**
-     * Reload the current main frame.
-     */
-    public native void reload(boolean allowStale);
-
-    /**
-     * Go back or forward the number of steps given.
-     * @param steps A negative or positive number indicating the direction
-     *              and number of steps to move.
-     */
-    private native void nativeGoBackOrForward(int steps);
-
-    /**
-     * stringByEvaluatingJavaScriptFromString will execute the
-     * JS passed in in the context of this browser frame.
-     * @param script A javascript string to execute
-     * 
-     * @return string result of execution or null
-     */
-    public native String stringByEvaluatingJavaScriptFromString(String script);
-
-    /**
-     * Add a javascript interface to the main frame.
-     */
-    private native void nativeAddJavascriptInterface(int nativeFramePointer,
-            Object obj, String interfaceName, boolean requireAnnotation);
-
-    public native void clearCache();
-
-    /**
-     * Returns false if the url is bad.
-     */
-    private native void nativeLoadUrl(String url, Map<String, String> headers);
-
-    private native void nativePostUrl(String url, byte[] postData);
-
-    private native void nativeLoadData(String baseUrl, String data,
-            String mimeType, String encoding, String historyUrl);
-
-    /**
-     * Stop loading the current page.
-     */
-    public void stopLoading() {
-        if (mIsMainFrame) {
-            resetLoadingStates();
-        }
-        nativeStopLoading();
-    }
-
-    private native void nativeStopLoading();
-
-    /**
-     * Return true if the document has images.
-     */
-    public native boolean documentHasImages();
-
-    /**
-     * @return TRUE if there is a password field in the current frame
-     */
-    private native boolean hasPasswordField();
-
-    /**
-     * Get username and password in the current frame. If found, String[0] is
-     * username and String[1] is password. Otherwise return NULL.
-     * @return String[]
-     */
-    private native String[] getUsernamePassword();
-
-    /**
-     * Set username and password to the proper fields in the current frame
-     * @param username
-     * @param password
-     */
-    private native void setUsernamePassword(String username, String password);
-
-    private native String nativeSaveWebArchive(String basename, boolean autoname);
-
-    private native void nativeOrientationChanged(int orientation);
-
-    private native void nativeAuthenticationProceed(int handle, String username, String password);
-    private native void nativeAuthenticationCancel(int handle);
-
-    private native void nativeSslCertErrorProceed(int handle);
-    private native void nativeSslCertErrorCancel(int handle, int certError);
-
-    native void nativeSslClientCert(int handle,
-                                    long ctx,
-                                    byte[][] asn1DerEncodedCertificateChain);
-
-    native void nativeSslClientCert(int handle,
-                                    byte[] pkey,
-                                    byte[][] asn1DerEncodedCertificateChain);
-
-    /**
-     * Returns true when the contents of the frame is an RTL or vertical-rl
-     * page. This is used for determining whether a frame should be initially
-     * scrolled right-most as opposed to left-most.
-     * @return true when the frame should be initially scrolled right-most
-     * based on the text direction and writing mode.
-     */
-    /* package */ boolean getShouldStartScrolledRight() {
-        return nativeGetShouldStartScrolledRight(mNativeFrame);
-    }
-
-    private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame);
-}
diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java
deleted file mode 100644
index 334526b..0000000
--- a/core/java/android/webkit/ByteArrayBuilder.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-/** Utility class optimized for accumulating bytes, and then spitting
-    them back out.  It does not optimize for returning the result in a
-    single array, though this is supported in the API. It is fastest
-    if the retrieval can be done via iterating through chunks.
-*/
-class ByteArrayBuilder {
-
-    private static final int DEFAULT_CAPACITY = 8192;
-
-    // Global pool of chunks to be used by other ByteArrayBuilders.
-    private static final LinkedList<SoftReference<Chunk>> sPool =
-            new LinkedList<SoftReference<Chunk>>();
-    // Reference queue for processing gc'd entries.
-    private static final ReferenceQueue<Chunk> sQueue =
-            new ReferenceQueue<Chunk>();
-
-    private LinkedList<Chunk> mChunks;
-
-    public ByteArrayBuilder() {
-        mChunks = new LinkedList<Chunk>();
-    }
-
-    public synchronized void append(byte[] array, int offset, int length) {
-        while (length > 0) {
-            Chunk c = null;
-            if (mChunks.isEmpty()) {
-                c = obtainChunk(length);
-                mChunks.addLast(c);
-            } else {
-                c = mChunks.getLast();
-                if (c.mLength == c.mArray.length) {
-                    c = obtainChunk(length);
-                    mChunks.addLast(c);
-                }
-            }
-            int amount = Math.min(length, c.mArray.length - c.mLength);
-            System.arraycopy(array, offset, c.mArray, c.mLength, amount);
-            c.mLength += amount;
-            length -= amount;
-            offset += amount;
-        }
-    }
-
-    /**
-     * The fastest way to retrieve the data is to iterate through the
-     * chunks.  This returns the first chunk.  Note: this pulls the
-     * chunk out of the queue.  The caller must call Chunk.release() to
-     * dispose of it.
-     */
-    public synchronized Chunk getFirstChunk() {
-        if (mChunks.isEmpty()) return null;
-        return mChunks.removeFirst();
-    }
-
-    public synchronized boolean isEmpty() {
-        return mChunks.isEmpty();
-    }
-
-    public synchronized int getByteSize() {
-        int total = 0;
-        ListIterator<Chunk> it = mChunks.listIterator(0);
-        while (it.hasNext()) {
-            Chunk c = it.next();
-            total += c.mLength;
-        }
-        return total;
-    }
-
-    public synchronized void clear() {
-        Chunk c = getFirstChunk();
-        while (c != null) {
-            c.release();
-            c = getFirstChunk();
-        }
-    }
-
-    // Must be called with lock held on sPool.
-    private void processPoolLocked() {
-        while (true) {
-            SoftReference<Chunk> entry = (SoftReference<Chunk>) sQueue.poll();
-            if (entry == null) {
-                break;
-            }
-            sPool.remove(entry);
-        }
-    }
-
-    private Chunk obtainChunk(int length) {
-        // Correct a small length.
-        if (length < DEFAULT_CAPACITY) {
-            length = DEFAULT_CAPACITY;
-        }
-        synchronized (sPool) {
-            // Process any queued references and remove them from the pool.
-            processPoolLocked();
-            if (!sPool.isEmpty()) {
-                Chunk c = sPool.removeFirst().get();
-                // The first item may have been queued after processPoolLocked
-                // so check for null.
-                if (c != null) {
-                    return c;
-                }
-            }
-            return new Chunk(length);
-        }
-    }
-
-    public static class Chunk {
-        public byte[]  mArray;
-        public int     mLength;
-
-        public Chunk(int length) {
-            mArray = new byte[length];
-            mLength = 0;
-        }
-
-        /**
-         * Release the chunk and make it available for reuse.
-         */
-        public void release() {
-            mLength = 0;
-            synchronized (sPool) {
-                // Add the chunk back to the pool as a SoftReference so it can
-                // be gc'd if needed.
-                sPool.offer(new SoftReference<Chunk>(this, sQueue));
-                sPool.notifyAll();
-            }
-        }
-
-    }
-}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
deleted file mode 100644
index bbd3f2b..0000000
--- a/core/java/android/webkit/CacheManager.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.net.http.Headers;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Map;
-
-
-/**
- * Manages the HTTP cache used by an application's {@link WebView} instances.
- * @deprecated Access to the HTTP cache will be removed in a future release.
- * @hide Since {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
- */
-// The class CacheManager provides the persistent cache of content that is
-// received over the network. The component handles parsing of HTTP headers and
-// utilizes the relevant cache headers to determine if the content should be
-// stored and if so, how long it is valid for. Network requests are provided to
-// this component and if they can not be resolved by the cache, the HTTP headers
-// are attached, as appropriate, to the request for revalidation of content. The
-// class also manages the cache size.
-//
-// CacheManager may only be used if your activity contains a WebView.
-@Deprecated
-public final class CacheManager {
-    /**
-     * Represents a resource stored in the HTTP cache. Instances of this class
-     * can be obtained by calling
-     * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}.
-     *
-     * @deprecated Access to the HTTP cache will be removed in a future release.
-     */
-    @Deprecated
-    public static class CacheResult {
-        // these fields are saved to the database
-        int httpStatusCode;
-        long contentLength;
-        long expires;
-        String expiresString;
-        String localPath;
-        String lastModified;
-        String etag;
-        String mimeType;
-        String location;
-        String encoding;
-        String contentdisposition;
-        String crossDomain;
-
-        // these fields are NOT saved to the database
-        InputStream inStream;
-        OutputStream outStream;
-        File outFile;
-
-        /**
-         * Gets the status code of this cache entry.
-         *
-         * @return the status code of this cache entry
-         */
-        public int getHttpStatusCode() {
-            return httpStatusCode;
-        }
-
-        /**
-         * Gets the content length of this cache entry.
-         *
-         * @return the content length of this cache entry
-         */
-        public long getContentLength() {
-            return contentLength;
-        }
-
-        /**
-         * Gets the path of the file used to store the content of this cache
-         * entry, relative to the base directory of the cache. See
-         * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}.
-         *
-         * @return the path of the file used to store this cache entry
-         */
-        public String getLocalPath() {
-            return localPath;
-        }
-
-        /**
-         * Gets the expiry date of this cache entry, expressed in milliseconds
-         * since midnight, January 1, 1970 UTC.
-         *
-         * @return the expiry date of this cache entry
-         */
-        public long getExpires() {
-            return expires;
-        }
-
-        /**
-         * Gets the expiry date of this cache entry, expressed as a string.
-         *
-         * @return the expiry date of this cache entry
-         *
-         */
-        public String getExpiresString() {
-            return expiresString;
-        }
-
-        /**
-         * Gets the date at which this cache entry was last modified, expressed
-         * as a string.
-         *
-         * @return the date at which this cache entry was last modified
-         */
-        public String getLastModified() {
-            return lastModified;
-        }
-
-        /**
-         * Gets the entity tag of this cache entry.
-         *
-         * @return the entity tag of this cache entry
-         */
-        public String getETag() {
-            return etag;
-        }
-
-        /**
-         * Gets the MIME type of this cache entry.
-         *
-         * @return the MIME type of this cache entry
-         */
-        public String getMimeType() {
-            return mimeType;
-        }
-
-        /**
-         * Gets the value of the HTTP 'Location' header with which this cache
-         * entry was received.
-         *
-         * @return the HTTP 'Location' header for this cache entry
-         */
-        public String getLocation() {
-            return location;
-        }
-
-        /**
-         * Gets the encoding of this cache entry.
-         *
-         * @return the encoding of this cache entry
-         */
-        public String getEncoding() {
-            return encoding;
-        }
-
-        /**
-         * Gets the value of the HTTP 'Content-Disposition' header with which
-         * this cache entry was received.
-         *
-         * @return the HTTP 'Content-Disposition' header for this cache entry
-         *
-         */
-        public String getContentDisposition() {
-            return contentdisposition;
-        }
-
-        /**
-         * Gets the input stream to the content of this cache entry, to allow
-         * content to be read. See
-         * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}.
-         *
-         * @return an input stream to the content of this cache entry
-         */
-        public InputStream getInputStream() {
-            return inStream;
-        }
-
-        /**
-         * Gets an output stream to the content of this cache entry, to allow
-         * content to be written. See
-         * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}.
-         *
-         * @return an output stream to the content of this cache entry
-         */
-        // Note that this is always null for objects returned by getCacheFile()!
-        public OutputStream getOutputStream() {
-            return outStream;
-        }
-
-
-        /**
-         * Sets an input stream to the content of this cache entry.
-         *
-         * @param stream an input stream to the content of this cache entry
-         */
-        public void setInputStream(InputStream stream) {
-            this.inStream = stream;
-        }
-
-        /**
-         * Sets the encoding of this cache entry.
-         *
-         * @param encoding the encoding of this cache entry
-         */
-        public void setEncoding(String encoding) {
-            this.encoding = encoding;
-        }
-
-        /**
-         * @hide
-         */
-        public void setContentLength(long contentLength) {
-            this.contentLength = contentLength;
-        }
-    }
-
-    /**
-     * Gets the base directory in which the files used to store the contents of
-     * cache entries are placed. See
-     * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
-     *
-     * @return the base directory of the cache
-     * @deprecated This method no longer has any effect and always returns null.
-     */
-    @Deprecated
-    public static File getCacheFileBaseDir() {
-        return null;
-    }
-
-    /**
-     * Gets whether the HTTP cache is disabled.
-     *
-     * @return true if the HTTP cache is disabled
-     * @deprecated This method no longer has any effect and always returns false.
-     */
-    @Deprecated
-    public static boolean cacheDisabled() {
-        return false;
-    }
-
-    /**
-     * Starts a cache transaction. Returns true if this is the only running
-     * transaction. Otherwise, this transaction is nested inside currently
-     * running transactions and false is returned.
-     *
-     * @return true if this is the only running transaction
-     * @deprecated This method no longer has any effect and always returns false.
-     */
-    @Deprecated
-    public static boolean startCacheTransaction() {
-        return false;
-    }
-
-    /**
-     * Ends the innermost cache transaction and returns whether this was the
-     * only running transaction.
-     *
-     * @return true if this was the only running transaction
-     * @deprecated This method no longer has any effect and always returns false.
-     */
-    @Deprecated
-    public static boolean endCacheTransaction() {
-        return false;
-    }
-
-    /**
-     * Gets the cache entry for the specified URL, or null if none is found.
-     * If a non-null value is provided for the HTTP headers map, and the cache
-     * entry needs validation, appropriate headers will be added to the map.
-     * The input stream of the CacheEntry object should be closed by the caller
-     * when access to the underlying file is no longer required.
-     *
-     * @param url the URL for which a cache entry is requested
-     * @param headers a map from HTTP header name to value, to be populated
-     *                for the returned cache entry
-     * @return the cache entry for the specified URL
-     * @deprecated This method no longer has any effect and always returns null.
-     */
-    @Deprecated
-    public static CacheResult getCacheFile(String url,
-            Map<String, String> headers) {
-        return null;
-    }
-
-    /**
-     * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes
-     * the cache entry's output stream.
-     *
-     * @param url the URL for which the cache entry should be added
-     * @param cacheResult the cache entry to add
-     * @deprecated Access to the HTTP cache will be removed in a future release.
-     */
-    @Deprecated
-    public static void saveCacheFile(String url, CacheResult cacheResult) {
-        saveCacheFile(url, 0, cacheResult);
-    }
-
-    static void saveCacheFile(String url, long postIdentifier,
-            CacheResult cacheRet) {
-        try {
-            cacheRet.outStream.close();
-        } catch (IOException e) {
-            return;
-        }
-
-        // This method is exposed in the public API but the API provides no
-        // way to obtain a new CacheResult object with a non-null output
-        // stream ...
-        // - CacheResult objects returned by getCacheFile() have a null
-        //   output stream.
-        // - new CacheResult objects have a null output stream and no
-        //   setter is provided.
-        // Since this method throws a null pointer exception in this case,
-        // it is effectively useless from the point of view of the public
-        // API.
-        //
-        // With the Chromium HTTP stack we continue to throw the same
-        // exception for 'backwards compatibility' with the Android HTTP
-        // stack.
-        //
-        // This method is not used from within this package, and for public API
-        // use, we should already have thrown an exception above.
-        assert false;
-    }
-}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
deleted file mode 100644
index 7707392..0000000
--- a/core/java/android/webkit/CallbackProxy.java
+++ /dev/null
@@ -1,1452 +0,0 @@
-/*
- * Copyright (C) 2007 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.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Browser;
-import android.util.Log;
-import android.view.KeyEvent;
-import com.android.internal.R;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This class is a proxy class for handling WebCore -> UI thread messaging. All
- * the callback functions are called from the WebCore thread and messages are
- * posted to the UI thread for the actual client callback.
- */
-/*
- * This class is created in the UI thread so its handler and any private classes
- * that extend Handler will operate in the UI thread.
- */
-class CallbackProxy extends Handler {
-    // Logging tag
-    static final String LOGTAG = "WebViewCallback";
-    // Enables API callback tracing
-    private static final boolean TRACE = DebugFlags.TRACE_CALLBACK;
-    // Instance of WebViewClient that is the client callback.
-    private volatile WebViewClient mWebViewClient;
-    // Instance of WebChromeClient for handling all chrome functions.
-    private volatile WebChromeClient mWebChromeClient;
-    // 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.
-    private boolean mProgressUpdatePending;
-    // Keep track of the last progress amount.
-    // Start with 100 to indicate it is not in load for the empty page.
-    private volatile int mLatestProgress = 100;
-    // Back/Forward list
-    private final WebBackForwardListClassic mBackForwardList;
-    // Back/Forward list client
-    private volatile WebBackForwardListClient mWebBackForwardListClient;
-    // Used to call startActivity during url override.
-    private final Context mContext;
-    // block messages flag for destroy
-    private boolean mBlockMessages;
-
-    // Message IDs
-    private static final int PAGE_STARTED                         = 100;
-    private static final int RECEIVED_ICON                        = 101;
-    private static final int RECEIVED_TITLE                       = 102;
-    private static final int OVERRIDE_URL                         = 103;
-    private static final int AUTH_REQUEST                         = 104;
-    private static final int SSL_ERROR                            = 105;
-    private static final int PROGRESS                             = 106;
-    private static final int UPDATE_VISITED                       = 107;
-    private static final int LOAD_RESOURCE                        = 108;
-    private static final int CREATE_WINDOW                        = 109;
-    private static final int CLOSE_WINDOW                         = 110;
-    private static final int SAVE_PASSWORD                        = 111;
-    private static final int JS_DIALOG                            = 112;
-    private static final int ASYNC_KEYEVENTS                      = 116;
-    private static final int DOWNLOAD_FILE                        = 118;
-    private static final int REPORT_ERROR                         = 119;
-    private static final int RESEND_POST_DATA                     = 120;
-    private static final int PAGE_FINISHED                        = 121;
-    private static final int REQUEST_FOCUS                        = 122;
-    private static final int SCALE_CHANGED                        = 123;
-    private static final int RECEIVED_CERTIFICATE                 = 124;
-    private static final int SWITCH_OUT_HISTORY                   = 125;
-    private static final int EXCEEDED_DATABASE_QUOTA              = 126;
-    private static final int REACHED_APPCACHE_MAXSIZE             = 127;
-    private static final int JS_TIMEOUT                           = 128;
-    private static final int ADD_MESSAGE_TO_CONSOLE               = 129;
-    private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT  = 130;
-    private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT  = 131;
-    private static final int RECEIVED_TOUCH_ICON_URL              = 132;
-    private static final int GET_VISITED_HISTORY                  = 133;
-    private static final int OPEN_FILE_CHOOSER                    = 134;
-    private static final int ADD_HISTORY_ITEM                     = 135;
-    private static final int HISTORY_INDEX_CHANGED                = 136;
-    private static final int AUTH_CREDENTIALS                     = 137;
-    private static final int AUTO_LOGIN                           = 140;
-    private static final int CLIENT_CERT_REQUEST                  = 141;
-    private static final int PROCEEDED_AFTER_SSL_ERROR            = 144;
-
-    // Message triggered by the client to resume execution
-    private static final int NOTIFY                               = 200;
-
-    // Result transportation object for returning results across thread
-    // boundaries.
-    private static class ResultTransport<E> {
-        // Private result object
-        private E mResult;
-
-        public ResultTransport(E defaultResult) {
-            mResult = defaultResult;
-        }
-
-        public synchronized void setResult(E result) {
-            mResult = result;
-        }
-
-        public synchronized E getResult() {
-            return mResult;
-        }
-    }
-
-    private class JsResultReceiver implements JsResult.ResultReceiver {
-        // This prevents a user from interacting with the result before WebCore is
-        // ready to handle it.
-        private boolean mReady;
-        // Tells us if the user tried to confirm or cancel the result before WebCore
-        // is ready.
-        private boolean mTriedToNotifyBeforeReady;
-
-        public JsPromptResult mJsResult = new JsPromptResult(this);
-
-        final void setReady() {
-            mReady = true;
-            if (mTriedToNotifyBeforeReady) {
-                notifyCallbackProxy();
-            }
-        }
-
-        /* Wake up the WebCore thread. */
-        @Override
-        public void onJsResultComplete(JsResult result) {
-            if (mReady) {
-                notifyCallbackProxy();
-            } else {
-                mTriedToNotifyBeforeReady = true;
-            }
-        }
-
-        private void notifyCallbackProxy() {
-            synchronized (CallbackProxy.this) {
-                CallbackProxy.this.notify();
-            }
-        }
-}
-
-    /**
-     * Construct a new CallbackProxy.
-     */
-    public CallbackProxy(Context context, WebViewClassic w) {
-        // Used to start a default activity.
-        mContext = context;
-        mWebView = w;
-        mBackForwardList = new WebBackForwardListClassic(this);
-    }
-
-    protected synchronized void blockMessages() {
-        mBlockMessages = true;
-    }
-
-    protected synchronized boolean messagesBlocked() {
-        return mBlockMessages;
-    }
-
-    protected void shutdown() {
-        removeCallbacksAndMessages(null);
-        setWebViewClient(null);
-        setWebChromeClient(null);
-    }
-
-    /**
-     * Set the WebViewClient.
-     * @param client An implementation of WebViewClient.
-     */
-    public void setWebViewClient(WebViewClient client) {
-        mWebViewClient = client;
-    }
-
-    /**
-     * Get the WebViewClient.
-     * @return the current WebViewClient instance.
-     */
-    public WebViewClient getWebViewClient() {
-       return mWebViewClient;
-    }
-
-    /**
-     * Set the WebChromeClient.
-     * @param client An implementation of WebChromeClient.
-     */
-    public void setWebChromeClient(WebChromeClient client) {
-        mWebChromeClient = client;
-    }
-
-    /**
-     * Get the WebChromeClient.
-     * @return the current WebChromeClient instance.
-     */
-    public WebChromeClient getWebChromeClient() {
-       return mWebChromeClient;
-    }
-
-    /**
-     * Set the client DownloadListener.
-     * @param client An implementation of DownloadListener.
-     */
-    public void setDownloadListener(DownloadListener client) {
-        mDownloadListener = client;
-    }
-
-    /**
-     * Get the Back/Forward list to return to the user or to update the cached
-     * history list.
-     */
-    public WebBackForwardListClassic getBackForwardList() {
-        return mBackForwardList;
-    }
-
-    void setWebBackForwardListClient(WebBackForwardListClient client) {
-        mWebBackForwardListClient = client;
-    }
-
-    WebBackForwardListClient getWebBackForwardListClient() {
-        return mWebBackForwardListClient;
-    }
-
-    /**
-     * Called by the UI side.  Calling overrideUrlLoading from the WebCore
-     * side will post a message to call this method.
-     */
-    public boolean uiOverrideUrlLoading(String overrideUrl) {
-        if (overrideUrl == null || overrideUrl.length() == 0) {
-            return false;
-        }
-        boolean override = false;
-        if (mWebViewClient != null) {
-            if (TRACE) Log.d(LOGTAG, "shouldOverrideUrlLoading=" + overrideUrl);
-            override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
-                    overrideUrl);
-        } else {
-            Intent intent = new Intent(Intent.ACTION_VIEW,
-                    Uri.parse(overrideUrl));
-            intent.addCategory(Intent.CATEGORY_BROWSABLE);
-            // If another application is running a WebView and launches the
-            // Browser through this Intent, we want to reuse the same window if
-            // possible.
-            intent.putExtra(Browser.EXTRA_APPLICATION_ID,
-                    mContext.getPackageName());
-            try {
-                mContext.startActivity(intent);
-                override = true;
-            } catch (ActivityNotFoundException ex) {
-                // If no application can handle the URL, assume that the
-                // browser can handle it.
-            }
-        }
-        return override;
-    }
-
-    /**
-     * Called by UI side.
-     */
-    public boolean uiOverrideKeyEvent(KeyEvent event) {
-        if (mWebViewClient != null) {
-            return mWebViewClient.shouldOverrideKeyEvent(mWebView.getWebView(), event);
-        }
-        return false;
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        // We don't have to do synchronization because this function operates
-        // in the UI thread. The WebViewClient and WebChromeClient functions
-        // that check for a non-null callback are ok because java ensures atomic
-        // 32-bit reads and writes.
-        if (messagesBlocked()) {
-            synchronized (this) {
-                notify();
-            }
-            return;
-        }
-        switch (msg.what) {
-            case PAGE_STARTED:
-                String startedUrl = msg.getData().getString("url");
-                mWebView.onPageStarted(startedUrl);
-                if (mWebViewClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onPageStarted=" + startedUrl);
-                    mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
-                            (Bitmap) msg.obj);
-                }
-                break;
-
-            case PAGE_FINISHED:
-                String finishedUrl = (String) msg.obj;
-                mWebView.onPageFinished(finishedUrl);
-                if (mWebViewClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onPageFinished=" + finishedUrl);
-                    mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
-                }
-                break;
-
-            case RECEIVED_ICON:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onReceivedIcon");
-                    mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
-                }
-                break;
-
-            case RECEIVED_TOUCH_ICON_URL:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onReceivedTouchIconUrl");
-                    mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
-                            (String) msg.obj, msg.arg1 == 1);
-                }
-                break;
-
-            case RECEIVED_TITLE:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onReceivedTitle");
-                    mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
-                            (String) msg.obj);
-                }
-                break;
-
-            case REPORT_ERROR:
-                if (mWebViewClient != null) {
-                    int reasonCode = msg.arg1;
-                    final String description  = msg.getData().getString("description");
-                    final String failUrl  = msg.getData().getString("failingUrl");
-                    if (TRACE) Log.d(LOGTAG, "onReceivedError=" + failUrl);
-                    mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
-                            description, failUrl);
-                }
-                break;
-
-            case RESEND_POST_DATA:
-                Message resend =
-                        (Message) msg.getData().getParcelable("resend");
-                Message dontResend =
-                        (Message) msg.getData().getParcelable("dontResend");
-                if (mWebViewClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onFormResubmission");
-                    mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
-                            resend);
-                } else {
-                    dontResend.sendToTarget();
-                }
-                break;
-
-            case OVERRIDE_URL:
-                String overrideUrl = msg.getData().getString("url");
-                boolean override = uiOverrideUrlLoading(overrideUrl);
-                ResultTransport<Boolean> result =
-                        (ResultTransport<Boolean>) msg.obj;
-                synchronized (this) {
-                    result.setResult(override);
-                    notify();
-                }
-                break;
-
-            case AUTH_REQUEST:
-                if (mWebViewClient != null) {
-                    HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
-                    String host = msg.getData().getString("host");
-                    String realm = msg.getData().getString("realm");
-                    if (TRACE) Log.d(LOGTAG, "onReceivedHttpAuthRequest");
-                    mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
-                            host, realm);
-                }
-                break;
-
-            case SSL_ERROR:
-                if (mWebViewClient != null) {
-                    HashMap<String, Object> map =
-                        (HashMap<String, Object>) msg.obj;
-                    if (TRACE) Log.d(LOGTAG, "onReceivedSslError");
-                    mWebViewClient.onReceivedSslError(mWebView.getWebView(),
-                            (SslErrorHandler) map.get("handler"),
-                            (SslError) map.get("error"));
-                }
-                break;
-
-            case PROCEEDED_AFTER_SSL_ERROR:
-                if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
-                    if (TRACE) Log.d(LOGTAG, "onProceededAfterSslError");
-                    ((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError(
-                            mWebView.getWebView(),
-                            (SslError) msg.obj);
-                }
-                break;
-
-            case CLIENT_CERT_REQUEST:
-                if (mWebViewClient != null  && mWebViewClient instanceof WebViewClientClassicExt) {
-                    if (TRACE) Log.d(LOGTAG, "onReceivedClientCertRequest");
-                    HashMap<String, Object> map = (HashMap<String, Object>) msg.obj;
-                    ((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest(
-                            mWebView.getWebView(),
-                            (ClientCertRequestHandler) map.get("handler"),
-                            (String) map.get("host_and_port"));
-                }
-                break;
-
-            case PROGRESS:
-                // Synchronize to ensure mLatestProgress is not modified after
-                // setProgress is called and before mProgressUpdatePending is
-                // changed.
-                synchronized (this) {
-                    if (mWebChromeClient != null) {
-                        if (TRACE) Log.d(LOGTAG, "onProgressChanged=" + mLatestProgress);
-                        mWebChromeClient.onProgressChanged(mWebView.getWebView(),
-                                mLatestProgress);
-                    }
-                    mProgressUpdatePending = false;
-                }
-                break;
-
-            case UPDATE_VISITED:
-                if (mWebViewClient != null) {
-                    String url = (String) msg.obj;
-                    if (TRACE) Log.d(LOGTAG, "doUpdateVisitedHistory=" + url);
-                    mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
-                            url, msg.arg1 != 0);
-                }
-                break;
-
-            case LOAD_RESOURCE:
-                if (mWebViewClient != null) {
-                    String url = (String) msg.obj;
-                    if (TRACE) Log.d(LOGTAG, "onLoadResource=" + url);
-                    mWebViewClient.onLoadResource(mWebView.getWebView(), url);
-                }
-                break;
-
-            case DOWNLOAD_FILE:
-                if (mDownloadListener != null) {
-                    String url = msg.getData().getString("url");
-                    String userAgent = msg.getData().getString("userAgent");
-                    String contentDisposition =
-                        msg.getData().getString("contentDisposition");
-                    String mimetype = msg.getData().getString("mimetype");
-                    String referer = msg.getData().getString("referer");
-                    Long contentLength = msg.getData().getLong("contentLength");
-
-                    if (TRACE) Log.d(LOGTAG, "onDownloadStart");
-                    if (mDownloadListener instanceof BrowserDownloadListener) {
-                        ((BrowserDownloadListener) mDownloadListener).onDownloadStart(url,
-                             userAgent, contentDisposition, mimetype, referer, contentLength);
-                    } else {
-                        mDownloadListener.onDownloadStart(url, userAgent,
-                             contentDisposition, mimetype, contentLength);
-                    }
-                }
-                break;
-
-            case CREATE_WINDOW:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onCreateWindow");
-                    if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
-                                msg.arg1 == 1, msg.arg2 == 1,
-                                (Message) msg.obj)) {
-                        synchronized (this) {
-                            notify();
-                        }
-                    }
-                    mWebView.dismissZoomControl();
-                }
-                break;
-
-            case REQUEST_FOCUS:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onRequestFocus");
-                    mWebChromeClient.onRequestFocus(mWebView.getWebView());
-                }
-                break;
-
-            case CLOSE_WINDOW:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onCloseWindow");
-                    mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
-                }
-                break;
-
-            case SAVE_PASSWORD:
-                Bundle bundle = msg.getData();
-                String schemePlusHost = bundle.getString("host");
-                String username = bundle.getString("username");
-                String password = bundle.getString("password");
-                // If the client returned false it means that the notify message
-                // will not be sent and we should notify WebCore ourselves.
-                if (!mWebView.onSavePassword(schemePlusHost, username, password,
-                            (Message) msg.obj)) {
-                    synchronized (this) {
-                        notify();
-                    }
-                }
-                break;
-
-            case ASYNC_KEYEVENTS:
-                if (mWebViewClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onUnhandledKeyEvent");
-                    mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
-                            (KeyEvent) msg.obj);
-                }
-                break;
-
-            case EXCEEDED_DATABASE_QUOTA:
-                if (mWebChromeClient != null) {
-                    HashMap<String, Object> map =
-                            (HashMap<String, Object>) msg.obj;
-                    String databaseIdentifier =
-                            (String) map.get("databaseIdentifier");
-                    String url = (String) map.get("url");
-                    long quota =
-                            ((Long) map.get("quota")).longValue();
-                    long totalQuota =
-                            ((Long) map.get("totalQuota")).longValue();
-                    long estimatedDatabaseSize =
-                            ((Long) map.get("estimatedDatabaseSize")).longValue();
-                    WebStorage.QuotaUpdater quotaUpdater =
-                        (WebStorage.QuotaUpdater) map.get("quotaUpdater");
-
-                    if (TRACE) Log.d(LOGTAG, "onExceededDatabaseQuota");
-                    mWebChromeClient.onExceededDatabaseQuota(url,
-                            databaseIdentifier, quota, estimatedDatabaseSize,
-                            totalQuota, quotaUpdater);
-                }
-                break;
-
-            case REACHED_APPCACHE_MAXSIZE:
-                if (mWebChromeClient != null) {
-                    HashMap<String, Object> map =
-                            (HashMap<String, Object>) msg.obj;
-                    long requiredStorage =
-                            ((Long) map.get("requiredStorage")).longValue();
-                    long quota =
-                        ((Long) map.get("quota")).longValue();
-                    WebStorage.QuotaUpdater quotaUpdater =
-                        (WebStorage.QuotaUpdater) map.get("quotaUpdater");
-
-                    if (TRACE) Log.d(LOGTAG, "onReachedMaxAppCacheSize");
-                    mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage,
-                            quota, quotaUpdater);
-                }
-                break;
-
-            case GEOLOCATION_PERMISSIONS_SHOW_PROMPT:
-                if (mWebChromeClient != null) {
-                    HashMap<String, Object> map =
-                            (HashMap<String, Object>) msg.obj;
-                    String origin = (String) map.get("origin");
-                    GeolocationPermissions.Callback callback =
-                            (GeolocationPermissions.Callback)
-                            map.get("callback");
-                    if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsShowPrompt");
-                    mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
-                            callback);
-                }
-                break;
-
-            case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsHidePrompt");
-                    mWebChromeClient.onGeolocationPermissionsHidePrompt();
-                }
-                break;
-
-            case JS_DIALOG:
-                if (mWebChromeClient != null) {
-                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
-                    JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg);
-                    if (TRACE) Log.d(LOGTAG, "onJsAlert");
-                    if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) {
-                        helper.showDialog(mContext);
-                    }
-                    receiver.setReady();
-                }
-                break;
-
-            case JS_TIMEOUT:
-                if(mWebChromeClient != null) {
-                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
-                    final JsResult res = receiver.mJsResult;
-                    if (TRACE) Log.d(LOGTAG, "onJsTimeout");
-                    if (mWebChromeClient.onJsTimeout()) {
-                        res.confirm();
-                    } else {
-                        res.cancel();
-                    }
-                    receiver.setReady();
-                }
-                break;
-
-            case RECEIVED_CERTIFICATE:
-                mWebView.setCertificate((SslCertificate) msg.obj);
-                break;
-
-            case NOTIFY:
-                synchronized (this) {
-                    notify();
-                }
-                break;
-
-            case SCALE_CHANGED:
-                if (mWebViewClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onScaleChanged");
-                    mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
-                            .getFloat("old"), msg.getData().getFloat("new"));
-                }
-                break;
-
-            case SWITCH_OUT_HISTORY:
-                mWebView.switchOutDrawHistory();
-                break;
-
-            case ADD_MESSAGE_TO_CONSOLE:
-                if (mWebChromeClient == null) {
-                    break;
-                }
-                String message = msg.getData().getString("message");
-                String sourceID = msg.getData().getString("sourceID");
-                int lineNumber = msg.getData().getInt("lineNumber");
-                int msgLevel = msg.getData().getInt("msgLevel");
-                int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
-                // Sanity bounds check as we'll index an array with msgLevel
-                if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
-                    msgLevel = 0;
-                }
-
-                ConsoleMessage.MessageLevel messageLevel =
-                        ConsoleMessage.MessageLevel.values()[msgLevel];
-
-                if (TRACE) Log.d(LOGTAG, "onConsoleMessage");
-                if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
-                        lineNumber, messageLevel))) {
-                    // If false was returned the user did not provide their own console function so
-                    //  we should output some default messages to the system log.
-                    String logTag = "Web Console";
-                    String logMessage = message + " at " + sourceID + ":" + lineNumber;
-
-                    switch (messageLevel) {
-                        case TIP:
-                            Log.v(logTag, logMessage);
-                            break;
-                        case LOG:
-                            Log.i(logTag, logMessage);
-                            break;
-                        case WARNING:
-                            Log.w(logTag, logMessage);
-                            break;
-                        case ERROR:
-                            Log.e(logTag, logMessage);
-                            break;
-                        case DEBUG:
-                            Log.d(logTag, logMessage);
-                            break;
-                    }
-                }
-
-                break;
-
-            case GET_VISITED_HISTORY:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "getVisitedHistory");
-                    mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
-                }
-                break;
-
-            case OPEN_FILE_CHOOSER:
-                if (mWebChromeClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "openFileChooser");
-                    UploadFileMessageData data = (UploadFileMessageData)msg.obj;
-                    mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(),
-                            data.getCapture());
-                }
-                break;
-
-            case ADD_HISTORY_ITEM:
-                if (mWebBackForwardListClient != null) {
-                    if (TRACE) Log.d(LOGTAG, "onNewHistoryItem");
-                    mWebBackForwardListClient.onNewHistoryItem(
-                            (WebHistoryItem) msg.obj);
-                }
-                break;
-
-            case HISTORY_INDEX_CHANGED:
-                if (mWebBackForwardListClient != null) {
-                    mWebBackForwardListClient.onIndexChanged(
-                            (WebHistoryItem) msg.obj, msg.arg1);
-                }
-                break;
-            case AUTH_CREDENTIALS: {
-                String host = msg.getData().getString("host");
-                String realm = msg.getData().getString("realm");
-                username = msg.getData().getString("username");
-                password = msg.getData().getString("password");
-                mWebView.setHttpAuthUsernamePassword(
-                        host, realm, username, password);
-                break;
-            }
-            case AUTO_LOGIN: {
-                if (mWebViewClient != null) {
-                    String realm = msg.getData().getString("realm");
-                    String account = msg.getData().getString("account");
-                    String args = msg.getData().getString("args");
-                    if (TRACE) Log.d(LOGTAG, "onReceivedLoginRequest");
-                    mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
-                            account, args);
-                }
-                break;
-            }
-        }
-    }
-
-    /**
-     * Return the latest progress.
-     */
-    public int getProgress() {
-        return mLatestProgress;
-    }
-
-    /**
-     * Called by WebCore side to switch out of history Picture drawing mode
-     */
-    void switchOutDrawHistory() {
-        sendMessage(obtainMessage(SWITCH_OUT_HISTORY));
-    }
-
-    //--------------------------------------------------------------------------
-    // WebViewClient functions.
-    // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so
-    // it is not necessary to include it here.
-    //--------------------------------------------------------------------------
-
-    // Performance probe
-    private static final boolean PERF_PROBE = false;
-    private long mWebCoreThreadTime;
-    private long mWebCoreIdleTime;
-
-    /*
-     * If PERF_PROBE is true, this block needs to be added to MessageQueue.java.
-     * startWait() and finishWait() should be called before and after wait().
-
-    private WaitCallback mWaitCallback = null;
-    public static interface WaitCallback {
-        void startWait();
-        void finishWait();
-    }
-    public final void setWaitCallback(WaitCallback callback) {
-        mWaitCallback = callback;
-    }
-    */
-
-    // un-comment this block if PERF_PROBE is true
-    /*
-    private IdleCallback mIdleCallback = new IdleCallback();
-
-    private final class IdleCallback implements MessageQueue.WaitCallback {
-        private long mStartTime = 0;
-
-        public void finishWait() {
-            mWebCoreIdleTime += SystemClock.uptimeMillis() - mStartTime;
-        }
-
-        public void startWait() {
-            mStartTime = SystemClock.uptimeMillis();
-        }
-    }
-    */
-
-    public void onPageStarted(String url, Bitmap favicon) {
-        // We need to send the message even if no WebViewClient is set, because we need to call
-        // WebView.onPageStarted().
-
-        // Performance probe
-        if (PERF_PROBE) {
-            mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
-            mWebCoreIdleTime = 0;
-            // un-comment this if PERF_PROBE is true
-//            Looper.myQueue().setWaitCallback(mIdleCallback);
-        }
-        Message msg = obtainMessage(PAGE_STARTED);
-        msg.obj = favicon;
-        msg.getData().putString("url", url);
-        sendMessage(msg);
-    }
-
-    public void onPageFinished(String url) {
-        // Performance probe
-        if (PERF_PROBE) {
-            // un-comment this if PERF_PROBE is true
-//            Looper.myQueue().setWaitCallback(null);
-            Log.d("WebCore", "WebCore thread used " +
-                    (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
-                    + " ms and idled " + mWebCoreIdleTime + " ms");
-        }
-        Message msg = obtainMessage(PAGE_FINISHED, url);
-        sendMessage(msg);
-    }
-
-    // Because this method is public and because CallbackProxy is mistakenly
-    // party of the public classes, we cannot remove this method.
-    public void onTooManyRedirects(Message cancelMsg, Message continueMsg) {
-        // deprecated.
-    }
-
-    public void onReceivedError(int errorCode, String description,
-            String failingUrl) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            return;
-        }
-
-        Message msg = obtainMessage(REPORT_ERROR);
-        msg.arg1 = errorCode;
-        msg.getData().putString("description", description);
-        msg.getData().putString("failingUrl", failingUrl);
-        sendMessage(msg);
-    }
-
-    public void onFormResubmission(Message dontResend,
-            Message resend) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            dontResend.sendToTarget();
-            return;
-        }
-
-        Message msg = obtainMessage(RESEND_POST_DATA);
-        Bundle bundle = msg.getData();
-        bundle.putParcelable("resend", resend);
-        bundle.putParcelable("dontResend", dontResend);
-        sendMessage(msg);
-    }
-
-    /**
-     * Called by the WebCore side
-     */
-    public boolean shouldOverrideUrlLoading(String url) {
-        // We have a default behavior if no client exists so always send the
-        // message.
-        ResultTransport<Boolean> res = new ResultTransport<Boolean>(false);
-        Message msg = obtainMessage(OVERRIDE_URL);
-        msg.getData().putString("url", url);
-        msg.obj = res;
-        sendMessageToUiThreadSync(msg);
-        return res.getResult().booleanValue();
-    }
-
-    public void onReceivedHttpAuthRequest(HttpAuthHandler handler,
-            String hostName, String realmName) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            handler.cancel();
-            return;
-        }
-        Message msg = obtainMessage(AUTH_REQUEST, handler);
-        msg.getData().putString("host", hostName);
-        msg.getData().putString("realm", realmName);
-        sendMessage(msg);
-    }
-
-    public void onReceivedSslError(SslErrorHandler handler, SslError error) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            handler.cancel();
-            return;
-        }
-        Message msg = obtainMessage(SSL_ERROR);
-        HashMap<String, Object> map = new HashMap();
-        map.put("handler", handler);
-        map.put("error", error);
-        msg.obj = map;
-        sendMessage(msg);
-    }
-
-    public void onProceededAfterSslError(SslError error) {
-        if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) {
-            return;
-        }
-        Message msg = obtainMessage(PROCEEDED_AFTER_SSL_ERROR);
-        msg.obj = error;
-        sendMessage(msg);
-    }
-
-    public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) {
-            handler.cancel();
-            return;
-        }
-        Message msg = obtainMessage(CLIENT_CERT_REQUEST);
-        HashMap<String, Object> map = new HashMap();
-        map.put("handler", handler);
-        map.put("host_and_port", host_and_port);
-        msg.obj = map;
-        sendMessage(msg);
-    }
-
-    public void onReceivedCertificate(SslCertificate certificate) {
-        // here, certificate can be null (if the site is not secure)
-        sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate));
-    }
-
-    public void doUpdateVisitedHistory(String url, boolean isReload) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url));
-    }
-
-    WebResourceResponse shouldInterceptRequest(String url) {
-        if (mWebViewClient == null) {
-            return null;
-        }
-        // Note: This method does _not_ send a message.
-        if (TRACE) Log.d(LOGTAG, "shouldInterceptRequest=" + url);
-        WebResourceResponse r =
-                mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
-        if (r == null) {
-            sendMessage(obtainMessage(LOAD_RESOURCE, url));
-        }
-        return r;
-    }
-
-    public void onUnhandledKeyEvent(KeyEvent event) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(ASYNC_KEYEVENTS, event));
-    }
-
-    public void onScaleChanged(float oldScale, float newScale) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            return;
-        }
-        Message msg = obtainMessage(SCALE_CHANGED);
-        Bundle bundle = msg.getData();
-        bundle.putFloat("old", oldScale);
-        bundle.putFloat("new", newScale);
-        sendMessage(msg);
-    }
-
-    void onReceivedLoginRequest(String realm, String account, String args) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebViewClient == null) {
-            return;
-        }
-        Message msg = obtainMessage(AUTO_LOGIN);
-        Bundle bundle = msg.getData();
-        bundle.putString("realm", realm);
-        bundle.putString("account", account);
-        bundle.putString("args", args);
-        sendMessage(msg);
-    }
-
-    //--------------------------------------------------------------------------
-    // DownloadListener functions.
-    //--------------------------------------------------------------------------
-
-    /**
-     * Starts a download if a download listener has been registered, otherwise
-     * return false.
-     */
-    public boolean onDownloadStart(String url, String userAgent,
-            String contentDisposition, String mimetype, String referer,
-            long contentLength) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mDownloadListener == null) {
-            // Cancel the download if there is no browser client.
-            return false;
-        }
-
-        Message msg = obtainMessage(DOWNLOAD_FILE);
-        Bundle bundle = msg.getData();
-        bundle.putString("url", url);
-        bundle.putString("userAgent", userAgent);
-        bundle.putString("mimetype", mimetype);
-        bundle.putString("referer", referer);
-        bundle.putLong("contentLength", contentLength);
-        bundle.putString("contentDisposition", contentDisposition);
-        sendMessage(msg);
-        return true;
-    }
-
-
-    //--------------------------------------------------------------------------
-    // WebView specific functions that do not interact with a client. These
-    // functions just need to operate within the UI thread.
-    //--------------------------------------------------------------------------
-
-    public boolean onSavePassword(String schemePlusHost, String username,
-            String password, Message resumeMsg) {
-        // resumeMsg should be null at this point because we want to create it
-        // within the CallbackProxy.
-        if (DebugFlags.CALLBACK_PROXY) {
-            junit.framework.Assert.assertNull(resumeMsg);
-        }
-        resumeMsg = obtainMessage(NOTIFY);
-
-        Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg);
-        Bundle bundle = msg.getData();
-        bundle.putString("host", schemePlusHost);
-        bundle.putString("username", username);
-        bundle.putString("password", password);
-        sendMessageToUiThreadSync(msg);
-        // Doesn't matter here
-        return false;
-    }
-
-    public void onReceivedHttpAuthCredentials(String host, String realm,
-            String username, String password) {
-        Message msg = obtainMessage(AUTH_CREDENTIALS);
-        msg.getData().putString("host", host);
-        msg.getData().putString("realm", realm);
-        msg.getData().putString("username", username);
-        msg.getData().putString("password", password);
-        sendMessage(msg);
-    }
-
-    //--------------------------------------------------------------------------
-    // WebChromeClient methods
-    //--------------------------------------------------------------------------
-
-    public void onProgressChanged(int newProgress) {
-        // Synchronize so that mLatestProgress is up-to-date.
-        synchronized (this) {
-            // update mLatestProgress even mWebChromeClient is null as
-            // WebView.getProgress() needs it
-            if (mLatestProgress == newProgress) {
-                return;
-            }
-            mLatestProgress = newProgress;
-            if (mWebChromeClient == null) {
-                return;
-            }
-            if (!mProgressUpdatePending) {
-                sendEmptyMessage(PROGRESS);
-                mProgressUpdatePending = true;
-            }
-        }
-    }
-
-    public BrowserFrame createWindow(boolean dialog, boolean userGesture) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return null;
-        }
-
-        WebView.WebViewTransport transport =
-            mWebView.getWebView().new WebViewTransport();
-        final Message msg = obtainMessage(NOTIFY);
-        msg.obj = transport;
-        sendMessageToUiThreadSync(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0,
-                userGesture ? 1 : 0, msg));
-        WebViewClassic w = WebViewClassic.fromWebView(transport.getWebView());
-        if (w != null) {
-            WebViewCore core = w.getWebViewCore();
-            // If WebView.destroy() has been called, core may be null.  Skip
-            // initialization in that case and return null.
-            if (core != null) {
-                core.initializeSubwindow();
-                return core.getBrowserFrame();
-            }
-        }
-        return null;
-    }
-
-    public void onRequestFocus() {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-
-        sendEmptyMessage(REQUEST_FOCUS);
-    }
-
-    public void onCloseWindow(WebViewClassic window) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(CLOSE_WINDOW, window));
-    }
-
-    public void onReceivedIcon(Bitmap icon) {
-        // The current item might be null if the icon was already stored in the
-        // database and this is a new WebView.
-        WebHistoryItemClassic i = mBackForwardList.getCurrentItem();
-        if (i != null) {
-            i.setFavicon(icon);
-        }
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(RECEIVED_ICON, icon));
-    }
-
-    /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) {
-        // We should have a current item but we do not want to crash so check
-        // for null.
-        WebHistoryItemClassic i = mBackForwardList.getCurrentItem();
-        if (i != null) {
-            i.setTouchIconUrl(url, precomposed);
-        }
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL,
-                precomposed ? 1 : 0, 0, url));
-    }
-
-    public void onReceivedTitle(String title) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-        sendMessage(obtainMessage(RECEIVED_TITLE, title));
-    }
-
-    public void onJsAlert(String url, String message) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return;
-        }
-        JsResultReceiver result = new JsResultReceiver();
-        Message alert = obtainMessage(JS_DIALOG, result);
-        alert.getData().putString("message", message);
-        alert.getData().putString("url", url);
-        alert.getData().putInt("type", JsDialogHelper.ALERT);
-        sendMessageToUiThreadSync(alert);
-    }
-
-    public boolean onJsConfirm(String url, String message) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return false;
-        }
-        JsResultReceiver result = new JsResultReceiver();
-        Message confirm = obtainMessage(JS_DIALOG, result);
-        confirm.getData().putString("message", message);
-        confirm.getData().putString("url", url);
-        confirm.getData().putInt("type", JsDialogHelper.CONFIRM);
-        sendMessageToUiThreadSync(confirm);
-        return result.mJsResult.getResult();
-    }
-
-    public String onJsPrompt(String url, String message, String defaultValue) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return null;
-        }
-        JsResultReceiver result = new JsResultReceiver();
-        Message prompt = obtainMessage(JS_DIALOG, result);
-        prompt.getData().putString("message", message);
-        prompt.getData().putString("default", defaultValue);
-        prompt.getData().putString("url", url);
-        prompt.getData().putInt("type", JsDialogHelper.PROMPT);
-        sendMessageToUiThreadSync(prompt);
-        return result.mJsResult.getStringResult();
-    }
-
-    public boolean onJsBeforeUnload(String url, String message) {
-        // Do an unsynchronized quick check to avoid posting if no callback has
-        // been set.
-        if (mWebChromeClient == null) {
-            return true;
-        }
-        JsResultReceiver result = new JsResultReceiver();
-        Message unload = obtainMessage(JS_DIALOG, result);
-        unload.getData().putString("message", message);
-        unload.getData().putString("url", url);
-        unload.getData().putInt("type", JsDialogHelper.UNLOAD);
-        sendMessageToUiThreadSync(unload);
-        return result.mJsResult.getResult();
-    }
-
-    /**
-     * Called by WebViewCore to inform the Java side that the current origin
-     * has overflowed it's database quota. Called in the WebCore thread so
-     * posts a message to the UI thread that will prompt the WebChromeClient
-     * for what to do. On return back to C++ side, the WebCore thread will
-     * sleep pending a new quota value.
-     * @param url The URL that caused the quota overflow.
-     * @param databaseIdentifier The identifier of the database that the
-     *     transaction that caused the overflow was running on.
-     * @param quota The current quota the origin is allowed.
-     * @param estimatedDatabaseSize The estimated size of the database.
-     * @param totalQuota is the sum of all origins' quota.
-     * @param quotaUpdater An instance of a class encapsulating a callback
-     *     to WebViewCore to run when the decision to allow or deny more
-     *     quota has been made.
-     */
-    public void onExceededDatabaseQuota(
-            String url, String databaseIdentifier, long quota,
-            long estimatedDatabaseSize, long totalQuota,
-            WebStorage.QuotaUpdater quotaUpdater) {
-        if (mWebChromeClient == null) {
-            // Native-side logic prevents the quota being updated to a smaller
-            // value.
-            quotaUpdater.updateQuota(quota);
-            return;
-        }
-
-        Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA);
-        HashMap<String, Object> map = new HashMap();
-        map.put("databaseIdentifier", databaseIdentifier);
-        map.put("url", url);
-        map.put("quota", quota);
-        map.put("estimatedDatabaseSize", estimatedDatabaseSize);
-        map.put("totalQuota", totalQuota);
-        map.put("quotaUpdater", quotaUpdater);
-        exceededQuota.obj = map;
-        sendMessage(exceededQuota);
-    }
-
-    /**
-     * Called by WebViewCore to inform the Java side that the appcache has
-     * exceeded its max size.
-     * @param requiredStorage is the amount of storage, in bytes, that would be
-     * needed in order for the last appcache operation to succeed.
-     * @param quota is the current quota (for all origins).
-     * @param quotaUpdater An instance of a class encapsulating a callback
-     * to WebViewCore to run when the decision to allow or deny a bigger
-     * app cache size has been made.
-     */
-    public void onReachedMaxAppCacheSize(long requiredStorage,
-            long quota, WebStorage.QuotaUpdater quotaUpdater) {
-        if (mWebChromeClient == null) {
-            // Native-side logic prevents the quota being updated to a smaller
-            // value.
-            quotaUpdater.updateQuota(quota);
-            return;
-        }
-
-        Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
-        HashMap<String, Object> map = new HashMap();
-        map.put("requiredStorage", requiredStorage);
-        map.put("quota", quota);
-        map.put("quotaUpdater", quotaUpdater);
-        msg.obj = map;
-        sendMessage(msg);
-    }
-
-    /**
-     * Called by WebViewCore to instruct the browser to display a prompt to ask
-     * the user to set the Geolocation permission state for the given origin.
-     * @param origin The origin requesting Geolocation permsissions.
-     * @param callback The callback to call once a permission state has been
-     *     obtained.
-     */
-    public void onGeolocationPermissionsShowPrompt(String origin,
-            GeolocationPermissions.Callback callback) {
-        if (mWebChromeClient == null) {
-            return;
-        }
-
-        Message showMessage =
-                obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT);
-        HashMap<String, Object> map = new HashMap();
-        map.put("origin", origin);
-        map.put("callback", callback);
-        showMessage.obj = map;
-        sendMessage(showMessage);
-    }
-
-    /**
-     * Called by WebViewCore to instruct the browser to hide the Geolocation
-     * permissions prompt.
-     */
-    public void onGeolocationPermissionsHidePrompt() {
-        if (mWebChromeClient == null) {
-            return;
-        }
-
-        Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT);
-        sendMessage(hideMessage);
-    }
-
-    /**
-     * Called by WebViewCore when we have a message to be added to the JavaScript
-     * error console. Sends a message to the Java side with the details.
-     * @param message The message to add to the console.
-     * @param lineNumber The lineNumber of the source file on which the error
-     *     occurred.
-     * @param sourceID The filename of the source file in which the error
-     *     occurred.
-     * @param msgLevel The message level, corresponding to the MessageLevel enum in
-     *     WebCore/page/Console.h
-     */
-    public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
-        if (mWebChromeClient == null) {
-            return;
-        }
-
-        Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
-        msg.getData().putString("message", message);
-        msg.getData().putString("sourceID", sourceID);
-        msg.getData().putInt("lineNumber", lineNumber);
-        msg.getData().putInt("msgLevel", msgLevel);
-        sendMessage(msg);
-    }
-
-    public boolean onJsTimeout() {
-        //always interrupt timedout JS by default
-        if (mWebChromeClient == null) {
-            return true;
-        }
-        JsResultReceiver result = new JsResultReceiver();
-        Message timeout = obtainMessage(JS_TIMEOUT, result);
-        sendMessageToUiThreadSync(timeout);
-        return result.mJsResult.getResult();
-    }
-
-    public void getVisitedHistory(ValueCallback<String[]> callback) {
-        if (mWebChromeClient == null) {
-            return;
-        }
-        Message msg = obtainMessage(GET_VISITED_HISTORY);
-        msg.obj = callback;
-        sendMessage(msg);
-    }
-
-    private static class UploadFileMessageData {
-        private UploadFile mCallback;
-        private String mAcceptType;
-        private String mCapture;
-
-        public UploadFileMessageData(UploadFile uploadFile, String acceptType, String capture) {
-            mCallback = uploadFile;
-            mAcceptType = acceptType;
-            mCapture = capture;
-        }
-
-        public UploadFile getUploadFile() {
-            return mCallback;
-        }
-
-        public String getAcceptType() {
-            return mAcceptType;
-        }
-
-        public String getCapture() {
-            return mCapture;
-        }
-    }
-
-    private class UploadFile implements ValueCallback<Uri> {
-        private Uri mValue;
-        public void onReceiveValue(Uri value) {
-            mValue = value;
-            synchronized (CallbackProxy.this) {
-                CallbackProxy.this.notify();
-            }
-        }
-        public Uri getResult() {
-            return mValue;
-        }
-    }
-
-    /**
-     * Called by WebViewCore to open a file chooser.
-     */
-    /* package */ Uri openFileChooser(String acceptType, String capture) {
-        if (mWebChromeClient == null) {
-            return null;
-        }
-        Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
-        UploadFile uploadFile = new UploadFile();
-        UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType, capture);
-        myMessage.obj = data;
-        sendMessageToUiThreadSync(myMessage);
-        return uploadFile.getResult();
-    }
-
-    void onNewHistoryItem(WebHistoryItem item) {
-        if (mWebBackForwardListClient == null) {
-            return;
-        }
-        Message msg = obtainMessage(ADD_HISTORY_ITEM, item);
-        sendMessage(msg);
-    }
-
-    void onIndexChanged(WebHistoryItem item, int index) {
-        if (mWebBackForwardListClient == null) {
-            return;
-        }
-        Message msg = obtainMessage(HISTORY_INDEX_CHANGED, index, 0, item);
-        sendMessage(msg);
-    }
-
-    private synchronized void sendMessageToUiThreadSync(Message msg) {
-        sendMessage(msg);
-        WebCoreThreadWatchdog.pause();
-        try {
-            wait();
-        } catch (InterruptedException e) {
-            Log.e(LOGTAG, "Caught exception waiting for synchronous UI message to be processed");
-            Log.e(LOGTAG, Log.getStackTraceString(e));
-        }
-        WebCoreThreadWatchdog.resume();
-    }
-}
diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java
deleted file mode 100644
index e4d09a9..0000000
--- a/core/java/android/webkit/CertTool.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.org.bouncycastle.asn1.ASN1Encoding;
-import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest;
-import com.android.org.bouncycastle.util.encoders.Base64;
-
-import android.content.Context;
-import android.security.Credentials;
-import android.security.KeyChain;
-import android.util.Log;
-
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.util.HashMap;
-
-final class CertTool {
-    private static final String LOGTAG = "CertTool";
-
-    private static final AlgorithmIdentifier MD5_WITH_RSA =
-            new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption);
-
-    private static HashMap<String, String> sCertificateTypeMap;
-    static {
-        sCertificateTypeMap = new HashMap<String, String>();
-        sCertificateTypeMap.put("application/x-x509-ca-cert", KeyChain.EXTRA_CERTIFICATE);
-        sCertificateTypeMap.put("application/x-x509-user-cert", KeyChain.EXTRA_CERTIFICATE);
-        sCertificateTypeMap.put("application/x-pkcs12", KeyChain.EXTRA_PKCS12);
-    }
-
-    static String[] getKeyStrengthList() {
-        return new String[] {"High Grade", "Medium Grade"};
-    }
-
-    static String getSignedPublicKey(Context context, int index, String challenge) {
-        try {
-            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
-            generator.initialize((index == 0) ? 2048 : 1024);
-            KeyPair pair = generator.genKeyPair();
-
-            NetscapeCertRequest request = new NetscapeCertRequest(challenge,
-                    MD5_WITH_RSA, pair.getPublic());
-            request.sign(pair.getPrivate());
-            byte[] signed = request.toASN1Primitive().getEncoded(ASN1Encoding.DER);
-
-            Credentials.getInstance().install(context, pair);
-            return new String(Base64.encode(signed));
-        } catch (Exception e) {
-            Log.w(LOGTAG, e);
-        }
-        return null;
-    }
-
-    static void addCertificate(Context context, String type, byte[] value) {
-        Credentials.getInstance().install(context, type, value);
-    }
-
-    static String getCertType(String mimeType) {
-        return sCertificateTypeMap.get(mimeType);
-    }
-
-    private CertTool() {}
-}
diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java
deleted file mode 100644
index d7a6806..0000000
--- a/core/java/android/webkit/ClientCertRequestHandler.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2011 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.os.Handler;
-import java.security.PrivateKey;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import com.android.org.conscrypt.NativeCrypto;
-import com.android.org.conscrypt.OpenSSLKey;
-import com.android.org.conscrypt.OpenSSLKeyHolder;
-
-/**
- * ClientCertRequestHandler: class responsible for handling client
- * certificate requests.  This class is passed as a parameter to
- * BrowserCallback.displayClientCertRequestDialog and is meant to
- * receive the user's response.
- *
- * @hide
- */
-public final class ClientCertRequestHandler extends Handler {
-
-    private final BrowserFrame mBrowserFrame;
-    private final int mHandle;
-    private final String mHostAndPort;
-    private final SslClientCertLookupTable mTable;
-    ClientCertRequestHandler(BrowserFrame browserFrame,
-                             int handle,
-                             String host_and_port,
-                             SslClientCertLookupTable table) {
-        mBrowserFrame = browserFrame;
-        mHandle = handle;
-        mHostAndPort = host_and_port;
-        mTable = table;
-    }
-
-    /**
-     * Proceed with the specified private key and client certificate chain.
-     */
-    public void proceed(PrivateKey privateKey, X509Certificate[] chain) {
-        try {
-            byte[][] chainBytes = NativeCrypto.encodeCertificates(chain);
-            mTable.Allow(mHostAndPort, privateKey, chainBytes);
-
-            if (privateKey instanceof OpenSSLKeyHolder) {
-                OpenSSLKey pkey = ((OpenSSLKeyHolder) privateKey).getOpenSSLKey();
-                setSslClientCertFromCtx(pkey.getPkeyContext(), chainBytes);
-            } else {
-                setSslClientCertFromPKCS8(privateKey.getEncoded(), chainBytes);
-            }
-        } catch (CertificateEncodingException e) {
-            post(new Runnable() {
-                    public void run() {
-                        mBrowserFrame.nativeSslClientCert(mHandle, 0, null);
-                        return;
-                    }
-                });
-        }
-    }
-
-    /**
-     * Proceed with the specified private key bytes and client certificate chain.
-     */
-    private void setSslClientCertFromCtx(final long ctx, final byte[][] chainBytes) {
-        post(new Runnable() {
-                public void run() {
-                    mBrowserFrame.nativeSslClientCert(mHandle, ctx, chainBytes);
-                }
-            });
-    }
-
-    /**
-     * Proceed with the specified private key context and client certificate chain.
-     */
-    private void setSslClientCertFromPKCS8(final byte[] key, final byte[][] chainBytes) {
-        post(new Runnable() {
-                public void run() {
-                    mBrowserFrame.nativeSslClientCert(mHandle, key, chainBytes);
-                }
-            });
-    }
-
-    /**
-     * Igore the request for now, the user may be prompted again.
-     */
-    public void ignore() {
-        post(new Runnable() {
-                public void run() {
-                    mBrowserFrame.nativeSslClientCert(mHandle, 0, null);
-                }
-            });
-    }
-
-    /**
-     * Cancel this request, remember the users negative choice.
-     */
-    public void cancel() {
-        mTable.Deny(mHostAndPort);
-        post(new Runnable() {
-                public void run() {
-                    mBrowserFrame.nativeSslClientCert(mHandle, 0, null);
-                }
-            });
-    }
-}
diff --git a/core/java/android/webkit/CookieManagerClassic.java b/core/java/android/webkit/CookieManagerClassic.java
deleted file mode 100644
index 36159e1..0000000
--- a/core/java/android/webkit/CookieManagerClassic.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.net.ParseException;
-import android.net.WebAddress;
-import android.os.AsyncTask;
-import android.util.Log;
-
-class CookieManagerClassic extends CookieManager {
-
-    private static CookieManagerClassic sRef;
-
-    private static final String LOGTAG = "webkit";
-
-    private int mPendingCookieOperations = 0;
-
-    private CookieManagerClassic() {
-    }
-
-    public static synchronized CookieManagerClassic getInstance() {
-        if (sRef == null) {
-            sRef = new CookieManagerClassic();
-        }
-        return sRef;
-    }
-
-    @Override
-    public synchronized void setAcceptCookie(boolean accept) {
-        nativeSetAcceptCookie(accept);
-    }
-
-    @Override
-    public synchronized boolean acceptCookie() {
-        return nativeAcceptCookie();
-    }
-
-    @Override
-    public void setCookie(String url, String value) {
-        setCookie(url, value, false);
-    }
-
-    /**
-     * See {@link #setCookie(String, String)}
-     * @param url The URL for which the cookie is set
-     * @param value The value of the cookie, as a string, using the format of
-     *              the 'Set-Cookie' HTTP response header
-     * @param privateBrowsing Whether to use the private browsing cookie jar
-     */
-    void setCookie(String url, String value, boolean privateBrowsing) {
-        WebAddress uri;
-        try {
-            uri = new WebAddress(url);
-        } catch (ParseException ex) {
-            Log.e(LOGTAG, "Bad address: " + url);
-            return;
-        }
-
-        nativeSetCookie(uri.toString(), value, privateBrowsing);
-    }
-
-    @Override
-    public String getCookie(String url) {
-        return getCookie(url, false);
-    }
-
-    @Override
-    public String getCookie(String url, boolean privateBrowsing) {
-        WebAddress uri;
-        try {
-            uri = new WebAddress(url);
-        } catch (ParseException ex) {
-            Log.e(LOGTAG, "Bad address: " + url);
-            return null;
-        }
-
-        return nativeGetCookie(uri.toString(), privateBrowsing);
-    }
-
-    @Override
-    public synchronized String getCookie(WebAddress uri) {
-        return nativeGetCookie(uri.toString(), false);
-    }
-
-    /**
-     * Waits for pending operations to completed.
-     */
-    void waitForCookieOperationsToComplete() {
-        // Note that this function is applicable for both the java
-        // and native http stacks, and works correctly with either.
-        synchronized (this) {
-            while (mPendingCookieOperations > 0) {
-                try {
-                    wait();
-                } catch (InterruptedException e) { }
-            }
-        }
-    }
-
-    private synchronized void signalCookieOperationsComplete() {
-        mPendingCookieOperations--;
-        assert mPendingCookieOperations > -1;
-        notify();
-    }
-
-    private synchronized void signalCookieOperationsStart() {
-        mPendingCookieOperations++;
-    }
-
-    @Override
-    public void removeSessionCookie() {
-        signalCookieOperationsStart();
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... none) {
-                nativeRemoveSessionCookie();
-                signalCookieOperationsComplete();
-                return null;
-            }
-        }.execute();
-    }
-
-    @Override
-    public void removeAllCookie() {
-        nativeRemoveAllCookie();
-    }
-
-    @Override
-    public synchronized boolean hasCookies() {
-        return hasCookies(false);
-    }
-
-    @Override
-    public synchronized boolean hasCookies(boolean privateBrowsing) {
-        return nativeHasCookies(privateBrowsing);
-    }
-
-    @Override
-    public void removeExpiredCookie() {
-        nativeRemoveExpiredCookie();
-    }
-
-    @Override
-    protected void flushCookieStore() {
-        nativeFlushCookieStore();
-    }
-
-    @Override
-    protected boolean allowFileSchemeCookiesImpl() {
-        return nativeAcceptFileSchemeCookies();
-    }
-
-    @Override
-    protected void setAcceptFileSchemeCookiesImpl(boolean accept) {
-        nativeSetAcceptFileSchemeCookies(accept);
-    }
-
-    // Native functions
-    private static native boolean nativeAcceptCookie();
-    private static native String nativeGetCookie(String url, boolean privateBrowsing);
-    private static native boolean nativeHasCookies(boolean privateBrowsing);
-    private static native void nativeRemoveAllCookie();
-    private static native void nativeRemoveExpiredCookie();
-    private static native void nativeRemoveSessionCookie();
-    private static native void nativeSetAcceptCookie(boolean accept);
-    private static native void nativeSetCookie(String url, String value, boolean privateBrowsing);
-    private static native void nativeFlushCookieStore();
-    private static native boolean nativeAcceptFileSchemeCookies();
-    private static native void nativeSetAcceptFileSchemeCookies(boolean accept);
-}
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index 154a290..13aa43f 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -89,10 +89,6 @@
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
         }
-        // TODO: Remove this workaround after webview classic is no longer supported.
-        if (WebViewFactory.getProvider().getClass().getName().contains("WebViewClassic")) {
-            WebViewDatabase.getInstance(context);
-        }
 
         setGetInstanceIsAllowed();
         return getInstance();
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 524f610..b5ca8c1 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -35,22 +35,4 @@
     public static final boolean URL_UTIL = false;
     public static final boolean WEB_SYNC_MANAGER = false;
 
-    // TODO: Delete these when WebViewClassic is moved
-    public static final boolean BROWSER_FRAME = false;
-    public static final boolean CACHE_MANAGER = false;
-    public static final boolean CALLBACK_PROXY = false;
-    public static final boolean COOKIE_MANAGER = false;
-    public static final boolean FRAME_LOADER = false;
-    public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
-    public static final boolean LOAD_LISTENER = false;
-    public static final boolean MEASURE_PAGE_SWAP_FPS = false;
-    public static final boolean NETWORK = false;
-    public static final boolean SSL_ERROR_HANDLER = false;
-    public static final boolean STREAM_LOADER = false;
-    public static final boolean WEB_BACK_FORWARD_LIST = false;
-    public static final boolean WEB_SETTINGS = false;
-    public static final boolean WEB_VIEW = false;
-    public static final boolean WEB_VIEW_CORE = false;
-
-
 }
diff --git a/core/java/android/webkit/DeviceMotionAndOrientationManager.java b/core/java/android/webkit/DeviceMotionAndOrientationManager.java
deleted file mode 100644
index ea1e9ff..0000000
--- a/core/java/android/webkit/DeviceMotionAndOrientationManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-/**
- * This class is simply a container for the methods used to implement DeviceMotion and
- * DeviceOrientation, including the mock DeviceOrientationClient for use in LayoutTests.
- *
- * This could be part of WebViewCore, but have moved it to its own class to
- * avoid bloat there.
- */
-final class DeviceMotionAndOrientationManager {
-    private WebViewCore mWebViewCore;
-
-    public DeviceMotionAndOrientationManager(WebViewCore webViewCore) {
-        mWebViewCore = webViewCore;
-    }
-
-    /**
-     * Sets that the Page for this WebViewCore should use a mock DeviceOrientation
-     * client.
-     */
-    public void setUseMock() {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        nativeSetUseMock(mWebViewCore);
-    }
-
-    /**
-     * Set the position for the mock DeviceOrientation service for this WebViewCore.
-     */
-    public void setMockOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta,
-            double beta, boolean canProvideGamma, double gamma) {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        nativeSetMockOrientation(mWebViewCore, canProvideAlpha, alpha, canProvideBeta, beta,
-                canProvideGamma, gamma);
-    }
-
-    // We only provide accelerationIncludingGravity.
-    public void onMotionChange(Double x, Double y, Double z, double interval) {
-        nativeOnMotionChange(mWebViewCore,
-                x != null, x != null ? x.doubleValue() : 0.0,
-                y != null, y != null ? y.doubleValue() : 0.0,
-                z != null, z != null ? z.doubleValue() : 0.0,
-                interval);
-    }
-    public void onOrientationChange(Double alpha, Double beta, Double gamma) {
-        nativeOnOrientationChange(mWebViewCore,
-                alpha != null, alpha != null ? alpha.doubleValue() : 0.0,
-                beta != null, beta != null ? beta.doubleValue() : 0.0,
-                gamma != null, gamma != null ? gamma.doubleValue() : 0.0);
-    }
-
-    // Native functions
-    private static native void nativeSetUseMock(WebViewCore webViewCore);
-    private static native void nativeSetMockOrientation(WebViewCore webViewCore,
-            boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
-            boolean canProvideGamma, double gamma);
-    private static native void nativeOnMotionChange(WebViewCore webViewCore,
-            boolean canProvideX, double x, boolean canProvideY, double y,
-            boolean canProvideZ, double z, double interval);
-    private static native void nativeOnOrientationChange(WebViewCore webViewCore,
-            boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
-            boolean canProvideGamma, double gamma);
-}
diff --git a/core/java/android/webkit/DeviceMotionService.java b/core/java/android/webkit/DeviceMotionService.java
deleted file mode 100644
index 9121429..0000000
--- a/core/java/android/webkit/DeviceMotionService.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Handler;
-import android.os.Message;
-import android.webkit.DeviceMotionAndOrientationManager;
-import java.lang.Runnable;
-import java.util.List;
-
-
-final class DeviceMotionService implements SensorEventListener {
-    private DeviceMotionAndOrientationManager mManager;
-    private boolean mIsRunning;
-    private Handler mHandler;
-    private SensorManager mSensorManager;
-    private Context mContext;
-    private boolean mHaveSentErrorEvent;
-    private Runnable mUpdateRunnable;
-    private float mLastAcceleration[];
-
-    private static final int INTERVAL_MILLIS = 100;
-
-    public DeviceMotionService(DeviceMotionAndOrientationManager manager, Context context) {
-        mManager = manager;
-        assert(mManager != null);
-        mContext = context;
-        assert(mContext != null);
-     }
-
-    public void start() {
-        mIsRunning = true;
-        registerForSensor();
-    }
-
-    public void stop() {
-        mIsRunning = false;
-        stopSendingUpdates();
-        unregisterFromSensor();
-    }
-
-    public void suspend() {
-        if (mIsRunning) {
-            stopSendingUpdates();
-            unregisterFromSensor();
-        }
-    }
-
-    public void resume() {
-        if (mIsRunning) {
-            registerForSensor();
-        }
-    }
-
-    private void sendErrorEvent() {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        // The spec requires that each listener receives the error event only once.
-        if (mHaveSentErrorEvent)
-            return;
-        mHaveSentErrorEvent = true;
-        createHandler();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-                if (mIsRunning) {
-                    // The special case of all nulls is used to signify a failure to get data.
-                    mManager.onMotionChange(null, null, null, 0.0);
-                }
-            }
-        });
-    }
-
-    private void createHandler() {
-        if (mHandler != null) {
-            return;
-        }
-
-        mHandler = new Handler();
-        mUpdateRunnable = new Runnable() {
-            @Override
-            public void run() {
-                assert mIsRunning;
-                mManager.onMotionChange(new Double(mLastAcceleration[0]),
-                        new Double(mLastAcceleration[1]), new Double(mLastAcceleration[2]),
-                        INTERVAL_MILLIS);
-                mHandler.postDelayed(mUpdateRunnable, INTERVAL_MILLIS);
-                // Now that we have successfully sent some data, reset whether we've sent an error.
-                mHaveSentErrorEvent = false;
-            }
-        };
-    }
-
-    private void startSendingUpdates() {
-        createHandler();
-        mUpdateRunnable.run();
-    }
-
-    private void stopSendingUpdates() {
-        mHandler.removeCallbacks(mUpdateRunnable);
-        mLastAcceleration = null;
-    }
-
-    private void registerForSensor() {
-        if (!registerForAccelerometerSensor()) {
-            sendErrorEvent();
-        }
-    }
-
-    private SensorManager getSensorManager() {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        if (mSensorManager == null) {
-            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
-        }
-        return mSensorManager;
-    }
-
-    private boolean registerForAccelerometerSensor() {
-        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
-        if (sensors.isEmpty()) {
-            return false;
-        }
-        createHandler();
-        // TODO: Consider handling multiple sensors.
-        return getSensorManager().registerListener(
-                this, sensors.get(0), SensorManager.SENSOR_DELAY_UI, mHandler);
-    }
-
-    private void unregisterFromSensor() {
-        getSensorManager().unregisterListener(this);
-    }
-
-    /**
-     * SensorEventListener implementation.
-     * Callbacks happen on the thread on which we registered - the WebCore thread.
-     */
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        assert(event.values.length == 3);
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        assert(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER);
-
-        // We may get callbacks after the call to getSensorManager().unregisterListener() returns.
-        if (!mIsRunning) {
-            return;
-        }
-
-        boolean firstData = mLastAcceleration == null;
-        mLastAcceleration = event.values;
-        if (firstData) {
-            startSendingUpdates();
-        }
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-    }
-}
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
deleted file mode 100644
index a4d240d..0000000
--- a/core/java/android/webkit/DeviceOrientationService.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Handler;
-import android.webkit.DeviceMotionAndOrientationManager;
-import java.lang.Runnable;
-import java.util.List;
-
-
-final class DeviceOrientationService implements SensorEventListener {
-    // The gravity vector expressed in the body frame.
-    private float[] mGravityVector;
-    // The geomagnetic vector expressed in the body frame.
-    private float[] mMagneticFieldVector;
-
-    private DeviceMotionAndOrientationManager mManager;
-    private boolean mIsRunning;
-    private Handler mHandler;
-    private SensorManager mSensorManager;
-    private Context mContext;
-    private Double mAlpha;
-    private Double mBeta;
-    private Double mGamma;
-    private boolean mHaveSentErrorEvent;
-
-    private static final double DELTA_DEGRESS = 1.0;
-
-    public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) {
-        mManager = manager;
-        assert(mManager != null);
-        mContext = context;
-        assert(mContext != null);
-     }
-
-    public void start() {
-        mIsRunning = true;
-        registerForSensors();
-    }
-
-    public void stop() {
-        mIsRunning = false;
-        unregisterFromSensors();
-    }
-
-    public void suspend() {
-        if (mIsRunning) {
-            unregisterFromSensors();
-        }
-    }
-
-    public void resume() {
-        if (mIsRunning) {
-            registerForSensors();
-        }
-    }
-
-    private void sendErrorEvent() {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        // The spec requires that each listener receives the error event only once.
-        if (mHaveSentErrorEvent)
-            return;
-        mHaveSentErrorEvent = true;
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-                if (mIsRunning) {
-                    // The special case of all nulls is used to signify a failure to get data.
-                    mManager.onOrientationChange(null, null, null);
-                }
-            }
-        });
-    }
-
-    private void registerForSensors() {
-        if (mHandler == null) {
-            mHandler = new Handler();
-        }
-        if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
-            unregisterFromSensors();
-            sendErrorEvent();
-        }
-    }
-
-    private void getOrientationUsingGetRotationMatrix() {
-        if (mGravityVector == null || mMagneticFieldVector == null) {
-            return;
-        }
-
-        // Get the rotation matrix.
-        // The rotation matrix that transforms from the body frame to the earth frame.
-        float[] deviceRotationMatrix = new float[9];
-        if (!SensorManager.getRotationMatrix(
-                deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) {
-            return;
-        }
-
-        // Convert rotation matrix to rotation angles.
-        // Assuming that the rotations are appied in the order listed at
-        // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
-        // the rotations are applied about the same axes and in the same order as required by the
-        // API. The only conversions are sign changes as follows.
-        // The angles are in radians
-        float[] rotationAngles = new float[3];
-        SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
-        double alpha = Math.toDegrees(-rotationAngles[0]);
-        while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
-        double beta = Math.toDegrees(-rotationAngles[1]);
-        while (beta < -180.0) { beta += 360.0; } // [-180, 180)
-        double gamma = Math.toDegrees(rotationAngles[2]);
-        while (gamma < -90.0) { gamma += 360.0; } // [-90, 90)
-
-        maybeSendChange(alpha, beta, gamma);
-    }
-
-    private SensorManager getSensorManager() {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        if (mSensorManager == null) {
-            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
-        }
-        return mSensorManager;
-    }
-
-    private boolean registerForAccelerometerSensor() {
-        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
-        if (sensors.isEmpty()) {
-            return false;
-        }
-        // TODO: Consider handling multiple sensors.
-        return getSensorManager().registerListener(
-                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
-    }
-
-    private boolean registerForMagneticFieldSensor() {
-        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
-        if (sensors.isEmpty()) {
-            return false;
-        }
-        // TODO: Consider handling multiple sensors.
-        return getSensorManager().registerListener(
-                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
-    }
-
-    private void unregisterFromSensors() {
-        getSensorManager().unregisterListener(this);
-    }
-
-    private void maybeSendChange(double alpha, double beta, double gamma) {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-        if (mAlpha == null || mBeta == null || mGamma == null
-                || Math.abs(alpha - mAlpha) > DELTA_DEGRESS
-                || Math.abs(beta - mBeta) > DELTA_DEGRESS
-                || Math.abs(gamma - mGamma) > DELTA_DEGRESS) {
-            mAlpha = alpha;
-            mBeta = beta;
-            mGamma = gamma;
-            mManager.onOrientationChange(mAlpha, mBeta, mGamma);
-            // Now that we have successfully sent some data, reset whether we've sent an error.
-            mHaveSentErrorEvent = false;
-        }
-    }
-
-    /**
-     * SensorEventListener implementation.
-     * Callbacks happen on the thread on which we registered - the WebCore thread.
-     */
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        assert(event.values.length == 3);
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-
-        // We may get callbacks after the call to getSensorManager().unregisterListener() returns.
-        if (!mIsRunning) {
-            return;
-        }
-
-        switch (event.sensor.getType()) {
-          case Sensor.TYPE_ACCELEROMETER:
-            if (mGravityVector == null) {
-                mGravityVector = new float[3];
-            }
-            mGravityVector[0] = event.values[0];
-            mGravityVector[1] = event.values[1];
-            mGravityVector[2] = event.values[2];
-            getOrientationUsingGetRotationMatrix();
-            break;
-          case Sensor.TYPE_MAGNETIC_FIELD:
-            if (mMagneticFieldVector == null) {
-                mMagneticFieldVector = new float[3];
-            }
-            mMagneticFieldVector[0] = event.values[0];
-            mMagneticFieldVector[1] = event.values[1];
-            mMagneticFieldVector[2] = event.values[2];
-            getOrientationUsingGetRotationMatrix();
-            break;
-          default:
-            assert(false);
-        }
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
-    }
-}
diff --git a/core/java/android/webkit/GeolocationPermissionsClassic.java b/core/java/android/webkit/GeolocationPermissionsClassic.java
deleted file mode 100644
index 8a9df39..0000000
--- a/core/java/android/webkit/GeolocationPermissionsClassic.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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.os.Handler;
-import android.os.Message;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-// This class is the Java counterpart of the WebKit C++ GeolocationPermissions
-// class. It simply marshals calls from the UI thread to the WebKit thread.
-final class GeolocationPermissionsClassic extends GeolocationPermissions {
-    private Handler mHandler;
-    private Handler mUIHandler;
-
-    // A queue to store messages until the handler is ready.
-    private Vector<Message> mQueuedMessages;
-
-    // Message ids
-    static final int GET_ORIGINS = 0;
-    static final int GET_ALLOWED = 1;
-    static final int CLEAR = 2;
-    static final int ALLOW = 3;
-    static final int CLEAR_ALL = 4;
-
-    // Message ids on the UI thread
-    static final int RETURN_ORIGINS = 0;
-    static final int RETURN_ALLOWED = 1;
-
-    private static final String ORIGINS = "origins";
-    private static final String ORIGIN = "origin";
-    private static final String CALLBACK = "callback";
-    private static final String ALLOWED = "allowed";
-
-    // Global instance
-    private static GeolocationPermissionsClassic sInstance;
-
-    public static GeolocationPermissionsClassic getInstance() {
-        if (sInstance == null) {
-            sInstance = new GeolocationPermissionsClassic();
-        }
-        return sInstance;
-      }
-
-    /**
-     * Creates the UI message handler. Must be called on the UI thread.
-     * @hide
-     */
-    public void createUIHandler() {
-        if (mUIHandler == null) {
-            mUIHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    // Runs on the UI thread.
-                    switch (msg.what) {
-                        case RETURN_ORIGINS: {
-                            Map values = (Map) msg.obj;
-                            Set<String> origins = (Set<String>) values.get(ORIGINS);
-                            ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK);
-                            callback.onReceiveValue(origins);
-                        } break;
-                        case RETURN_ALLOWED: {
-                            Map values = (Map) msg.obj;
-                            Boolean allowed = (Boolean) values.get(ALLOWED);
-                            ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK);
-                            callback.onReceiveValue(allowed);
-                        } break;
-                    }
-                }
-            };
-        }
-    }
-
-    /**
-     * Creates the message handler. Must be called on the WebKit thread.
-     * @hide
-     */
-    public synchronized void createHandler() {
-        if (mHandler == null) {
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    // Runs on the WebKit thread.
-                    switch (msg.what) {
-                        case GET_ORIGINS: {
-                            Set origins = nativeGetOrigins();
-                            ValueCallback callback = (ValueCallback) msg.obj;
-                            Map values = new HashMap<String, Object>();
-                            values.put(CALLBACK, callback);
-                            values.put(ORIGINS, origins);
-                            postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
-                            } break;
-                        case GET_ALLOWED: {
-                            Map values = (Map) msg.obj;
-                            String origin = (String) values.get(ORIGIN);
-                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
-                            boolean allowed = nativeGetAllowed(origin);
-                            Map retValues = new HashMap<String, Object>();
-                            retValues.put(CALLBACK, callback);
-                            retValues.put(ALLOWED, Boolean.valueOf(allowed));
-                            postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues));
-                            } break;
-                        case CLEAR:
-                            nativeClear((String) msg.obj);
-                            break;
-                        case ALLOW:
-                            nativeAllow((String) msg.obj);
-                            break;
-                        case CLEAR_ALL:
-                            nativeClearAll();
-                            break;
-                    }
-                }
-            };
-
-            // Handle the queued messages
-            if (mQueuedMessages != null) {
-                while (!mQueuedMessages.isEmpty()) {
-                    mHandler.sendMessage(mQueuedMessages.remove(0));
-                }
-                mQueuedMessages = null;
-            }
-        }
-    }
-
-    /**
-     * Utility function to send a message to our handler.
-     */
-    private synchronized void postMessage(Message msg) {
-        if (mHandler == null) {
-            if (mQueuedMessages == null) {
-                mQueuedMessages = new Vector<Message>();
-            }
-            mQueuedMessages.add(msg);
-        } else {
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    /**
-     * Utility function to send a message to the handler on the UI thread
-     */
-    private void postUIMessage(Message msg) {
-        if (mUIHandler != null) {
-            mUIHandler.sendMessage(msg);
-        }
-    }
-
-    // Note that we represent the origins as strings. These are created using
-    // WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
-    // (Database, Geolocation etc) do so, it's safe to match up origins based
-    // on this string.
-    @Override
-    public void getOrigins(ValueCallback<Set<String> > callback) {
-        if (callback != null) {
-            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-                Set origins = nativeGetOrigins();
-                callback.onReceiveValue(origins);
-            } else {
-                postMessage(Message.obtain(null, GET_ORIGINS, callback));
-            }
-        }
-    }
-
-    @Override
-    public void getAllowed(String origin, ValueCallback<Boolean> callback) {
-        if (callback == null) {
-            return;
-        }
-        if (origin == null) {
-            callback.onReceiveValue(null);
-            return;
-        }
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            boolean allowed = nativeGetAllowed(origin);
-            callback.onReceiveValue(Boolean.valueOf(allowed));
-        } else {
-            Map values = new HashMap<String, Object>();
-            values.put(ORIGIN, origin);
-            values.put(CALLBACK, callback);
-            postMessage(Message.obtain(null, GET_ALLOWED, values));
-        }
-    }
-
-    // This method may be called before the WebKit
-    // thread has intialized the message handler. Messages will be queued until
-    // this time.
-    @Override
-    public void clear(String origin) {
-        // Called on the UI thread.
-        postMessage(Message.obtain(null, CLEAR, origin));
-    }
-
-    // This method may be called before the WebKit
-    // thread has intialized the message handler. Messages will be queued until
-    // this time.
-    @Override
-    public void allow(String origin) {
-        // Called on the UI thread.
-        postMessage(Message.obtain(null, ALLOW, origin));
-    }
-
-    @Override
-    public void clearAll() {
-        // Called on the UI thread.
-        postMessage(Message.obtain(null, CLEAR_ALL));
-    }
-
-    GeolocationPermissionsClassic() {}
-
-    // Native functions, run on the WebKit thread.
-    private static native Set nativeGetOrigins();
-    private static native boolean nativeGetAllowed(String origin);
-    private static native void nativeClear(String origin);
-    private static native void nativeAllow(String origin);
-    private static native void nativeClearAll();
-}
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
deleted file mode 100644
index 225053b..0000000
--- a/core/java/android/webkit/GeolocationService.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2009 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.app.ActivityThread;
-import android.content.Context;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.util.Log;
-import android.webkit.WebViewCore;
-
-
-/**
- * Implements the Java side of GeolocationServiceAndroid.
- */
-final class GeolocationService implements LocationListener {
-
-    // Log tag
-    private static final String TAG = "geolocationService";
-
-    private long mNativeObject;
-    private LocationManager mLocationManager;
-    private boolean mIsGpsEnabled;
-    private boolean mIsRunning;
-    private boolean mIsNetworkProviderAvailable;
-    private boolean mIsGpsProviderAvailable;
-
-    /**
-     * Constructor
-     * @param context The context from which we obtain the system service.
-     * @param nativeObject The native object to which this object will report position updates and
-     *     errors.
-     */
-    public GeolocationService(Context context, long nativeObject) {
-        mNativeObject = nativeObject;
-        // Register newLocationAvailable with platform service.
-        mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
-        if (mLocationManager == null) {
-            Log.e(TAG, "Could not get location manager.");
-        }
-     }
-
-    /**
-     * Start listening for location updates.
-     */
-    public boolean start() {
-        registerForLocationUpdates();
-        mIsRunning = true;
-        return mIsNetworkProviderAvailable || mIsGpsProviderAvailable;
-    }
-
-    /**
-     * Stop listening for location updates.
-     */
-    public void stop() {
-        unregisterFromLocationUpdates();
-        mIsRunning = false;
-    }
-
-    /**
-     * Sets whether to use the GPS.
-     * @param enable Whether to use the GPS.
-     */
-    public void setEnableGps(boolean enable) {
-        if (mIsGpsEnabled != enable) {
-            mIsGpsEnabled = enable;
-            if (mIsRunning) {
-                // There's no way to unregister from a single provider, so we can
-                // only unregister from all, then reregister with all but the GPS.
-                unregisterFromLocationUpdates();
-                registerForLocationUpdates();
-                // Check that the providers are still available after we re-register.
-                maybeReportError("The last location provider is no longer available");
-            }
-        }
-    }
-
-    /**
-     * LocationListener implementation.
-     * Called when the location has changed.
-     * @param location The new location, as a Location object.
-     */
-    public void onLocationChanged(Location location) {
-        // Callbacks from the system location sevice are queued to this thread, so it's possible
-        // that we receive callbacks after unregistering. At this point, the native object will no
-        // longer exist.
-        if (mIsRunning) {
-            nativeNewLocationAvailable(mNativeObject, location);
-        }
-    }
-
-    /**
-     * LocationListener implementation.
-     * Called when the provider status changes.
-     * @param provider The name of the provider.
-     * @param status The new status of the provider.
-     * @param extras an optional Bundle with provider specific data.
-     */
-    public void onStatusChanged(String providerName, int status, Bundle extras) {
-        boolean isAvailable = (status == LocationProvider.AVAILABLE);
-        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
-            mIsNetworkProviderAvailable = isAvailable;
-        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
-            mIsGpsProviderAvailable = isAvailable;
-        }
-        maybeReportError("The last location provider is no longer available");
-    }
-
-    /**
-     * LocationListener implementation.
-     * Called when the provider is enabled.
-     * @param provider The name of the location provider that is now enabled.
-     */
-    public void onProviderEnabled(String providerName) {
-        // No need to notify the native side. It's enough to start sending
-        // valid position fixes again.
-        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
-            mIsNetworkProviderAvailable = true;
-        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
-            mIsGpsProviderAvailable = true;
-        }
-    }
-
-    /**
-     * LocationListener implementation.
-     * Called when the provider is disabled.
-     * @param provider The name of the location provider that is now disabled.
-     */
-    public void onProviderDisabled(String providerName) {
-        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
-            mIsNetworkProviderAvailable = false;
-        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
-            mIsGpsProviderAvailable = false;
-        }
-        maybeReportError("The last location provider was disabled");
-    }
-
-    /**
-     * Registers this object with the location service.
-     */
-    private void registerForLocationUpdates() {
-        try {
-            // Registration may fail if providers are not present on the device.
-            try {
-                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
-                mIsNetworkProviderAvailable = true;
-            } catch(IllegalArgumentException e) { }
-            if (mIsGpsEnabled) {
-                try {
-                    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
-                    mIsGpsProviderAvailable = true;
-                } catch(IllegalArgumentException e) { }
-            }
-        } catch(SecurityException e) {
-            Log.e(TAG, "Caught security exception registering for location updates from system. " +
-                "This should only happen in DumpRenderTree.");
-        }
-    }
-
-    /**
-     * Unregisters this object from the location service.
-     */
-    private void unregisterFromLocationUpdates() {
-        mLocationManager.removeUpdates(this);
-        mIsNetworkProviderAvailable = false;
-        mIsGpsProviderAvailable = false;
-    }
-
-    /**
-     * Reports an error if neither the network nor the GPS provider is available.
-     */
-    private void maybeReportError(String message) {
-        // Callbacks from the system location sevice are queued to this thread, so it's possible
-        // that we receive callbacks after unregistering. At this point, the native object will no
-        // longer exist.
-        if (mIsRunning && !mIsNetworkProviderAvailable && !mIsGpsProviderAvailable) {
-            nativeNewErrorAvailable(mNativeObject, message);
-        }
-    }
-
-    // Native functions
-    private static native void nativeNewLocationAvailable(long nativeObject, Location location);
-    private static native void nativeNewErrorAvailable(long nativeObject, String message);
-}
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
deleted file mode 100644
index 17eb2df..0000000
--- a/core/java/android/webkit/HTML5Audio.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * HTML5 support class for Audio.
- *
- * This class runs almost entirely on the WebCore thread. The exception is when
- * accessing the WebView object to determine whether private browsing is
- * enabled.
- */
-class HTML5Audio extends Handler
-                 implements MediaPlayer.OnBufferingUpdateListener,
-                            MediaPlayer.OnCompletionListener,
-                            MediaPlayer.OnErrorListener,
-                            MediaPlayer.OnPreparedListener,
-                            MediaPlayer.OnSeekCompleteListener,
-                            AudioManager.OnAudioFocusChangeListener {
-    // Logging tag.
-    private static final String LOGTAG = "HTML5Audio";
-
-    private MediaPlayer mMediaPlayer;
-
-    // The C++ MediaPlayerPrivateAndroid object.
-    private int mNativePointer;
-    // The private status of the view that created this player
-    private IsPrivateBrowsingEnabledGetter mIsPrivateBrowsingEnabledGetter;
-
-    private static int IDLE                =  0;
-    private static int INITIALIZED         =  1;
-    private static int PREPARED            =  2;
-    private static int STARTED             =  4;
-    private static int COMPLETE            =  5;
-    private static int PAUSED              =  6;
-    private static int PAUSED_TRANSITORILY =  7;
-    private static int STOPPED             = -2;
-    private static int ERROR               = -1;
-
-    private int mState = IDLE;
-
-    private String mUrl;
-    private boolean mAskToPlay = false;
-    private boolean mLoopEnabled = false;
-    private boolean mProcessingOnEnd = false;
-    private Context mContext;
-
-    // Timer thread -> UI thread
-    private static final int TIMEUPDATE = 100;
-
-    private static final String COOKIE = "Cookie";
-    private static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
-
-    // The spec says the timer should fire every 250 ms or less.
-    private static final int TIMEUPDATE_PERIOD = 250;  // ms
-    // The timer for timeupate events.
-    // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
-    private Timer mTimer;
-    private final class TimeupdateTask extends TimerTask {
-        @Override
-        public void run() {
-            HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
-        }
-    }
-
-    // Helper class to determine whether private browsing is enabled in the
-    // given WebView. Queries the WebView on the UI thread. Calls to get()
-    // block until the data is available.
-    private class IsPrivateBrowsingEnabledGetter {
-        private boolean mIsReady;
-        private boolean mIsPrivateBrowsingEnabled;
-        IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebViewClassic webView) {
-            new Handler(uiThreadLooper).post(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized(IsPrivateBrowsingEnabledGetter.this) {
-                        mIsPrivateBrowsingEnabled = webView.isPrivateBrowsingEnabled();
-                        mIsReady = true;
-                        IsPrivateBrowsingEnabledGetter.this.notify();
-                    }
-                }
-            });
-        }
-        synchronized boolean get() {
-            while (!mIsReady) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                }
-            }
-            return mIsPrivateBrowsingEnabled;
-        }
-    };
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case TIMEUPDATE: {
-                try {
-                    if (mState != ERROR && mMediaPlayer.isPlaying()) {
-                        int position = mMediaPlayer.getCurrentPosition();
-                        nativeOnTimeupdate(position, mNativePointer);
-                    }
-                } catch (IllegalStateException e) {
-                    mState = ERROR;
-                }
-            }
-        }
-    }
-
-    // event listeners for MediaPlayer
-    // Those are called from the same thread we created the MediaPlayer
-    // (i.e. the webviewcore thread here)
-
-    // MediaPlayer.OnBufferingUpdateListener
-    @Override
-    public void onBufferingUpdate(MediaPlayer mp, int percent) {
-        nativeOnBuffering(percent, mNativePointer);
-    }
-
-    // MediaPlayer.OnCompletionListener;
-    @Override
-    public void onCompletion(MediaPlayer mp) {
-        mState = COMPLETE;
-        mProcessingOnEnd = true;
-        nativeOnEnded(mNativePointer);
-        mProcessingOnEnd = false;
-        if (mLoopEnabled == true) {
-            nativeOnRequestPlay(mNativePointer);
-            mLoopEnabled = false;
-        }
-    }
-
-    // MediaPlayer.OnErrorListener
-    @Override
-    public boolean onError(MediaPlayer mp, int what, int extra) {
-        mState = ERROR;
-        resetMediaPlayer();
-        mState = IDLE;
-        return false;
-    }
-
-    // MediaPlayer.OnPreparedListener
-    @Override
-    public void onPrepared(MediaPlayer mp) {
-        mState = PREPARED;
-        if (mTimer != null) {
-            mTimer.schedule(new TimeupdateTask(),
-                            TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
-        }
-        nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer);
-        if (mAskToPlay) {
-            mAskToPlay = false;
-            play();
-        }
-    }
-
-    // MediaPlayer.OnSeekCompleteListener
-    @Override
-    public void onSeekComplete(MediaPlayer mp) {
-        nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
-    }
-
-
-    /**
-     * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
-     */
-    public HTML5Audio(WebViewCore webViewCore, int nativePtr) {
-        // Save the native ptr
-        mNativePointer = nativePtr;
-        resetMediaPlayer();
-        mContext = webViewCore.getContext();
-        mIsPrivateBrowsingEnabledGetter = new IsPrivateBrowsingEnabledGetter(
-                webViewCore.getContext().getMainLooper(), webViewCore.getWebViewClassic());
-    }
-
-    private void resetMediaPlayer() {
-        if (mMediaPlayer == null) {
-            mMediaPlayer = new MediaPlayer();
-        } else {
-            mMediaPlayer.reset();
-        }
-        mMediaPlayer.setOnBufferingUpdateListener(this);
-        mMediaPlayer.setOnCompletionListener(this);
-        mMediaPlayer.setOnErrorListener(this);
-        mMediaPlayer.setOnPreparedListener(this);
-        mMediaPlayer.setOnSeekCompleteListener(this);
-
-        if (mTimer != null) {
-            mTimer.cancel();
-        }
-        mTimer = new Timer();
-        mState = IDLE;
-    }
-
-    private void setDataSource(String url) {
-        mUrl = url;
-        try {
-            if (mState != IDLE) {
-                resetMediaPlayer();
-            }
-            String cookieValue = CookieManager.getInstance().getCookie(
-                    url, mIsPrivateBrowsingEnabledGetter.get());
-            Map<String, String> headers = new HashMap<String, String>();
-
-            if (cookieValue != null) {
-                headers.put(COOKIE, cookieValue);
-            }
-            if (mIsPrivateBrowsingEnabledGetter.get()) {
-                headers.put(HIDE_URL_LOGS, "true");
-            }
-
-            mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers);
-            mState = INITIALIZED;
-            mMediaPlayer.prepareAsync();
-        } catch (IOException e) {
-            String debugUrl = url.length() > 128 ? url.substring(0, 128) + "..." : url;
-            Log.e(LOGTAG, "couldn't load the resource: "+ debugUrl +" exc: " + e);
-            resetMediaPlayer();
-        }
-    }
-
-    @Override
-    public void onAudioFocusChange(int focusChange) {
-        switch (focusChange) {
-        case AudioManager.AUDIOFOCUS_GAIN:
-            // resume playback
-            if (mMediaPlayer == null) {
-                resetMediaPlayer();
-            } else if (mState == PAUSED_TRANSITORILY && !mMediaPlayer.isPlaying()) {
-                mMediaPlayer.start();
-                mState = STARTED;
-            }
-            break;
-
-        case AudioManager.AUDIOFOCUS_LOSS:
-            // Lost focus for an unbounded amount of time: stop playback.
-            if (mState != ERROR && mMediaPlayer.isPlaying()) {
-                mMediaPlayer.stop();
-                mState = STOPPED;
-            }
-            break;
-
-        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
-        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
-            // Lost focus for a short time, but we have to stop
-            // playback.
-            if (mState != ERROR && mMediaPlayer.isPlaying()) {
-                pause(PAUSED_TRANSITORILY);
-            }
-            break;
-        }
-    }
-
-
-    private void play() {
-        if (mState == COMPLETE && mLoopEnabled == true) {
-            // Play it again, Sam
-            mMediaPlayer.start();
-            mState = STARTED;
-            return;
-        }
-
-        if (((mState >= ERROR && mState < PREPARED)) && mUrl != null) {
-            resetMediaPlayer();
-            setDataSource(mUrl);
-            mAskToPlay = true;
-        }
-
-        if (mState >= PREPARED) {
-            AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-            int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
-                AudioManager.AUDIOFOCUS_GAIN);
-
-            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
-                mMediaPlayer.start();
-                mState = STARTED;
-            }
-        }
-    }
-
-    private void pause() {
-        pause(PAUSED);
-    }
-
-    private void pause(int state) {
-        if (mState == STARTED) {
-            if (mTimer != null) {
-                mTimer.purge();
-            }
-            mMediaPlayer.pause();
-            mState = state;
-        }
-    }
-
-    private void seek(int msec) {
-        if (mProcessingOnEnd == true && mState == COMPLETE && msec == 0) {
-            mLoopEnabled = true;
-        }
-        if (mState >= PREPARED) {
-            mMediaPlayer.seekTo(msec);
-        }
-    }
-
-    /**
-     * Called only over JNI when WebKit is happy to
-     * destroy the media player.
-     */
-    private void teardown() {
-        mMediaPlayer.release();
-        mMediaPlayer = null;
-        mState = ERROR;
-        mNativePointer = 0;
-    }
-
-    private float getMaxTimeSeekable() {
-        if (mState >= PREPARED) {
-            return mMediaPlayer.getDuration() / 1000.0f;
-        } else {
-            return 0;
-        }
-    }
-
-    private native void nativeOnBuffering(int percent, int nativePointer);
-    private native void nativeOnEnded(int nativePointer);
-    private native void nativeOnRequestPlay(int nativePointer);
-    private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
-    private native void nativeOnTimeupdate(int position, int nativePointer);
-
-}
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
deleted file mode 100644
index 6fb32c8..0000000
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * 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.media.MediaPlayer;
-import android.media.Metadata;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.MediaController;
-import android.widget.MediaController.MediaPlayerControl;
-
-
-/**
- * @hide This is only used by the browser
- */
-public class HTML5VideoFullScreen extends HTML5VideoView
-    implements MediaPlayerControl, MediaPlayer.OnPreparedListener,
-    View.OnTouchListener {
-
-    // Add this sub-class to handle the resizing when rotating screen.
-    private class VideoSurfaceView extends SurfaceView {
-
-        public VideoSurfaceView(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
-            int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
-            if (mVideoWidth > 0 && mVideoHeight > 0) {
-                if ( mVideoWidth * height  > width * mVideoHeight ) {
-                    height = width * mVideoHeight / mVideoWidth;
-                } else if ( mVideoWidth * height  < width * mVideoHeight ) {
-                    width = height * mVideoWidth / mVideoHeight;
-                }
-            }
-            setMeasuredDimension(width, height);
-        }
-    }
-
-    // This view will contain the video.
-    private VideoSurfaceView mVideoSurfaceView;
-
-    // We need the full screen state to decide which surface to render to and
-    // when to create the MediaPlayer accordingly.
-    static final int FULLSCREEN_OFF               = 0;
-    static final int FULLSCREEN_SURFACECREATING   = 1;
-    static final int FULLSCREEN_SURFACECREATED    = 2;
-
-    private int mFullScreenMode;
-    // The Media Controller only used for full screen mode
-    private MediaController mMediaController;
-
-    // SurfaceHolder for full screen
-    private SurfaceHolder mSurfaceHolder = null;
-
-    // Data only for MediaController
-    private boolean mCanSeekBack;
-    private boolean mCanSeekForward;
-    private boolean mCanPause;
-    private int mCurrentBufferPercentage;
-
-    // The progress view.
-    private static View mProgressView;
-    // The container for the progress view and video view
-    private static FrameLayout mLayout;
-
-    // The video size will be ready when prepared. Used to make sure the aspect
-    // ratio is correct.
-    private int mVideoWidth;
-    private int mVideoHeight;
-    private boolean mPlayingWhenDestroyed = false;
-    SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
-    {
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format,
-                                    int w, int h)
-        {
-            if (mPlayer != null && mMediaController != null
-                    && mCurrentState == STATE_PREPARED) {
-                if (mMediaController.isShowing()) {
-                    // ensure the controller will get repositioned later
-                    mMediaController.hide();
-                }
-                mMediaController.show();
-            }
-        }
-
-        @Override
-        public void surfaceCreated(SurfaceHolder holder)
-        {
-            mSurfaceHolder = holder;
-            mFullScreenMode = FULLSCREEN_SURFACECREATED;
-
-            prepareForFullScreen();
-        }
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder)
-        {
-            mPlayingWhenDestroyed = mPlayer.isPlaying();
-            pauseAndDispatch(mProxy);
-            // We need to set the display to null before switching into inline
-            // mode to avoid error.
-            mPlayer.setDisplay(null);
-            mSurfaceHolder = null;
-            if (mMediaController != null) {
-                mMediaController.hide();
-            }
-        }
-    };
-
-    MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
-        new MediaPlayer.OnVideoSizeChangedListener() {
-            @Override
-            public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
-                mVideoWidth = mp.getVideoWidth();
-                mVideoHeight = mp.getVideoHeight();
-                if (mVideoWidth != 0 && mVideoHeight != 0) {
-                    mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
-                }
-            }
-    };
-
-    private SurfaceView getSurfaceView() {
-        return mVideoSurfaceView;
-    }
-
-    HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare) {
-        mVideoSurfaceView = new VideoSurfaceView(context);
-        mFullScreenMode = FULLSCREEN_OFF;
-        mVideoWidth = 0;
-        mVideoHeight = 0;
-        init(videoLayerId, position, skipPrepare);
-    }
-
-    private void setMediaController(MediaController m) {
-        mMediaController  = m;
-        attachMediaController();
-    }
-
-    private void attachMediaController() {
-        if (mPlayer != null && mMediaController != null) {
-            mMediaController.setMediaPlayer(this);
-            mMediaController.setAnchorView(mVideoSurfaceView);
-            //Will be enabled when prepared
-            mMediaController.setEnabled(false);
-        }
-    }
-
-    @Override
-    public void decideDisplayMode() {
-        mPlayer.setDisplay(mSurfaceHolder);
-    }
-
-    private void prepareForFullScreen() {
-        MediaController mc = new FullScreenMediaController(mProxy.getContext(), mLayout);
-        mc.setSystemUiVisibility(mLayout.getSystemUiVisibility());
-        setMediaController(mc);
-        mPlayer.setScreenOnWhilePlaying(true);
-        mPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
-        prepareDataAndDisplayMode(mProxy);
-    }
-
-
-    private void toggleMediaControlsVisiblity() {
-        if (mMediaController.isShowing()) {
-            mMediaController.hide();
-        } else {
-            mMediaController.show();
-        }
-    }
-
-    @Override
-    public void onPrepared(MediaPlayer mp) {
-        super.onPrepared(mp);
-
-        mVideoSurfaceView.setOnTouchListener(this);
-        // Get the capabilities of the player for this stream
-        Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
-                MediaPlayer.BYPASS_METADATA_FILTER);
-        if (data != null) {
-            mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
-                    || data.getBoolean(Metadata.PAUSE_AVAILABLE);
-            mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
-                    || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
-            mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
-                    || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
-        } else {
-            mCanPause = mCanSeekBack = mCanSeekForward = true;
-        }
-
-        if (getStartWhenPrepared()) {
-            mPlayer.start();
-            // Clear the flag.
-            setStartWhenPrepared(false);
-        }
-
-        // mMediaController status depends on the Metadata result, so put it
-        // after reading the MetaData.
-        // And make sure mPlayer state is updated before showing the controller.
-        if (mMediaController != null) {
-            mMediaController.setEnabled(true);
-            mMediaController.show();
-        }
-
-        if (mProgressView != null) {
-            mProgressView.setVisibility(View.GONE);
-        }
-
-        mVideoWidth = mp.getVideoWidth();
-        mVideoHeight = mp.getVideoHeight();
-        // This will trigger the onMeasure to get the display size right.
-        mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
-
-    }
-
-    @Override
-    public boolean fullScreenExited() {
-        return (mLayout == null);
-    }
-
-    private final WebChromeClient.CustomViewCallback mCallback =
-        new WebChromeClient.CustomViewCallback() {
-            @Override
-            public void onCustomViewHidden() {
-                // It listens to SurfaceHolder.Callback.SurfaceDestroyed event
-                // which happens when the video view is detached from its parent
-                // view. This happens in the WebChromeClient before this method
-                // is invoked.
-                mLayout.removeView(getSurfaceView());
-
-                if (mProgressView != null) {
-                    mLayout.removeView(mProgressView);
-                    mProgressView = null;
-                }
-                mLayout = null;
-                // Re enable plugin views.
-                mProxy.getWebView().getViewManager().showAll();
-                // Don't show the controller after exiting the full screen.
-                mMediaController = null;
-                // Continue the inline mode playing if necessary.
-                mProxy.dispatchOnStopFullScreen(mPlayingWhenDestroyed);
-                mProxy = null;
-            }
-        };
-
-    @Override
-    public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebViewClassic webView) {
-        mFullScreenMode = FULLSCREEN_SURFACECREATING;
-        mCurrentBufferPercentage = 0;
-        mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
-        mProxy = proxy;
-
-        mVideoSurfaceView.getHolder().addCallback(mSHCallback);
-        mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        mVideoSurfaceView.setFocusable(true);
-        mVideoSurfaceView.setFocusableInTouchMode(true);
-        mVideoSurfaceView.requestFocus();
-        mVideoSurfaceView.setOnKeyListener(mProxy);
-        // Create a FrameLayout that will contain the VideoView and the
-        // progress view (if any).
-        mLayout = new FrameLayout(mProxy.getContext());
-        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
-                            ViewGroup.LayoutParams.WRAP_CONTENT,
-                            ViewGroup.LayoutParams.WRAP_CONTENT,
-                            Gravity.CENTER);
-
-        mLayout.addView(getSurfaceView(), layoutParams);
-
-        mLayout.setVisibility(View.VISIBLE);
-        WebChromeClient client = webView.getWebChromeClient();
-        if (client != null) {
-            if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onShowCustomView");
-            client.onShowCustomView(mLayout, mCallback);
-            // Plugins like Flash will draw over the video so hide
-            // them while we're playing.
-            if (webView.getViewManager() != null)
-                webView.getViewManager().hideAll();
-
-            if (DebugFlags.TRACE_CALLBACK) {
-                Log.d(CallbackProxy.LOGTAG, "getVideoLoadingProgressView");
-            }
-            mProgressView = client.getVideoLoadingProgressView();
-            if (mProgressView != null) {
-                mLayout.addView(mProgressView, layoutParams);
-                mProgressView.setVisibility(View.VISIBLE);
-            }
-        }
-    }
-
-    /**
-     * @return true when we are in full screen mode, even the surface not fully
-     * created.
-     */
-    @Override
-    public boolean isFullScreenMode() {
-        return true;
-    }
-
-    // MediaController FUNCTIONS:
-    @Override
-    public boolean canPause() {
-        return mCanPause;
-    }
-
-    @Override
-    public boolean canSeekBackward() {
-        return mCanSeekBack;
-    }
-
-    @Override
-    public boolean canSeekForward() {
-        return mCanSeekForward;
-    }
-
-    @Override
-    public int getBufferPercentage() {
-        if (mPlayer != null) {
-            return mCurrentBufferPercentage;
-        }
-    return 0;
-    }
-
-    @Override
-    public int getAudioSessionId() {
-        if (mPlayer == null) {
-            return 0;
-        }
-        return mPlayer.getAudioSessionId();
-    }
-
-    @Override
-    public void showControllerInFullScreen() {
-        if (mMediaController != null) {
-            mMediaController.show(0);
-        }
-    }
-
-    // Other listeners functions:
-    private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
-        new MediaPlayer.OnBufferingUpdateListener() {
-        @Override
-        public void onBufferingUpdate(MediaPlayer mp, int percent) {
-            mCurrentBufferPercentage = percent;
-        }
-    };
-
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        if (mFullScreenMode >= FULLSCREEN_SURFACECREATED
-                && mMediaController != null) {
-            toggleMediaControlsVisiblity();
-        }
-        return false;
-    }
-
-    @Override
-    protected void switchProgressView(boolean playerBuffering) {
-        if (mProgressView != null) {
-            if (playerBuffering) {
-                mProgressView.setVisibility(View.VISIBLE);
-            } else {
-                mProgressView.setVisibility(View.GONE);
-            }
-        }
-        return;
-    }
-
-    static class FullScreenMediaController extends MediaController {
-
-        View mVideoView;
-
-        public FullScreenMediaController(Context context, View video) {
-            super(context);
-            mVideoView = video;
-        }
-
-        @Override
-        public void show() {
-            super.show();
-            if (mVideoView != null) {
-                mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
-            }
-        }
-
-        @Override
-        public void hide() {
-            if (mVideoView != null) {
-                mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
-                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
-            }
-            super.hide();
-        }
-
-    }
-
-}
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
deleted file mode 100644
index 2ab2ab9..0000000
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.Manifest.permission;
-import android.content.pm.PackageManager;
-import android.graphics.SurfaceTexture;
-import android.webkit.HTML5VideoView;
-import android.webkit.HTML5VideoViewProxy;
-import android.view.Surface;
-import android.opengl.GLES20;
-import android.os.PowerManager;
-
-/**
- * @hide This is only used by the browser
- */
-public class HTML5VideoInline extends HTML5VideoView{
-
-    // Due to the fact that the decoder consume a lot of memory, we make the
-    // surface texture as singleton. But the GL texture (m_textureNames)
-    // associated with the surface texture can be used for showing the screen
-    // shot when paused, so they are not singleton.
-    private static SurfaceTexture mSurfaceTexture = null;
-    private static int[] mTextureNames = null;
-    // Every time when the VideoLayer Id change, we need to recreate the
-    // SurfaceTexture in order to delete the old video's decoder memory.
-    private static int mVideoLayerUsingSurfaceTexture = -1;
-
-    // Video control FUNCTIONS:
-    @Override
-    public void start() {
-        if (!getPauseDuringPreparing()) {
-            super.start();
-        }
-    }
-
-    HTML5VideoInline(int videoLayerId, int position, boolean skipPrepare) {
-        init(videoLayerId, position, skipPrepare);
-    }
-
-    @Override
-    public void decideDisplayMode() {
-        SurfaceTexture surfaceTexture = getSurfaceTexture(getVideoLayerId());
-        Surface surface = new Surface(surfaceTexture);
-        mPlayer.setSurface(surface);
-        surface.release();
-    }
-
-    // Normally called immediately after setVideoURI. But for full screen,
-    // this should be after surface holder created
-    @Override
-    public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
-        super.prepareDataAndDisplayMode(proxy);
-        setFrameAvailableListener(proxy);
-        // TODO: This is a workaround, after b/5375681 fixed, we should switch
-        // to the better way.
-        if (mProxy.getContext().checkCallingOrSelfPermission(permission.WAKE_LOCK)
-                == PackageManager.PERMISSION_GRANTED) {
-            mPlayer.setWakeMode(proxy.getContext(), PowerManager.FULL_WAKE_LOCK);
-        }
-    }
-
-    // Pause the play and update the play/pause button
-    @Override
-    public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
-        super.pauseAndDispatch(proxy);
-    }
-
-    // Inline Video specific FUNCTIONS:
-
-    public static SurfaceTexture getSurfaceTexture(int videoLayerId) {
-        // Create the surface texture.
-        if (videoLayerId != mVideoLayerUsingSurfaceTexture
-            || mSurfaceTexture == null
-            || mTextureNames == null) {
-            // The GL texture will store in the VideoLayerManager at native side.
-            // They will be clean up when requested.
-            // The reason we recreated GL texture name is for screen shot support.
-            mTextureNames = new int[1];
-            GLES20.glGenTextures(1, mTextureNames, 0);
-            mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
-        }
-        mVideoLayerUsingSurfaceTexture = videoLayerId;
-        return mSurfaceTexture;
-    }
-
-    public static boolean surfaceTextureDeleted() {
-        return (mSurfaceTexture == null);
-    }
-
-    @Override
-    public void deleteSurfaceTexture() {
-        cleanupSurfaceTexture();
-        return;
-    }
-
-    public static void cleanupSurfaceTexture() {
-        mSurfaceTexture = null;
-        mVideoLayerUsingSurfaceTexture = -1;
-        return;
-    }
-
-    @Override
-    public int getTextureName() {
-        if (mTextureNames != null) {
-            return mTextureNames[0];
-        } else {
-            return 0;
-        }
-    }
-
-    private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) {
-        if (mSurfaceTexture != null) {
-            mSurfaceTexture.setOnFrameAvailableListener(l);
-        }
-    }
-
-}
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
deleted file mode 100644
index 0e8a5db..0000000
--- a/core/java/android/webkit/HTML5VideoView.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * 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.media.MediaPlayer;
-import android.net.Uri;
-import android.webkit.HTML5VideoViewProxy;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * @hide This is only used by the browser
- */
-public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
-
-    protected static final String LOGTAG = "HTML5VideoView";
-
-    protected static final String COOKIE = "Cookie";
-    protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
-
-    // For handling the seekTo before prepared, we need to know whether or not
-    // the video is prepared. Therefore, we differentiate the state between
-    // prepared and not prepared.
-    // When the video is not prepared, we will have to save the seekTo time,
-    // and use it when prepared to play.
-    // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side.
-    // Please keep them in sync when changed.
-    static final int STATE_INITIALIZED        = 0;
-    static final int STATE_PREPARING          = 1;
-    static final int STATE_PREPARED           = 2;
-    static final int STATE_PLAYING            = 3;
-    static final int STATE_RESETTED           = 4;
-    static final int STATE_RELEASED           = 5;
-
-    protected HTML5VideoViewProxy mProxy;
-
-    // Save the seek time when not prepared. This can happen when switching
-    // video besides initial load.
-    protected int mSaveSeekTime;
-
-    // This is used to find the VideoLayer on the native side.
-    protected int mVideoLayerId;
-
-    // Given the fact we only have one SurfaceTexture, we cannot support multiple
-    // player at the same time. We may recreate a new one and abandon the old
-    // one at transition time.
-    protected static MediaPlayer mPlayer = null;
-    protected static int mCurrentState = -1;
-
-    // We need to save such info.
-    protected Uri mUri;
-    protected Map<String, String> mHeaders;
-
-    // The timer for timeupate events.
-    // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
-    protected static Timer mTimer;
-
-    protected boolean mPauseDuringPreparing;
-
-    // The spec says the timer should fire every 250 ms or less.
-    private static final int TIMEUPDATE_PERIOD = 250;  // ms
-    private boolean mSkipPrepare = false;
-
-    // common Video control FUNCTIONS:
-    public void start() {
-        if (mCurrentState == STATE_PREPARED) {
-            // When replaying the same video, there is no onPrepared call.
-            // Therefore, the timer should be set up here.
-            if (mTimer == null)
-            {
-                mTimer = new Timer();
-                mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD,
-                        TIMEUPDATE_PERIOD);
-            }
-            mPlayer.start();
-            setPlayerBuffering(false);
-        }
-    }
-
-    public void pause() {
-        if (isPlaying()) {
-            mPlayer.pause();
-        } else if (mCurrentState == STATE_PREPARING) {
-            mPauseDuringPreparing = true;
-        }
-        // Delete the Timer to stop it since there is no stop call.
-        if (mTimer != null) {
-            mTimer.purge();
-            mTimer.cancel();
-            mTimer = null;
-        }
-    }
-
-    public int getDuration() {
-        if (mCurrentState == STATE_PREPARED) {
-            return mPlayer.getDuration();
-        } else {
-            return -1;
-        }
-    }
-
-    public int getCurrentPosition() {
-        if (mCurrentState == STATE_PREPARED) {
-            return mPlayer.getCurrentPosition();
-        }
-        return 0;
-    }
-
-    public void seekTo(int pos) {
-        if (mCurrentState == STATE_PREPARED)
-            mPlayer.seekTo(pos);
-        else
-            mSaveSeekTime = pos;
-    }
-
-    public boolean isPlaying() {
-        if (mCurrentState == STATE_PREPARED) {
-            return mPlayer.isPlaying();
-        } else {
-            return false;
-        }
-    }
-
-    public void reset() {
-        if (mCurrentState < STATE_RESETTED) {
-            mPlayer.reset();
-        }
-        mCurrentState = STATE_RESETTED;
-    }
-
-    public void stopPlayback() {
-        if (mCurrentState == STATE_PREPARED) {
-            mPlayer.stop();
-        }
-    }
-
-    public static void release() {
-        if (mPlayer != null && mCurrentState != STATE_RELEASED) {
-            mPlayer.release();
-            mPlayer = null;
-        }
-        mCurrentState = STATE_RELEASED;
-    }
-
-    public boolean isReleased() {
-        return mCurrentState == STATE_RELEASED;
-    }
-
-    public boolean getPauseDuringPreparing() {
-        return mPauseDuringPreparing;
-    }
-
-    // Every time we start a new Video, we create a VideoView and a MediaPlayer
-    public void init(int videoLayerId, int position, boolean skipPrepare) {
-        if (mPlayer == null) {
-            mPlayer = new MediaPlayer();
-            mCurrentState = STATE_INITIALIZED;
-        }
-        mSkipPrepare = skipPrepare;
-        // If we want to skip the prepare, then we keep the state.
-        if (!mSkipPrepare) {
-            mCurrentState = STATE_INITIALIZED;
-        }
-        mProxy = null;
-        mVideoLayerId = videoLayerId;
-        mSaveSeekTime = position;
-        mTimer = null;
-        mPauseDuringPreparing = false;
-    }
-
-    protected HTML5VideoView() {
-    }
-
-    protected static Map<String, String> generateHeaders(String url,
-            HTML5VideoViewProxy proxy) {
-        boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
-        String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
-        Map<String, String> headers = new HashMap<String, String>();
-        if (cookieValue != null) {
-            headers.put(COOKIE, cookieValue);
-        }
-        if (isPrivate) {
-            headers.put(HIDE_URL_LOGS, "true");
-        }
-
-        return headers;
-    }
-
-    public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
-        // When switching players, surface texture will be reused.
-        mUri = Uri.parse(uri);
-        mHeaders = generateHeaders(uri, proxy);
-    }
-
-    // Listeners setup FUNCTIONS:
-    public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
-        mPlayer.setOnCompletionListener(proxy);
-    }
-
-    public void setOnErrorListener(HTML5VideoViewProxy proxy) {
-        mPlayer.setOnErrorListener(proxy);
-    }
-
-    public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
-        mProxy = proxy;
-        mPlayer.setOnPreparedListener(this);
-    }
-
-    public void setOnInfoListener(HTML5VideoViewProxy proxy) {
-        mPlayer.setOnInfoListener(proxy);
-    }
-
-    public void prepareDataCommon(HTML5VideoViewProxy proxy) {
-        if (!mSkipPrepare) {
-            try {
-                mPlayer.reset();
-                mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
-                mPlayer.prepareAsync();
-            } catch (IllegalArgumentException e) {
-                e.printStackTrace();
-            } catch (IllegalStateException e) {
-                e.printStackTrace();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            mCurrentState = STATE_PREPARING;
-        } else {
-            // If we skip prepare and the onPrepared happened in inline mode, we
-            // don't need to call prepare again, we just need to call onPrepared
-            // to refresh the state here.
-            if (mCurrentState >= STATE_PREPARED) {
-                onPrepared(mPlayer);
-            }
-            mSkipPrepare = false;
-        }
-    }
-
-    public void reprepareData(HTML5VideoViewProxy proxy) {
-        mPlayer.reset();
-        prepareDataCommon(proxy);
-    }
-
-    // Normally called immediately after setVideoURI. But for full screen,
-    // this should be after surface holder created
-    public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
-        // SurfaceTexture will be created lazily here for inline mode
-        decideDisplayMode();
-
-        setOnCompletionListener(proxy);
-        setOnPreparedListener(proxy);
-        setOnErrorListener(proxy);
-        setOnInfoListener(proxy);
-
-        prepareDataCommon(proxy);
-    }
-
-
-    // Common code
-    public int getVideoLayerId() {
-        return mVideoLayerId;
-    }
-
-
-    public int getCurrentState() {
-        if (isPlaying()) {
-            return STATE_PLAYING;
-        } else {
-            return mCurrentState;
-        }
-    }
-
-    private static final class TimeupdateTask extends TimerTask {
-        private HTML5VideoViewProxy mProxy;
-
-        public TimeupdateTask(HTML5VideoViewProxy proxy) {
-            mProxy = proxy;
-        }
-
-        @Override
-        public void run() {
-            mProxy.onTimeupdate();
-        }
-    }
-
-    @Override
-    public void onPrepared(MediaPlayer mp) {
-        mCurrentState = STATE_PREPARED;
-        seekTo(mSaveSeekTime);
-        if (mProxy != null) {
-            mProxy.onPrepared(mp);
-        }
-        if (mPauseDuringPreparing) {
-            pauseAndDispatch(mProxy);
-            mPauseDuringPreparing = false;
-        }
-    }
-
-    // Pause the play and update the play/pause button
-    public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
-        pause();
-        if (proxy != null) {
-            proxy.dispatchOnPaused();
-        }
-    }
-
-    // Below are functions that are different implementation on inline and full-
-    // screen mode. Some are specific to one type, but currently are called
-    // directly from the proxy.
-    public void enterFullScreenVideoState(int layerId,
-            HTML5VideoViewProxy proxy, WebViewClassic webView) {
-    }
-
-    public boolean isFullScreenMode() {
-        return false;
-    }
-
-    public void decideDisplayMode() {
-    }
-
-    public boolean getReadyToUseSurfTex() {
-        return false;
-    }
-
-    public void deleteSurfaceTexture() {
-    }
-
-    public int getTextureName() {
-        return 0;
-    }
-
-    // This is true only when the player is buffering and paused
-    public boolean mPlayerBuffering = false;
-
-    public boolean getPlayerBuffering() {
-        return mPlayerBuffering;
-    }
-
-    public void setPlayerBuffering(boolean playerBuffering) {
-        mPlayerBuffering = playerBuffering;
-        switchProgressView(playerBuffering);
-    }
-
-
-    protected void switchProgressView(boolean playerBuffering) {
-        // Only used in HTML5VideoFullScreen
-    }
-
-    public boolean fullScreenExited() {
-        // Only meaningful for HTML5VideoFullScreen
-        return false;
-    }
-
-    private boolean mStartWhenPrepared = false;
-
-    public void setStartWhenPrepared(boolean willPlay) {
-        mStartWhenPrepared  = willPlay;
-    }
-
-    public boolean getStartWhenPrepared() {
-        return mStartWhenPrepared;
-    }
-
-    public void showControllerInFullScreen() {
-    }
-
-}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
deleted file mode 100644
index e8538f6..0000000
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ /dev/null
@@ -1,825 +0,0 @@
-/*
- * Copyright (C) 2009 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.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer;
-import android.net.http.EventHandler;
-import android.net.http.Headers;
-import android.net.http.RequestHandle;
-import android.net.http.RequestQueue;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * <p>Proxy for HTML5 video views.
- */
-class HTML5VideoViewProxy extends Handler
-                          implements MediaPlayer.OnPreparedListener,
-                          MediaPlayer.OnCompletionListener,
-                          MediaPlayer.OnErrorListener,
-                          MediaPlayer.OnInfoListener,
-                          SurfaceTexture.OnFrameAvailableListener,
-                          View.OnKeyListener {
-    // Logging tag.
-    private static final String LOGTAG = "HTML5VideoViewProxy";
-
-    // Message Ids for WebCore thread -> UI thread communication.
-    private static final int PLAY                = 100;
-    private static final int SEEK                = 101;
-    private static final int PAUSE               = 102;
-    private static final int ERROR               = 103;
-    private static final int LOAD_DEFAULT_POSTER = 104;
-    private static final int BUFFERING_START     = 105;
-    private static final int BUFFERING_END       = 106;
-    private static final int ENTER_FULLSCREEN    = 107;
-
-    // Message Ids to be handled on the WebCore thread
-    private static final int PREPARED          = 200;
-    private static final int ENDED             = 201;
-    private static final int POSTER_FETCHED    = 202;
-    private static final int PAUSED            = 203;
-    private static final int STOPFULLSCREEN    = 204;
-    private static final int RESTORESTATE      = 205;
-
-    // Timer thread -> UI thread
-    private static final int TIMEUPDATE = 300;
-
-    // The C++ MediaPlayerPrivateAndroid object.
-    int mNativePointer;
-    // The handler for WebCore thread messages;
-    private Handler mWebCoreHandler;
-    // 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;
-    // The poster downloader.
-    private PosterDownloader mPosterDownloader;
-    // The seek position.
-    private int mSeekPosition;
-    // A helper class to control the playback. This executes on the UI thread!
-    private static final class VideoPlayer {
-        // The proxy that is currently playing (if any).
-        private static HTML5VideoViewProxy mCurrentProxy;
-        // The VideoView instance. This is a singleton for now, at least until
-        // http://b/issue?id=1973663 is fixed.
-        private static HTML5VideoView mHTML5VideoView;
-
-        private static boolean isVideoSelfEnded = false;
-
-        private static void setPlayerBuffering(boolean playerBuffering) {
-            mHTML5VideoView.setPlayerBuffering(playerBuffering);
-        }
-
-        // Every time webView setBaseLayer, this will be called.
-        // When we found the Video layer, then we set the Surface Texture to it.
-        // Otherwise, we may want to delete the Surface Texture to save memory.
-        public static void setBaseLayer(int layer) {
-            // Don't do this for full screen mode.
-            if (mHTML5VideoView != null
-                && !mHTML5VideoView.isFullScreenMode()
-                && !mHTML5VideoView.isReleased()) {
-                int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
-                SurfaceTexture surfTexture =
-                        HTML5VideoInline.getSurfaceTexture(currentVideoLayerId);
-                int textureName = mHTML5VideoView.getTextureName();
-
-                if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
-                    int playerState = mHTML5VideoView.getCurrentState();
-                    if (mHTML5VideoView.getPlayerBuffering())
-                        playerState = HTML5VideoView.STATE_PREPARING;
-                    boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
-                            layer, currentVideoLayerId, textureName,
-                            playerState);
-                    if (playerState >= HTML5VideoView.STATE_PREPARED
-                            && !foundInTree) {
-                        mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
-                    }
-                }
-            }
-        }
-
-        // When a WebView is paused, we also want to pause the video in it.
-        public static void pauseAndDispatch() {
-            if (mHTML5VideoView != null) {
-                mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
-            }
-        }
-
-        public static void enterFullScreenVideo(int layerId, String url,
-                HTML5VideoViewProxy proxy, WebViewClassic webView) {
-                // Save the inline video info and inherit it in the full screen
-                int savePosition = 0;
-                boolean canSkipPrepare = false;
-                boolean forceStart = false;
-                if (mHTML5VideoView != null) {
-                    // We don't allow enter full screen mode while the previous
-                    // full screen video hasn't finished yet.
-                    if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
-                        Log.w(LOGTAG, "Try to reenter the full screen mode");
-                        return;
-                    }
-                    int playerState = mHTML5VideoView.getCurrentState();
-                    // If we are playing the same video, then it is better to
-                    // save the current position.
-                    if (layerId == mHTML5VideoView.getVideoLayerId()) {
-                        savePosition = mHTML5VideoView.getCurrentPosition();
-                        canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING
-                                || playerState == HTML5VideoView.STATE_PREPARED
-                                || playerState == HTML5VideoView.STATE_PLAYING)
-                                && !mHTML5VideoView.isFullScreenMode();
-                    }
-                    if (!canSkipPrepare) {
-                        mHTML5VideoView.reset();
-                    } else {
-                        forceStart = playerState == HTML5VideoView.STATE_PREPARING
-                                || playerState == HTML5VideoView.STATE_PLAYING;
-                    }
-                }
-                mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
-                        layerId, savePosition, canSkipPrepare);
-                mHTML5VideoView.setStartWhenPrepared(forceStart);
-                mCurrentProxy = proxy;
-                mHTML5VideoView.setVideoURI(url, mCurrentProxy);
-                mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
-        }
-
-        public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
-                WebViewClassic webView) {
-            if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
-                WebChromeClient client = webView.getWebChromeClient();
-                if (client != null) {
-                    if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
-                    client.onHideCustomView();
-                }
-            }
-        }
-
-        // This is on the UI thread.
-        // When native tell Java to play, we need to check whether or not it is
-        // still the same video by using videoLayerId and treat it differently.
-        public static void play(String url, int time, HTML5VideoViewProxy proxy,
-                WebChromeClient client, int videoLayerId) {
-            int currentVideoLayerId = -1;
-            boolean backFromFullScreenMode = false;
-            if (mHTML5VideoView != null) {
-                currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
-                backFromFullScreenMode = mHTML5VideoView.fullScreenExited();
-
-                // When playing video back to back in full screen mode,
-                // javascript will switch the src and call play.
-                // In this case, we can just reuse the same full screen view,
-                // and play the video after prepared.
-                if (mHTML5VideoView.isFullScreenMode()
-                    && !backFromFullScreenMode
-                    && currentVideoLayerId != videoLayerId
-                    && mCurrentProxy != proxy) {
-                    mCurrentProxy = proxy;
-                    mHTML5VideoView.setStartWhenPrepared(true);
-                    mHTML5VideoView.setVideoURI(url, proxy);
-                    mHTML5VideoView.reprepareData(proxy);
-                    return;
-                }
-            }
-
-            boolean skipPrepare = false;
-            boolean createInlineView = false;
-            if (backFromFullScreenMode
-                && currentVideoLayerId == videoLayerId
-                && !mHTML5VideoView.isReleased()) {
-                skipPrepare = true;
-                createInlineView = true;
-            } else if(backFromFullScreenMode
-                || currentVideoLayerId != videoLayerId
-                || HTML5VideoInline.surfaceTextureDeleted()) {
-                // Here, we handle the case when switching to a new video,
-                // either inside a WebView or across WebViews
-                // For switching videos within a WebView or across the WebView,
-                // we need to pause the old one and re-create a new media player
-                // inside the HTML5VideoView.
-                if (mHTML5VideoView != null) {
-                    if (!backFromFullScreenMode) {
-                        mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
-                    }
-                    mHTML5VideoView.reset();
-                }
-                createInlineView = true;
-            }
-            if (createInlineView) {
-                mCurrentProxy = proxy;
-                mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, skipPrepare);
-
-                mHTML5VideoView.setVideoURI(url, mCurrentProxy);
-                mHTML5VideoView.prepareDataAndDisplayMode(proxy);
-                return;
-            }
-
-            if (mCurrentProxy == proxy) {
-                // Here, we handle the case when we keep playing with one video
-                if (!mHTML5VideoView.isPlaying()) {
-                    mHTML5VideoView.seekTo(time);
-                    mHTML5VideoView.start();
-                }
-            } else if (mCurrentProxy != null) {
-                // Some other video is already playing. Notify the caller that
-                // its playback ended.
-                proxy.dispatchOnEnded();
-            }
-        }
-
-        public static boolean isPlaying(HTML5VideoViewProxy proxy) {
-            return (mCurrentProxy == proxy && mHTML5VideoView != null
-                    && mHTML5VideoView.isPlaying());
-        }
-
-        public static int getCurrentPosition() {
-            int currentPosMs = 0;
-            if (mHTML5VideoView != null) {
-                currentPosMs = mHTML5VideoView.getCurrentPosition();
-            }
-            return currentPosMs;
-        }
-
-        public static void seek(int time, HTML5VideoViewProxy proxy) {
-            if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) {
-                mHTML5VideoView.seekTo(time);
-            }
-        }
-
-        public static void pause(HTML5VideoViewProxy proxy) {
-            if (mCurrentProxy == proxy && mHTML5VideoView != null) {
-                mHTML5VideoView.pause();
-            }
-        }
-
-        public static void onPrepared() {
-            if (!mHTML5VideoView.isFullScreenMode()) {
-                mHTML5VideoView.start();
-            }
-        }
-
-        public static void end() {
-            mHTML5VideoView.showControllerInFullScreen();
-            if (mCurrentProxy != null) {
-                if (isVideoSelfEnded)
-                    mCurrentProxy.dispatchOnEnded();
-                else
-                    mCurrentProxy.dispatchOnPaused();
-            }
-            isVideoSelfEnded = false;
-        }
-    }
-
-    // A bunch event listeners for our VideoView
-    // MediaPlayer.OnPreparedListener
-    @Override
-    public void onPrepared(MediaPlayer mp) {
-        VideoPlayer.onPrepared();
-        Message msg = Message.obtain(mWebCoreHandler, PREPARED);
-        Map<String, Object> map = new HashMap<String, Object>();
-        map.put("dur", new Integer(mp.getDuration()));
-        map.put("width", new Integer(mp.getVideoWidth()));
-        map.put("height", new Integer(mp.getVideoHeight()));
-        msg.obj = map;
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    // MediaPlayer.OnCompletionListener;
-    @Override
-    public void onCompletion(MediaPlayer mp) {
-        // The video ended by itself, so we need to
-        // send a message to the UI thread to dismiss
-        // the video view and to return to the WebView.
-        // arg1 == 1 means the video ends by itself.
-        sendMessage(obtainMessage(ENDED, 1, 0));
-    }
-
-    // MediaPlayer.OnErrorListener
-    @Override
-    public boolean onError(MediaPlayer mp, int what, int extra) {
-        sendMessage(obtainMessage(ERROR));
-        return false;
-    }
-
-    public void dispatchOnEnded() {
-        Message msg = Message.obtain(mWebCoreHandler, ENDED);
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    public void dispatchOnPaused() {
-        Message msg = Message.obtain(mWebCoreHandler, PAUSED);
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    public void dispatchOnStopFullScreen(boolean stillPlaying) {
-        Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
-        msg.arg1 = stillPlaying ? 1 : 0;
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    public void dispatchOnRestoreState() {
-        Message msg = Message.obtain(mWebCoreHandler, RESTORESTATE);
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    public void onTimeupdate() {
-        sendMessage(obtainMessage(TIMEUPDATE));
-    }
-
-    // When there is a frame ready from surface texture, we should tell WebView
-    // to refresh.
-    @Override
-    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
-        // TODO: This should support partial invalidation too.
-        mWebView.invalidate();
-    }
-
-    // Handler for the messages from WebCore or Timer thread to the UI thread.
-    @Override
-    public void handleMessage(Message msg) {
-        // This executes on the UI thread.
-        switch (msg.what) {
-            case PLAY: {
-                String url = (String) msg.obj;
-                WebChromeClient client = mWebView.getWebChromeClient();
-                int videoLayerID = msg.arg1;
-                if (client != null) {
-                    VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID);
-                }
-                break;
-            }
-            case ENTER_FULLSCREEN:{
-                String url = (String) msg.obj;
-                WebChromeClient client = mWebView.getWebChromeClient();
-                int videoLayerID = msg.arg1;
-                if (client != null) {
-                    VideoPlayer.enterFullScreenVideo(videoLayerID, url, this, mWebView);
-                }
-                break;
-            }
-            case SEEK: {
-                Integer time = (Integer) msg.obj;
-                mSeekPosition = time;
-                VideoPlayer.seek(mSeekPosition, this);
-                break;
-            }
-            case PAUSE: {
-                VideoPlayer.pause(this);
-                break;
-            }
-            case ENDED:
-                if (msg.arg1 == 1)
-                    VideoPlayer.isVideoSelfEnded = true;
-                VideoPlayer.end();
-                break;
-            case ERROR: {
-                WebChromeClient client = mWebView.getWebChromeClient();
-                if (client != null) {
-                    if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
-                    client.onHideCustomView();
-                }
-                break;
-            }
-            case LOAD_DEFAULT_POSTER: {
-                WebChromeClient client = mWebView.getWebChromeClient();
-                if (client != null) {
-                    if (DebugFlags.TRACE_CALLBACK) {
-                        Log.d(CallbackProxy.LOGTAG, "getDefaultVideoPoster");
-                    }
-                    doSetPoster(client.getDefaultVideoPoster());
-                }
-                break;
-            }
-            case TIMEUPDATE: {
-                if (VideoPlayer.isPlaying(this)) {
-                    sendTimeupdate();
-                }
-                break;
-            }
-            case BUFFERING_START: {
-                VideoPlayer.setPlayerBuffering(true);
-                break;
-            }
-            case BUFFERING_END: {
-                VideoPlayer.setPlayerBuffering(false);
-                break;
-            }
-        }
-    }
-
-    // Everything below this comment executes on the WebCore thread, except for
-    // the EventHandler methods, which are called on the network thread.
-
-    // A helper class that knows how to download posters
-    private static final class PosterDownloader implements EventHandler {
-        // The request queue. This is static as we have one queue for all posters.
-        private static RequestQueue mRequestQueue;
-        private static int mQueueRefCount = 0;
-        // The poster URL
-        private URL mUrl;
-        // The proxy we're doing this for.
-        private final HTML5VideoViewProxy mProxy;
-        // The poster bytes. We only touch this on the network thread.
-        private ByteArrayOutputStream mPosterBytes;
-        // The request handle. We only touch this on the WebCore thread.
-        private RequestHandle mRequestHandle;
-        // The response status code.
-        private int mStatusCode;
-        // The response headers.
-        private Headers mHeaders;
-        // The handler to handle messages on the WebCore thread.
-        private Handler mHandler;
-
-        public PosterDownloader(String url, HTML5VideoViewProxy proxy) {
-            try {
-                mUrl = new URL(url);
-            } catch (MalformedURLException e) {
-                mUrl = null;
-            }
-            mProxy = proxy;
-            mHandler = new Handler();
-        }
-        // Start the download. Called on WebCore thread.
-        public void start() {
-            retainQueue();
-
-            if (mUrl == null) {
-                return;
-            }
-
-            // Only support downloading posters over http/https.
-            // FIXME: Add support for other schemes. WebKit seems able to load
-            // posters over other schemes e.g. file://, but gets the dimensions wrong.
-            String protocol = mUrl.getProtocol();
-            if ("http".equals(protocol) || "https".equals(protocol)) {
-                mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null,
-                        this, null, 0);
-            }
-        }
-        // Cancel the download if active and release the queue. Called on WebCore thread.
-        public void cancelAndReleaseQueue() {
-            if (mRequestHandle != null) {
-                mRequestHandle.cancel();
-                mRequestHandle = null;
-            }
-            releaseQueue();
-        }
-        // EventHandler methods. Executed on the network thread.
-        @Override
-        public void status(int major_version,
-                int minor_version,
-                int code,
-                String reason_phrase) {
-            mStatusCode = code;
-        }
-
-        @Override
-        public void headers(Headers headers) {
-            mHeaders = headers;
-        }
-
-        @Override
-        public void data(byte[] data, int len) {
-            if (mPosterBytes == null) {
-                mPosterBytes = new ByteArrayOutputStream();
-            }
-            mPosterBytes.write(data, 0, len);
-        }
-
-        @Override
-        public void endData() {
-            if (mStatusCode == 200) {
-                if (mPosterBytes.size() > 0) {
-                    Bitmap poster = BitmapFactory.decodeByteArray(
-                            mPosterBytes.toByteArray(), 0, mPosterBytes.size());
-                    mProxy.doSetPoster(poster);
-                }
-                cleanup();
-            } else if (mStatusCode >= 300 && mStatusCode < 400) {
-                // We have a redirect.
-                try {
-                    mUrl = new URL(mHeaders.getLocation());
-                } catch (MalformedURLException e) {
-                    mUrl = null;
-                }
-                if (mUrl != null) {
-                    mHandler.post(new Runnable() {
-                       @Override
-                       public void run() {
-                           if (mRequestHandle != null) {
-                               mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode,
-                                       new HashMap<String, String>());
-                           }
-                       }
-                    });
-                }
-            }
-        }
-
-        @Override
-        public void certificate(SslCertificate certificate) {
-            // Don't care.
-        }
-
-        @Override
-        public void error(int id, String description) {
-            cleanup();
-        }
-
-        @Override
-        public boolean handleSslErrorRequest(SslError error) {
-            // Don't care. If this happens, data() will never be called so
-            // mPosterBytes will never be created, so no need to call cleanup.
-            return false;
-        }
-        // Tears down the poster bytes stream. Called on network thread.
-        private void cleanup() {
-            if (mPosterBytes != null) {
-                try {
-                    mPosterBytes.close();
-                } catch (IOException ignored) {
-                    // Ignored.
-                } finally {
-                    mPosterBytes = null;
-                }
-            }
-        }
-
-        // Queue management methods. Called on WebCore thread.
-        private void retainQueue() {
-            if (mRequestQueue == null) {
-                mRequestQueue = new RequestQueue(mProxy.getContext());
-            }
-            mQueueRefCount++;
-        }
-
-        private void releaseQueue() {
-            if (mQueueRefCount == 0) {
-                return;
-            }
-            if (--mQueueRefCount == 0) {
-                mRequestQueue.shutdown();
-                mRequestQueue = null;
-            }
-        }
-    }
-
-    /**
-     * Private constructor.
-     * @param webView is the WebView that hosts the video.
-     * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
-     */
-    private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) {
-        // This handler is for the main (UI) thread.
-        super(Looper.getMainLooper());
-        // Save the WebView object.
-        mWebView = webView;
-        // Pass Proxy into webview, such that every time we have a setBaseLayer
-        // call, we tell this Proxy to call the native to update the layer tree
-        // for the Video Layer's surface texture info
-        mWebView.setHTML5VideoViewProxy(this);
-        // Save the native ptr
-        mNativePointer = nativePtr;
-        // create the message handler for this thread
-        createWebCoreHandler();
-    }
-
-    private void createWebCoreHandler() {
-        mWebCoreHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case PREPARED: {
-                        Map<String, Object> map = (Map<String, Object>) msg.obj;
-                        Integer duration = (Integer) map.get("dur");
-                        Integer width = (Integer) map.get("width");
-                        Integer height = (Integer) map.get("height");
-                        nativeOnPrepared(duration.intValue(), width.intValue(),
-                                height.intValue(), mNativePointer);
-                        break;
-                    }
-                    case ENDED:
-                        mSeekPosition = 0;
-                        nativeOnEnded(mNativePointer);
-                        break;
-                    case PAUSED:
-                        nativeOnPaused(mNativePointer);
-                        break;
-                    case POSTER_FETCHED:
-                        Bitmap poster = (Bitmap) msg.obj;
-                        nativeOnPosterFetched(poster, mNativePointer);
-                        break;
-                    case TIMEUPDATE:
-                        nativeOnTimeupdate(msg.arg1, mNativePointer);
-                        break;
-                    case STOPFULLSCREEN:
-                        nativeOnStopFullscreen(msg.arg1, mNativePointer);
-                        break;
-                    case RESTORESTATE:
-                        nativeOnRestoreState(mNativePointer);
-                        break;
-                }
-            }
-        };
-    }
-
-    private void doSetPoster(Bitmap poster) {
-        if (poster == null) {
-            return;
-        }
-        // Save a ref to the bitmap and send it over to the WebCore thread.
-        mPoster = poster;
-        Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED);
-        msg.obj = poster;
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    private void sendTimeupdate() {
-        Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE);
-        msg.arg1 = VideoPlayer.getCurrentPosition();
-        mWebCoreHandler.sendMessage(msg);
-    }
-
-    public Context getContext() {
-        return mWebView.getContext();
-    }
-
-    // The public methods below are all called from WebKit only.
-    /**
-     * Play a video stream.
-     * @param url is the URL of the video stream.
-     */
-    public void play(String url, int position, int videoLayerID) {
-        if (url == null) {
-            return;
-        }
-
-        if (position > 0) {
-            seek(position);
-        }
-        Message message = obtainMessage(PLAY);
-        message.arg1 = videoLayerID;
-        message.obj = url;
-        sendMessage(message);
-    }
-
-    /**
-     * Play a video stream in full screen mode.
-     * @param url is the URL of the video stream.
-     */
-    public void enterFullscreenForVideoLayer(String url, int videoLayerID) {
-        if (url == null) {
-            return;
-        }
-
-        Message message = obtainMessage(ENTER_FULLSCREEN);
-        message.arg1 = videoLayerID;
-        message.obj = url;
-        sendMessage(message);
-    }
-
-    /**
-     * Seek into the video stream.
-     * @param  time is the position in the video stream.
-     */
-    public void seek(int time) {
-        Message message = obtainMessage(SEEK);
-        message.obj = new Integer(time);
-        sendMessage(message);
-    }
-
-    /**
-     * Pause the playback.
-     */
-    public void pause() {
-        Message message = obtainMessage(PAUSE);
-        sendMessage(message);
-    }
-
-    /**
-     * Tear down this proxy object.
-     */
-    public void teardown() {
-        // This is called by the C++ MediaPlayerPrivate dtor.
-        // Cancel any active poster download.
-        if (mPosterDownloader != null) {
-            mPosterDownloader.cancelAndReleaseQueue();
-        }
-        mNativePointer = 0;
-    }
-
-    /**
-     * Load the poster image.
-     * @param url is the URL of the poster image.
-     */
-    public void loadPoster(String url) {
-        if (url == null) {
-            Message message = obtainMessage(LOAD_DEFAULT_POSTER);
-            sendMessage(message);
-            return;
-        }
-        // Cancel any active poster download.
-        if (mPosterDownloader != null) {
-            mPosterDownloader.cancelAndReleaseQueue();
-        }
-        // Load the poster asynchronously
-        mPosterDownloader = new PosterDownloader(url, this);
-        mPosterDownloader.start();
-    }
-
-    // These three function are called from UI thread only by WebView.
-    public void setBaseLayer(int layer) {
-        VideoPlayer.setBaseLayer(layer);
-    }
-
-    public void pauseAndDispatch() {
-        VideoPlayer.pauseAndDispatch();
-    }
-
-    public void enterFullScreenVideo(int layerId, String url) {
-        VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
-    }
-
-    public void exitFullScreenVideo() {
-        VideoPlayer.exitFullScreenVideo(this, mWebView);
-    }
-
-    /**
-     * The factory for HTML5VideoViewProxy instances.
-     * @param webViewCore is the WebViewCore that is requesting the proxy.
-     *
-     * @return a new HTML5VideoViewProxy object.
-     */
-    public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) {
-        return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr);
-    }
-
-    /* package */ WebViewClassic getWebView() {
-        return mWebView;
-    }
-
-    private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
-    private native void nativeOnEnded(int nativePointer);
-    private native void nativeOnPaused(int nativePointer);
-    private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
-    private native void nativeOnTimeupdate(int position, int nativePointer);
-    private native void nativeOnStopFullscreen(int stillPlaying, int nativePointer);
-    private native void nativeOnRestoreState(int nativePointer);
-    private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
-            int baseLayer, int videoLayerId, int textureName,
-            int playerState);
-
-    @Override
-    public boolean onInfo(MediaPlayer mp, int what, int extra) {
-        if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
-            sendMessage(obtainMessage(BUFFERING_START, what, extra));
-        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
-            sendMessage(obtainMessage(BUFFERING_END, what, extra));
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                return true;
-            } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) {
-                VideoPlayer.exitFullScreenVideo(this, mWebView);
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
deleted file mode 100644
index e6eaa14..0000000
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.net.ProxyProperties;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Set;
-
-final class JWebCoreJavaBridge extends Handler {
-    // Identifier for the timer message.
-    private static final int TIMER_MESSAGE = 1;
-    // ID for servicing functionptr queue
-    private static final int FUNCPTR_MESSAGE = 2;
-    // Log system identifier.
-    private static final String LOGTAG = "webkit-timers";
-
-    // Native object pointer for interacting in native code.
-    private int mNativeBridge;
-    // Instant timer is used to implement a timer that needs to fire almost
-    // immediately.
-    private boolean mHasInstantTimer;
-
-    private boolean mTimerPaused;
-    private boolean mHasDeferredTimers;
-
-    // keep track of the main WebViewClassic attached to the current window so that we
-    // can get the proper Context.
-    private static WeakReference<WebViewClassic> sCurrentMainWebView =
-            new WeakReference<WebViewClassic>(null);
-
-    /* package */
-    static final int REFRESH_PLUGINS = 100;
-
-    private HashMap<String, String> mContentUriToFilePathMap;
-
-    /**
-     * Construct a new JWebCoreJavaBridge to interface with
-     * WebCore timers and cookies.
-     */
-    public JWebCoreJavaBridge() {
-        nativeConstructor();
-
-    }
-
-    @Override
-    protected void finalize() {
-        nativeFinalize();
-    }
-
-    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<WebViewClassic>(webview);
-    }
-
-    static synchronized void removeActiveWebView(WebViewClassic webview) {
-        if (sCurrentMainWebView.get() != webview) {
-            // it is possible if there is a sub-WebView. Do nothing.
-            return;
-        }
-        sCurrentMainWebView.clear();
-    }
-
-    /**
-     * Call native timer callbacks.
-     */
-    private void fireSharedTimer() { 
-        // clear the flag so that sharedTimerFired() can set a new timer
-        mHasInstantTimer = false;
-        sharedTimerFired();
-    }
-
-    /**
-     * handleMessage
-     * @param msg The dispatched message.
-     *
-     * The only accepted message currently is TIMER_MESSAGE
-     */
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case TIMER_MESSAGE: {
-                if (mTimerPaused) {
-                    mHasDeferredTimers = true;
-                } else {
-                    fireSharedTimer();
-                }
-                break;
-            }
-            case FUNCPTR_MESSAGE:
-                nativeServiceFuncPtrQueue();
-                break;
-            case REFRESH_PLUGINS:
-                nativeUpdatePluginDirectories(PluginManager.getInstance(null)
-                        .getPluginDirectories(), ((Boolean) msg.obj)
-                        .booleanValue());
-                break;
-        }
-    }
-    
-    // called from JNI side
-    private void signalServiceFuncPtrQueue() {
-        Message msg = obtainMessage(FUNCPTR_MESSAGE);
-        sendMessage(msg);
-    }
-    
-    private native void nativeServiceFuncPtrQueue();
-
-    /**
-     * Pause all timers.
-     */
-    public void pause() {
-        if (!mTimerPaused) {
-            mTimerPaused = true;
-            mHasDeferredTimers = false;
-        }
-    }
-
-    /**
-     * Resume all timers.
-     */
-    public void resume() {
-        if (mTimerPaused) {
-           mTimerPaused = false;
-           if (mHasDeferredTimers) {
-               mHasDeferredTimers = false;
-               fireSharedTimer();
-           }
-        }
-    }
-
-    /**
-     * Set WebCore cache size.
-     * @param bytes The cache size in bytes.
-     */
-    public native void setCacheSize(int bytes);
-
-    /**
-     * Store a cookie string associated with a url.
-     * @param url The url to be used as a key for the cookie.
-     * @param value The cookie string to be stored.
-     */
-    private void setCookies(String url, String value) {
-        if (value.contains("\r") || value.contains("\n")) {
-            // for security reason, filter out '\r' and '\n' from the cookie
-            int size = value.length();
-            StringBuilder buffer = new StringBuilder(size);
-            int i = 0;
-            while (i != -1 && i < size) {
-                int ir = value.indexOf('\r', i);
-                int in = value.indexOf('\n', i);
-                int newi = (ir == -1) ? in : (in == -1 ? ir : (ir < in ? ir
-                        : in));
-                if (newi > i) {
-                    buffer.append(value.subSequence(i, newi));
-                } else if (newi == -1) {
-                    buffer.append(value.subSequence(i, size));
-                    break;
-                }
-                i = newi + 1;
-            }
-            value = buffer.toString();
-        }
-        CookieManager.getInstance().setCookie(url, value);
-    }
-
-    /**
-     * Retrieve the cookie string for the given url.
-     * @param url The resource's url.
-     * @return A String representing the cookies for the given resource url.
-     */
-    private String cookies(String url) {
-        return CookieManager.getInstance().getCookie(url);
-    }
-
-    /**
-     * Returns whether cookies are enabled or not.
-     */
-    private boolean cookiesEnabled() {
-        return CookieManager.getInstance().acceptCookie();
-    }
-
-    /**
-     * Returns an array of plugin directoies
-     */
-    private String[] getPluginDirectories() {
-        return PluginManager.getInstance(null).getPluginDirectories();
-    }
-
-    /**
-     * Returns the path of the plugin data directory
-     */
-    private String getPluginSharedDataDirectory() {
-        return PluginManager.getInstance(null).getPluginSharedDataDirectory();
-    }
-
-    /**
-     * setSharedTimer
-     * @param timemillis The relative time when the timer should fire
-     */
-    private void setSharedTimer(long timemillis) {
-        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
-
-        if (timemillis <= 0) {
-            // we don't accumulate the sharedTimer unless it is a delayed
-            // request. This way we won't flood the message queue with
-            // WebKit messages. This should improve the browser's
-            // responsiveness to key events.
-            if (mHasInstantTimer) {
-                return;
-            } else {
-                mHasInstantTimer = true;
-                Message msg = obtainMessage(TIMER_MESSAGE);
-                sendMessageDelayed(msg, timemillis);
-            }
-        } else {
-            Message msg = obtainMessage(TIMER_MESSAGE);
-            sendMessageDelayed(msg, timemillis);
-        }
-    }
-
-    /**
-     * Stop the shared timer.
-     */
-    private void stopSharedTimer() {
-        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
-            Log.v(LOGTAG, "stopSharedTimer removing all timers");
-        }
-        removeMessages(TIMER_MESSAGE);
-        mHasInstantTimer = false;
-        mHasDeferredTimers = false;
-    }
-
-    private String[] getKeyStrengthList() {
-        return CertTool.getKeyStrengthList();
-    }
-
-    synchronized private String getSignedPublicKey(int index, String challenge,
-            String url) {
-        WebViewClassic current = sCurrentMainWebView.get();
-        if (current != null) {
-            // generateKeyPair expects organizations which we don't have. Ignore
-            // url.
-            return CertTool.getSignedPublicKey(
-                    current.getContext(), index, challenge);
-        } else {
-            Log.e(LOGTAG, "There is no active WebView for getSignedPublicKey");
-            return "";
-        }
-    }
-
-    // Called on the WebCore thread through JNI.
-    private String resolveFilePathForContentUri(String uri) {
-        if (mContentUriToFilePathMap != null) {
-            String fileName = mContentUriToFilePathMap.get(uri);
-            if (fileName != null) {
-                return fileName;
-            }
-        }
-
-        // Failsafe fallback to just use the last path segment.
-        // (See OpenableColumns documentation in the SDK)
-        Uri jUri = Uri.parse(uri);
-        return jUri.getLastPathSegment();
-    }
-
-    public void storeFilePathForContentUri(String path, String contentUri) {
-        if (mContentUriToFilePathMap == null) {
-            mContentUriToFilePathMap = new HashMap<String, String>();
-        }
-        mContentUriToFilePathMap.put(contentUri, path);
-    }
-
-    public void updateProxy(ProxyProperties proxyProperties) {
-        if (proxyProperties == null) {
-            nativeUpdateProxy("", "");
-            return;
-        }
-
-        String host = proxyProperties.getHost();
-        int port = proxyProperties.getPort();
-        if (port != 0)
-            host += ":" + port;
-
-        nativeUpdateProxy(host, proxyProperties.getExclusionList());
-    }
-
-    private native void nativeConstructor();
-    private native void nativeFinalize();
-    private native void sharedTimerFired();
-    private native void nativeUpdatePluginDirectories(String[] directories,
-            boolean reload);
-    public native void setNetworkOnLine(boolean online);
-    public native void setNetworkType(String type, String subtype);
-    public native void addPackageNames(Set<String> packageNames);
-    public native void addPackageName(String packageName);
-    public native void removePackageName(String packageName);
-    public native void nativeUpdateProxy(String newProxy, String exclusionList);
-}
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
deleted file mode 100644
index 01a81c4..0000000
--- a/core/java/android/webkit/JniUtil.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.net.Uri;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.io.File;
-import java.io.InputStream;
-
-class JniUtil {
-
-    static {
-        System.loadLibrary("webcore");
-        System.loadLibrary("chromium_net");
-    }
-    private static final String LOGTAG = "webkit";
-    private JniUtil() {} // Utility class, do not instantiate.
-
-    // Used by the Chromium HTTP stack.
-    private static String sDatabaseDirectory;
-    private static String sCacheDirectory;
-    private static Context sContext;
-
-    private static void checkInitialized() {
-        if (sContext == null) {
-            throw new IllegalStateException("Call CookieSyncManager::createInstance() or create a webview before using this class");
-        }
-    }
-
-    protected static synchronized void setContext(Context context) {
-        if (sContext != null) {
-            return;
-        }
-
-        sContext = context.getApplicationContext();
-    }
-
-    protected static synchronized Context getContext() {
-        return sContext;
-    }
-
-    /**
-     * Called by JNI. Gets the application's database directory, excluding the trailing slash.
-     * @return String The application's database directory
-     */
-    private static synchronized String getDatabaseDirectory() {
-        checkInitialized();
-
-        if (sDatabaseDirectory == null) {
-            sDatabaseDirectory = sContext.getDatabasePath("dummy").getParent();
-        }
-
-        return sDatabaseDirectory;
-    }
-
-    /**
-     * Called by JNI. Gets the application's cache directory, excluding the trailing slash.
-     * @return String The application's cache directory
-     */
-    private static synchronized String getCacheDirectory() {
-        checkInitialized();
-
-        if (sCacheDirectory == null) {
-            File cacheDir = sContext.getCacheDir();
-            if (cacheDir == null) {
-                sCacheDirectory = "";
-            } else {
-                sCacheDirectory = cacheDir.getAbsolutePath();
-            }
-        }
-
-        return sCacheDirectory;
-    }
-
-    /**
-     * Called by JNI. Gets the application's package name.
-     * @return String The application's package name
-     */
-    private static synchronized String getPackageName() {
-        checkInitialized();
-
-        return sContext.getPackageName();
-    }
-
-    private static final String ANDROID_CONTENT = URLUtil.CONTENT_BASE;
-
-    /**
-     * Called by JNI. Calculates the size of an input stream by reading it.
-     * @return long The size of the stream
-     */
-    private static synchronized long contentUrlSize(String url) {
-        // content://
-        if (url.startsWith(ANDROID_CONTENT)) {
-            try {
-                // Strip off MIME type. If we don't do this, we can fail to
-                // load Gmail attachments, because the URL being loaded doesn't
-                // exactly match the URL we have permission to read.
-                int mimeIndex = url.lastIndexOf('?');
-                if (mimeIndex != -1) {
-                    url = url.substring(0, mimeIndex);
-                }
-                Uri uri = Uri.parse(url);
-                InputStream is = sContext.getContentResolver().openInputStream(uri);
-                byte[] buffer = new byte[1024];
-                int n;
-                long size = 0;
-                try {
-                    while ((n = is.read(buffer)) != -1) {
-                        size += n;
-                    }
-                } finally {
-                    is.close();
-                }
-                return size;
-            } catch (Exception e) {
-                Log.e(LOGTAG, "Exception: " + url);
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Called by JNI.
-     *
-     * @return  Opened input stream to content
-     * TODO: Make all content loading use this instead of BrowserFrame.java
-     */
-    private static synchronized InputStream contentUrlStream(String url) {
-        // content://
-        if (url.startsWith(ANDROID_CONTENT)) {
-            try {
-                // Strip off mimetype, for compatibility with ContentLoader.java
-                // (used with Android HTTP stack, now removed).
-                // If we don't do this, we can fail to load Gmail attachments,
-                // because the URL being loaded doesn't exactly match the URL we
-                // have permission to read.
-                int mimeIndex = url.lastIndexOf('?');
-                if (mimeIndex != -1) {
-                    url = url.substring(0, mimeIndex);
-                }
-                Uri uri = Uri.parse(url);
-                return sContext.getContentResolver().openInputStream(uri);
-            } catch (Exception e) {
-                Log.e(LOGTAG, "Exception: " + url);
-                return null;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    private static synchronized String getAutofillQueryUrl() {
-        checkInitialized();
-        // If the device has not checked in it won't have pulled down the system setting for the
-        // Autofill Url. In that case we will not make autofill server requests.
-        return Settings.Global.getString(sContext.getContentResolver(),
-                Settings.Global.WEB_AUTOFILL_QUERY_URL);
-    }
-
-    private static boolean canSatisfyMemoryAllocation(long bytesRequested) {
-        checkInitialized();
-        ActivityManager manager = (ActivityManager) sContext.getSystemService(
-                Context.ACTIVITY_SERVICE);
-        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
-        manager.getMemoryInfo(memInfo);
-        long leftToAllocate = memInfo.availMem - memInfo.threshold;
-        return !memInfo.lowMemory && bytesRequested < leftToAllocate;
-    }
-}
diff --git a/core/java/android/webkit/KeyStoreHandler.java b/core/java/android/webkit/KeyStoreHandler.java
deleted file mode 100644
index 849007e..0000000
--- a/core/java/android/webkit/KeyStoreHandler.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2011 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.os.Handler;
-import android.util.Log;
-
-/**
- * KeyStoreHandler: class responsible for certificate installation to
- * the system key store. It reads the certificates file from network
- * then pass the bytes to class CertTool.
- * This class is only needed if the Chromium HTTP stack is used.
- */
-class KeyStoreHandler extends Handler {
-    private static final String LOGTAG = "KeyStoreHandler";
-
-    private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
-
-    private String mMimeType;
-
-    public KeyStoreHandler(String mimeType) {
-      mMimeType = mimeType;
-    }
-
-    /**
-     * Add data to the internal collection of data.
-     * @param data A byte array containing the content.
-     * @param length The length of data.
-     */
-    public void didReceiveData(byte[] data, int length) {
-        synchronized (mDataBuilder) {
-            mDataBuilder.append(data, 0, length);
-        }
-    }
-
-    public void installCert(Context context) {
-        String type = CertTool.getCertType(mMimeType);
-        if (type == null) return;
-
-        // This must be synchronized so that no more data can be added
-        // after getByteSize returns.
-        synchronized (mDataBuilder) {
-            // In the case of downloading certificate, we will save it
-            // to the KeyStore and stop the current loading so that it
-            // will not generate a new history page
-            byte[] cert = new byte[mDataBuilder.getByteSize()];
-            int offset = 0;
-            while (true) {
-                ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
-                if (c == null) break;
-
-                if (c.mLength != 0) {
-                    System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
-                    offset += c.mLength;
-                }
-                c.release();
-            }
-            CertTool.addCertificate(context, type, cert);
-            return;
-        }
-    }
-}
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
deleted file mode 100644
index a1c6a53..0000000
--- a/core/java/android/webkit/L10nUtils.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-
-import java.lang.ref.SoftReference;
-import java.util.Map;
-import java.util.HashMap;
-
-/**
- * @hide
- */
-public class L10nUtils {
-
-    // These array elements must be kept in sync with those defined in
-    // external/chromium/android/app/l10n_utils.h
-    private static int[] mIdsArray = {
-        com.android.internal.R.string.autofill_address_name_separator,      // IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR
-        com.android.internal.R.string.autofill_address_summary_name_format, // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT
-        com.android.internal.R.string.autofill_address_summary_separator,   // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR
-        com.android.internal.R.string.autofill_address_summary_format,      // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT
-        com.android.internal.R.string.autofill_attention_ignored_re,        // IDS_AUTOFILL_ATTENTION_IGNORED_RE
-        com.android.internal.R.string.autofill_region_ignored_re,           // IDS_AUTOFILL_REGION_IGNORED_RE
-        com.android.internal.R.string.autofill_company_re,                  // IDS_AUTOFILL_COMPANY_RE
-        com.android.internal.R.string.autofill_address_line_1_re,           // IDS_AUTOFILL_ADDRESS_LINE_1_RE
-        com.android.internal.R.string.autofill_address_line_1_label_re,     // IDS_AUTOFILL_ADDRESS_LINE_1_LABEL_RE
-        com.android.internal.R.string.autofill_address_line_2_re,           // IDS_AUTOFILL_ADDRESS_LINE_2_RE
-        com.android.internal.R.string.autofill_address_line_3_re,           // IDS_AUTOFILL_ADDRESS_LINE_3_RE
-        com.android.internal.R.string.autofill_country_re,                  // IDS_AUTOFILL_COUNTRY_RE
-        com.android.internal.R.string.autofill_zip_code_re,                 // IDS_AUTOFILL_ZIP_CODE_RE
-        com.android.internal.R.string.autofill_zip_4_re,                    // IDS_AUTOFILL_ZIP_4_RE
-        com.android.internal.R.string.autofill_city_re,                     // IDS_AUTOFILL_CITY_RE
-        com.android.internal.R.string.autofill_state_re,                    // IDS_AUTOFILL_STATE_RE
-        com.android.internal.R.string.autofill_address_type_same_as_re,     // IDS_AUTOFILL_SAME_AS_RE
-        com.android.internal.R.string.autofill_address_type_use_my_re,      // IDS_AUTOFILL_USE_MY_RE
-        com.android.internal.R.string.autofill_billing_designator_re,       // IDS_AUTOFILL_BILLING_DESIGNATOR_RE
-        com.android.internal.R.string.autofill_shipping_designator_re,      // IDS_AUTOFILL_SHIPPING_DESIGNATOR_RE
-        com.android.internal.R.string.autofill_email_re,                    // IDS_AUTOFILL_EMAIL_RE
-        com.android.internal.R.string.autofill_username_re,                 // IDS_AUTOFILL_USERNAME_RE
-        com.android.internal.R.string.autofill_name_re,                     // IDS_AUTOFILL_NAME_RE
-        com.android.internal.R.string.autofill_name_specific_re,            // IDS_AUTOFILL_NAME_SPECIFIC_RE
-        com.android.internal.R.string.autofill_first_name_re,               // IDS_AUTOFILL_FIRST_NAME_RE
-        com.android.internal.R.string.autofill_middle_initial_re,           // IDS_AUTOFILL_MIDDLE_INITIAL_RE
-        com.android.internal.R.string.autofill_middle_name_re,              // IDS_AUTOFILL_MIDDLE_NAME_RE
-        com.android.internal.R.string.autofill_last_name_re,                // IDS_AUTOFILL_LAST_NAME_RE
-        com.android.internal.R.string.autofill_phone_re,                    // IDS_AUTOFILL_PHONE_RE
-        com.android.internal.R.string.autofill_area_code_re,                // IDS_AUTOFILL_AREA_CODE_RE
-        com.android.internal.R.string.autofill_phone_prefix_re,             // IDS_AUTOFILL_PHONE_PREFIX_RE
-        com.android.internal.R.string.autofill_phone_suffix_re,             // IDS_AUTOFILL_PHONE_SUFFIX_RE
-        com.android.internal.R.string.autofill_phone_extension_re,          // IDS_AUTOFILL_PHONE_EXTENSION_RE
-        com.android.internal.R.string.autofill_name_on_card_re,             // IDS_AUTOFILL_NAME_ON_CARD_RE
-        com.android.internal.R.string.autofill_name_on_card_contextual_re,  // IDS_AUTOFILL_NAME_ON_CARD_CONTEXTUAL_RE
-        com.android.internal.R.string.autofill_card_cvc_re,                 // IDS_AUTOFILL_CARD_CVC_RE
-        com.android.internal.R.string.autofill_card_number_re,              // IDS_AUTOFILL_CARD_NUMBER_RE
-        com.android.internal.R.string.autofill_expiration_month_re,         // IDS_AUTOFILL_EXPIRATION_MONTH_RE
-        com.android.internal.R.string.autofill_expiration_date_re,          // IDS_AUTOFILL_EXPIRATION_DATE_RE
-        com.android.internal.R.string.autofill_card_ignored_re,             // IDS_AUTOFILL_CARD_IGNORED_RE
-        com.android.internal.R.string.autofill_fax_re,                      // IDS_AUTOFILL_FAX_RE
-        com.android.internal.R.string.autofill_country_code_re,             // IDS_AUTOFILL_COUNTRY_CODE_RE
-        com.android.internal.R.string.autofill_area_code_notext_re,         // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE
-        com.android.internal.R.string.autofill_phone_prefix_separator_re,   // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE
-        com.android.internal.R.string.autofill_phone_suffix_separator_re,   // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE
-        com.android.internal.R.string.autofill_province,                    // IDS_AUTOFILL_DIALOG_PROVINCE
-        com.android.internal.R.string.autofill_postal_code,                 // IDS_AUTOFILL_DIALOG_POSTAL_CODE
-        com.android.internal.R.string.autofill_state,                       // IDS_AUTOFILL_DIALOG_STATE
-        com.android.internal.R.string.autofill_zip_code,                    // IDS_AUTOFILL_DIALOG_ZIP_CODE
-        com.android.internal.R.string.autofill_county,                      // IDS_AUTOFILL_DIALOG_COUNTY
-        com.android.internal.R.string.autofill_island,                      // IDS_AUTOFILL_DIALOG_ISLAND
-        com.android.internal.R.string.autofill_district,                    // IDS_AUTOFILL_DIALOG_DISTRICT
-        com.android.internal.R.string.autofill_department,                  // IDS_AUTOFILL_DIALOG_DEPARTMENT
-        com.android.internal.R.string.autofill_prefecture,                  // IDS_AUTOFILL_DIALOG_PREFECTURE
-        com.android.internal.R.string.autofill_parish,                      // IDS_AUTOFILL_DIALOG_PARISH
-        com.android.internal.R.string.autofill_area,                        // IDS_AUTOFILL_DIALOG_AREA
-        com.android.internal.R.string.autofill_emirate                      // IDS_AUTOFILL_DIALOG_EMIRATE
-    };
-
-    private static Context mApplicationContext;
-    private static Map<Integer, SoftReference<String> > mStrings;
-
-    public static void setApplicationContext(Context applicationContext) {
-        mApplicationContext = applicationContext.getApplicationContext();
-    }
-
-    private static String loadString(int id) {
-        if (mStrings == null) {
-            mStrings = new HashMap<Integer, SoftReference<String> >(mIdsArray.length);
-        }
-
-        String localisedString = mApplicationContext.getResources().getString(mIdsArray[id]);
-        mStrings.put(id, new SoftReference<String>(localisedString));
-        return localisedString;
-    }
-
-    public static String getLocalisedString(int id) {
-        if (mStrings == null) {
-            // This is the first time we need a localised string.
-            // loadString will create the Map.
-            return loadString(id);
-        }
-
-        SoftReference<String> ref = mStrings.get(id);
-        boolean needToLoad = ref == null || ref.get() == null;
-        return needToLoad ? loadString(id) : ref.get();
-    }
-}
diff --git a/core/java/android/webkit/MockGeolocation.java b/core/java/android/webkit/MockGeolocation.java
deleted file mode 100644
index 885c6c2..0000000
--- a/core/java/android/webkit/MockGeolocation.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-/**
- * Used to configure the mock Geolocation client for the LayoutTests.
- * @hide
- */
-public final class MockGeolocation {
-    private WebViewCore mWebViewCore;
-
-    public MockGeolocation(WebViewCore webViewCore) {
-        mWebViewCore = webViewCore;
-    }
-
-    /**
-     * Sets use of the mock Geolocation client. Also resets that client.
-     */
-    public void setUseMock() {
-        nativeSetUseMock(mWebViewCore);
-    }
-
-    /**
-     * Set the position for the mock Geolocation service.
-     */
-    public void setPosition(double latitude, double longitude, double accuracy) {
-        // This should only ever be called on the WebKit thread.
-        nativeSetPosition(mWebViewCore, latitude, longitude, accuracy);
-    }
-
-    /**
-     * Set the error for the mock Geolocation service.
-     */
-    public void setError(int code, String message) {
-        // This should only ever be called on the WebKit thread.
-        nativeSetError(mWebViewCore, code, message);
-    }
-
-    public void setPermission(boolean allow) {
-        // This should only ever be called on the WebKit thread.
-        nativeSetPermission(mWebViewCore, allow);
-    }
-
-    // Native functions
-    private static native void nativeSetUseMock(WebViewCore webViewCore);
-    private static native void nativeSetPosition(WebViewCore webViewCore, double latitude,
-            double longitude, double accuracy);
-    private static native void nativeSetError(WebViewCore webViewCore, int code, String message);
-    private static native void nativeSetPermission(WebViewCore webViewCore, boolean allow);
-}
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
deleted file mode 100644
index d91f860..0000000
--- a/core/java/android/webkit/OverScrollGlow.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.webkit;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.widget.EdgeEffect;
-
-/**
- * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
- * @hide
- */
-public class OverScrollGlow {
-    private WebViewClassic mHostView;
-
-    private EdgeEffect mEdgeGlowTop;
-    private EdgeEffect mEdgeGlowBottom;
-    private EdgeEffect mEdgeGlowLeft;
-    private EdgeEffect mEdgeGlowRight;
-
-    private int mOverScrollDeltaX;
-    private int mOverScrollDeltaY;
-
-    public OverScrollGlow(WebViewClassic host) {
-        mHostView = host;
-        Context context = host.getContext();
-        mEdgeGlowTop = new EdgeEffect(context);
-        mEdgeGlowBottom = new EdgeEffect(context);
-        mEdgeGlowLeft = new EdgeEffect(context);
-        mEdgeGlowRight = new EdgeEffect(context);
-    }
-
-    /**
-     * Pull leftover touch scroll distance into one of the edge glows as appropriate.
-     *
-     * @param x Current X scroll offset
-     * @param y Current Y scroll offset
-     * @param oldX Old X scroll offset
-     * @param oldY Old Y scroll offset
-     * @param maxX Maximum range for horizontal scrolling
-     * @param maxY Maximum range for vertical scrolling
-     */
-    public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) {
-        // Only show overscroll bars if there was no movement in any direction
-        // as a result of scrolling.
-        if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) {
-            // Don't show left/right glows if we fit the whole content.
-            // Also don't show if there was vertical movement.
-            if (maxX > 0) {
-                final int pulledToX = oldX + mOverScrollDeltaX;
-                if (pulledToX < 0) {
-                    mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
-                    if (!mEdgeGlowRight.isFinished()) {
-                        mEdgeGlowRight.onRelease();
-                    }
-                } else if (pulledToX > maxX) {
-                    mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
-                    if (!mEdgeGlowLeft.isFinished()) {
-                        mEdgeGlowLeft.onRelease();
-                    }
-                }
-                mOverScrollDeltaX = 0;
-            }
-
-            if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
-                final int pulledToY = oldY + mOverScrollDeltaY;
-                if (pulledToY < 0) {
-                    mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
-                    if (!mEdgeGlowBottom.isFinished()) {
-                        mEdgeGlowBottom.onRelease();
-                    }
-                } else if (pulledToY > maxY) {
-                    mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
-                    if (!mEdgeGlowTop.isFinished()) {
-                        mEdgeGlowTop.onRelease();
-                    }
-                }
-                mOverScrollDeltaY = 0;
-            }
-        }
-    }
-
-    /**
-     * Set touch delta values indicating the current amount of overscroll.
-     *
-     * @param deltaX
-     * @param deltaY
-     */
-    public void setOverScrollDeltas(int deltaX, int deltaY) {
-        mOverScrollDeltaX = deltaX;
-        mOverScrollDeltaY = deltaY;
-    }
-
-    /**
-     * Absorb leftover fling velocity into one of the edge glows as appropriate.
-     *
-     * @param x Current X scroll offset
-     * @param y Current Y scroll offset
-     * @param oldX Old X scroll offset
-     * @param oldY Old Y scroll offset
-     * @param rangeX Maximum range for horizontal scrolling
-     * @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.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
-            if (y < 0 && oldY >= 0) {
-                mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
-                if (!mEdgeGlowBottom.isFinished()) {
-                    mEdgeGlowBottom.onRelease();
-                }
-            } else if (y > rangeY && oldY <= rangeY) {
-                mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
-                if (!mEdgeGlowTop.isFinished()) {
-                    mEdgeGlowTop.onRelease();
-                }
-            }
-        }
-
-        if (rangeX > 0) {
-            if (x < 0 && oldX >= 0) {
-                mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
-                if (!mEdgeGlowRight.isFinished()) {
-                    mEdgeGlowRight.onRelease();
-                }
-            } else if (x > rangeX && oldX <= rangeX) {
-                mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity());
-                if (!mEdgeGlowLeft.isFinished()) {
-                    mEdgeGlowLeft.onRelease();
-                }
-            }
-        }
-    }
-
-    /**
-     * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null.
-     *
-     * @param canvas Canvas to draw into, transformed into view coordinates.
-     * @return true if glow effects are still animating and the view should invalidate again.
-     */
-    public boolean drawEdgeGlows(Canvas canvas) {
-        final int scrollX = mHostView.getScrollX();
-        final int scrollY = mHostView.getScrollY();
-        final int width = mHostView.getWidth();
-        int height = mHostView.getHeight();
-
-        boolean invalidateForGlow = false;
-        if (!mEdgeGlowTop.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.translate(scrollX, mHostView.getVisibleTitleHeight() + Math.min(0, scrollY));
-            mEdgeGlowTop.setSize(width, height);
-            invalidateForGlow |= mEdgeGlowTop.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        if (!mEdgeGlowBottom.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.translate(-width + scrollX, Math.max(mHostView.computeMaxScrollY(), scrollY)
-                    + height);
-            canvas.rotate(180, width, 0);
-            mEdgeGlowBottom.setSize(width, height);
-            invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        if (!mEdgeGlowLeft.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.rotate(270);
-            canvas.translate(-height - scrollY, Math.min(0, scrollX));
-            mEdgeGlowLeft.setSize(height, width);
-            invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        if (!mEdgeGlowRight.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.rotate(90);
-            canvas.translate(scrollY,
-                    -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width));
-            mEdgeGlowRight.setSize(height, width);
-            invalidateForGlow |= mEdgeGlowRight.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        return invalidateForGlow;
-    }
-
-    /**
-     * @return True if any glow is still animating
-     */
-    public boolean isAnimating() {
-        return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() ||
-                !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished());
-    }
-
-    /**
-     * Release all glows from any touch pulls in progress.
-     */
-    public void releaseAll() {
-        mEdgeGlowTop.onRelease();
-        mEdgeGlowBottom.onRelease();
-        mEdgeGlowLeft.onRelease();
-        mEdgeGlowRight.onRelease();
-    }
-}
diff --git a/core/java/android/webkit/PluginData.java b/core/java/android/webkit/PluginData.java
deleted file mode 100644
index 88fc9b7..0000000
--- a/core/java/android/webkit/PluginData.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2009 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 java.io.InputStream;
-import java.util.Map;
-
-/**
- * This class encapsulates the content generated by a plugin.  The
- * data itself is meant to be loaded into webkit via the
- * PluginContentLoader class, which needs to be able to construct an
- * HTTP response. For this, it needs a stream with the response body,
- * the length of the body, the response headers, and the response
- * status code. The PluginData class is the container for all these
- * parts.
- *
- * @hide
- * @deprecated This class was intended to be used by Gears. Since Gears was
- * deprecated, so is this class.
- */
-@Deprecated
-public final class PluginData {
-    /**
-     * The content stream.
-     */
-    private InputStream mStream;
-    /**
-     * The content length.
-     */
-    private long mContentLength;
-    /**
-     * The associated HTTP response headers stored as a map of
-     * lowercase header name to [ unmodified header name, header value].
-     * TODO: This design was always a hack. Remove (involves updating
-     * the Gears C++ side).
-     */
-    private Map<String, String[]> mHeaders;
-
-    /**
-     * The associated HTTP response code.
-     */
-    private int mStatusCode;
-
-    /**
-     * Creates a PluginData instance.
-     *
-     * @param stream The stream that supplies content for the plugin.
-     * @param length The length of the plugin content.
-     * @param headers The response headers. Map of
-     * lowercase header name to [ unmodified header name, header value]
-     * @param length The HTTP response status code.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public PluginData(
-            InputStream stream,
-            long length,
-            Map<String, String[]> headers,
-            int code) {
-        mStream = stream;
-        mContentLength = length;
-        mHeaders = headers;
-        mStatusCode = code;
-    }
-
-    /**
-     * Returns the input stream that contains the plugin content.
-     *
-     * @return An InputStream instance with the plugin content.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public InputStream getInputStream() {
-        return mStream;
-    }
-
-    /**
-     * Returns the length of the plugin content.
-     *
-     * @return the length of the plugin content.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public long getContentLength() {
-        return mContentLength;
-    }
-
-    /**
-     * Returns the HTTP response headers associated with the plugin
-     * content.
-     *
-     * @return A Map<String, String[]> containing all headers. The
-     * mapping is 'lowercase header name' to ['unmodified header
-     * name', header value].
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public Map<String, String[]> getHeaders() {
-        return mHeaders;
-    }
-
-    /**
-     * Returns the HTTP status code for the response.
-     *
-     * @return The HTTP statue code, e.g 200.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public int getStatusCode() {
-        return mStatusCode;
-    }
-}
diff --git a/core/java/android/webkit/PluginFullScreenHolder.java b/core/java/android/webkit/PluginFullScreenHolder.java
deleted file mode 100644
index 665cd9d..0000000
--- a/core/java/android/webkit/PluginFullScreenHolder.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package android.webkit;
-
-import android.content.Context;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-class PluginFullScreenHolder {
-
-    private final WebViewClassic mWebView;
-    private final int mNpp;
-    private final int mOrientation;
-
-    // The container for the plugin view
-    private static CustomFrameLayout mLayout;
-
-    private View mContentView;
-
-    PluginFullScreenHolder(WebViewClassic webView, int orientation, int npp) {
-        mWebView = webView;
-        mNpp = npp;
-        mOrientation = orientation;
-    }
-
-    public void setContentView(View contentView) {
-
-        // Create a FrameLayout that will contain the plugin's view
-        mLayout = new CustomFrameLayout(mWebView.getContext());
-        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
-                            ViewGroup.LayoutParams.MATCH_PARENT,
-                            ViewGroup.LayoutParams.MATCH_PARENT,
-                            Gravity.CENTER);
-
-        mLayout.addView(contentView, layoutParams);
-        mLayout.setVisibility(View.VISIBLE);
-
-        // fixed size is only used either during pinch zoom or surface is too
-        // big. Make sure it is not fixed size before setting it to the full
-        // screen content view. The SurfaceView will be set to the correct mode
-        // by the ViewManager when it is re-attached to the WebView.
-        if (contentView instanceof SurfaceView) {
-            final SurfaceView sView = (SurfaceView) contentView;
-            if (sView.isFixedSize()) {
-                sView.getHolder().setSizeFromLayout();
-            }
-        }
-
-        mContentView = contentView;
-    }
-
-    public void show() {
-        // Other plugins may attempt to draw so hide them while we're active.
-        if (mWebView.getViewManager() != null)
-            mWebView.getViewManager().hideAll();
-
-        WebChromeClient client = mWebView.getWebChromeClient();
-        client.onShowCustomView(mLayout, mOrientation, mCallback);
-    }
-
-    public void hide() {
-        WebChromeClient client = mWebView.getWebChromeClient();
-        client.onHideCustomView();
-    }
-
-    private class CustomFrameLayout extends FrameLayout {
-
-        CustomFrameLayout(Context context) {
-            super(context);
-        }
-
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            if (event.isSystem()) {
-                return super.onKeyDown(keyCode, event);
-            }
-            mWebView.onKeyDown(keyCode, event);
-            // always return true as we are the handler
-            return true;
-        }
-
-        @Override
-        public boolean onKeyUp(int keyCode, KeyEvent event) {
-            if (event.isSystem()) {
-                return super.onKeyUp(keyCode, event);
-            }
-            mWebView.onKeyUp(keyCode, event);
-            // always return true as we are the handler
-            return true;
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            // always return true as we don't want the event to propagate any further
-            return true;
-        }
-
-        @Override
-        public boolean onTrackballEvent(MotionEvent event) {
-            mWebView.onTrackballEvent(event);
-            // always return true as we are the handler
-            return true;
-        }
-    }
-    
-    private final WebChromeClient.CustomViewCallback mCallback =
-        new WebChromeClient.CustomViewCallback() {
-            public void onCustomViewHidden() {
-
-                mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
-                    .sendToTarget();
-
-                mWebView.getWebViewCore().sendMessage(
-                        WebViewCore.EventHub.HIDE_FULLSCREEN, mNpp, 0);
-
-                mLayout.removeView(mContentView);
-                mLayout = null;
-
-                // Re enable plugin views.
-                mWebView.getViewManager().showAll();
-            }
-        };
-}
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
deleted file mode 100644
index fe40156..0000000
--- a/core/java/android/webkit/PluginManager.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2009 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 java.util.ArrayList;
-import java.util.List;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.Signature;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-/**
- * Class for managing the relationship between the {@link WebViewClassic} and installed
- * plugins in the system. You can find this class through
- * {@link PluginManager#getInstance}.
- * 
- * @hide pending API solidification
- */
-public class PluginManager {
-
-    /**
-     * Service Action: A plugin wishes to be loaded in the WebView must provide
-     * {@link android.content.IntentFilter IntentFilter} that accepts this
-     * action in their AndroidManifest.xml.
-     * <p>
-     * TODO: we may change this to a new PLUGIN_ACTION if this is going to be
-     * public.
-     */
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
-
-    /**
-     * A plugin wishes to be loaded in the WebView must provide this permission
-     * in their AndroidManifest.xml.
-     */
-    public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
-
-    private static final String LOGTAG = "PluginManager";
-
-    private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/";
-
-    private static final String PLUGIN_TYPE = "type";
-    private static final String TYPE_NATIVE = "native";
-
-    private static PluginManager mInstance = null;
-
-    private final Context mContext;
-
-    private ArrayList<PackageInfo> mPackageInfoCache;
-
-    // Only plugin matches one of the signatures in the list can be loaded
-    // inside the WebView process
-    private static final String SIGNATURE_1 = "308204c5308203ada003020102020900d7cb412f75f4887e300d06092a864886f70d010105050030819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f7261746564301e170d3039313030313030323331345a170d3337303231363030323331345a30819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f726174656430820120300d06092a864886f70d01010105000382010d0030820108028201010099724f3e05bbd78843794f357776e04b340e13cb1c9ccb3044865180d7d8fec8166c5bbd876da8b80aa71eb6ba3d4d3455c9a8de162d24a25c4c1cd04c9523affd06a279fc8f0d018f242486bdbb2dbfbf6fcb21ed567879091928b876f7ccebc7bccef157366ebe74e33ae1d7e9373091adab8327482154afc0693a549522f8c796dd84d16e24bb221f5dbb809ca56dd2b6e799c5fa06b6d9c5c09ada54ea4c5db1523a9794ed22a3889e5e05b29f8ee0a8d61efe07ae28f65dece2ff7edc5b1416d7c7aad7f0d35e8f4a4b964dbf50ae9aa6d620157770d974131b3e7e3abd6d163d65758e2f0822db9c88598b9db6263d963d13942c91fc5efe34fc1e06e3020103a382010630820102301d0603551d0e041604145af418e419a639e1657db960996364a37ef20d403081d20603551d230481ca3081c780145af418e419a639e1657db960996364a37ef20d40a181a3a481a030819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f7261746564820900d7cb412f75f4887e300c0603551d13040530030101ff300d06092a864886f70d0101050500038201010076c2a11fe303359689c2ebc7b2c398eff8c3f9ad545cdbac75df63bf7b5395b6988d1842d6aa1556d595b5692e08224d667a4c9c438f05e74906c53dd8016dde7004068866f01846365efd146e9bfaa48c9ecf657f87b97c757da11f225c4a24177bf2d7188e6cce2a70a1e8a841a14471eb51457398b8a0addd8b6c8c1538ca8f1e40b4d8b960009ea22c188d28924813d2c0b4a4d334b7cf05507e1fcf0a06fe946c7ffc435e173af6fc3e3400643710acc806f830a14788291d46f2feed9fb5c70423ca747ed1572d752894ac1f19f93989766308579393fabb43649aa8806a313b1ab9a50922a44c2467b9062037f2da0d484d9ffd8fe628eeea629ba637";
-
-    private static final Signature[] SIGNATURES = new Signature[] {
-        new Signature(SIGNATURE_1)
-    };
-
-    private PluginManager(Context context) {
-        mContext = context;
-        mPackageInfoCache = new ArrayList<PackageInfo>();
-    }
-
-    public static synchronized PluginManager getInstance(Context context) {
-        if (mInstance == null) {
-            if (context == null) {
-                throw new IllegalStateException(
-                        "First call to PluginManager need a valid context.");
-            }
-            mInstance = new PluginManager(context.getApplicationContext());
-        }
-        return mInstance;
-    }
-
-    /**
-     * Signal the WebCore thread to refresh its list of plugins. Use this if the
-     * directory contents of one of the plugin directories has been modified and
-     * needs its changes reflecting. May cause plugin load and/or unload.
-     * 
-     * @param reloadOpenPages Set to true to reload all open pages.
-     */
-    public void refreshPlugins(boolean reloadOpenPages) {
-        BrowserFrame.sJavaBridge.obtainMessage(
-                JWebCoreJavaBridge.REFRESH_PLUGINS, reloadOpenPages)
-                .sendToTarget();
-    }
-
-    String[] getPluginDirectories() {
-
-        ArrayList<String> directories = new ArrayList<String>();
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION),
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
-
-        synchronized(mPackageInfoCache) {
-
-            // clear the list of existing packageInfo objects
-            mPackageInfoCache.clear();
-
-            for (ResolveInfo info : plugins) {
-
-                // retrieve the plugin's service information
-                ServiceInfo serviceInfo = info.serviceInfo;
-                if (serviceInfo == null) {
-                    Log.w(LOGTAG, "Ignore bad plugin");
-                    continue;
-                }
-
-                // retrieve information from the plugin's manifest
-                PackageInfo pkgInfo;
-                try {
-                    pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
-                                    PackageManager.GET_PERMISSIONS
-                                    | PackageManager.GET_SIGNATURES);
-                } catch (NameNotFoundException e) {
-                    Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName);
-                    continue;
-                }
-                if (pkgInfo == null) {
-                    continue;
-                }
-
-                /*
-                 * find the location of the plugin's shared library. The default
-                 * is to assume the app is either a user installed app or an
-                 * updated system app. In both of these cases the library is
-                 * stored in the app's data directory.
-                 */
-                String directory = pkgInfo.applicationInfo.dataDir + "/lib";
-                final int appFlags = pkgInfo.applicationInfo.flags;
-                final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM |
-                                               ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-                // preloaded system app with no user updates
-                if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) {
-                    directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName;
-                }
-
-                // check if the plugin has the required permissions and
-                // signatures
-                if (!containsPluginPermissionAndSignatures(pkgInfo)) {
-                    continue;
-                }
-
-                // determine the type of plugin from the manifest
-                if (serviceInfo.metaData == null) {
-                    Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined");
-                    continue;
-                }
-
-                String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE);
-                if (!TYPE_NATIVE.equals(pluginType)) {
-                    Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType);
-                    continue;
-                }
-
-                try {
-                    Class<?> cls = getPluginClass(serviceInfo.packageName, serviceInfo.name);
-
-                    //TODO implement any requirements of the plugin class here!
-                    boolean classFound = true;
-
-                    if (!classFound) {
-                        Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class.");
-                        continue;
-                    }
-
-                } catch (NameNotFoundException e) {
-                    Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName);
-                    continue;
-                } catch (ClassNotFoundException e) {
-                    Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name);
-                    continue;
-                }
-
-                // if all checks have passed then make the plugin available
-                mPackageInfoCache.add(pkgInfo);
-                directories.add(directory);
-            }
-        }
-
-        return directories.toArray(new String[directories.size()]);
-    }
-
-    /* package */
-    boolean containsPluginPermissionAndSignatures(String pluginAPKName) {
-        PackageManager pm = mContext.getPackageManager();
-
-        // retrieve information from the plugin's manifest
-        try {
-            PackageInfo pkgInfo = pm.getPackageInfo(pluginAPKName, PackageManager.GET_PERMISSIONS
-                    | PackageManager.GET_SIGNATURES);
-            if (pkgInfo != null) {
-                return containsPluginPermissionAndSignatures(pkgInfo);
-            }
-        } catch (NameNotFoundException e) {
-            Log.w(LOGTAG, "Can't find plugin: " + pluginAPKName);
-        }
-        return false;
-    }
-
-    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
-
-        // check if the plugin has the required permissions
-        String permissions[] = pkgInfo.requestedPermissions;
-        if (permissions == null) {
-            return false;
-        }
-        boolean permissionOk = false;
-        for (String permit : permissions) {
-            if (PLUGIN_PERMISSION.equals(permit)) {
-                permissionOk = true;
-                break;
-            }
-        }
-        if (!permissionOk) {
-            return false;
-        }
-
-        // check to ensure the plugin is properly signed
-        Signature signatures[] = pkgInfo.signatures;
-        if (signatures == null) {
-            return false;
-        }
-        if (SystemProperties.getBoolean("ro.secure", false)) {
-            boolean signatureMatch = false;
-            for (Signature signature : signatures) {
-                for (int i = 0; i < SIGNATURES.length; i++) {
-                    if (SIGNATURES[i].equals(signature)) {
-                        signatureMatch = true;
-                        break;
-                    }
-                }
-            }
-            if (!signatureMatch) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /* package */
-    String getPluginsAPKName(String pluginLib) {
-
-        // basic error checking on input params
-        if (pluginLib == null || pluginLib.length() == 0) {
-            return null;
-        }
-
-        // must be synchronized to ensure the consistency of the cache
-        synchronized(mPackageInfoCache) {
-            for (PackageInfo pkgInfo : mPackageInfoCache) {
-                if (pluginLib.contains(pkgInfo.packageName)) {
-                    return pkgInfo.packageName;
-                }
-            }
-        }
-
-        // if no apk was found then return null
-        return null;
-    }
-
-    String getPluginSharedDataDirectory() {
-        return mContext.getDir("plugins", 0).getPath();
-    }
-
-    /* package */
-    Class<?> getPluginClass(String packageName, String className)
-            throws NameNotFoundException, ClassNotFoundException {
-        Context pluginContext = mContext.createPackageContext(packageName,
-                Context.CONTEXT_INCLUDE_CODE |
-                Context.CONTEXT_IGNORE_SECURITY);
-        ClassLoader pluginCL = pluginContext.getClassLoader();
-        return pluginCL.loadClass(className);
-    }
-}
diff --git a/core/java/android/webkit/QuadF.java b/core/java/android/webkit/QuadF.java
deleted file mode 100644
index e9011e3..0000000
--- a/core/java/android/webkit/QuadF.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.graphics.PointF;
-
-/**
- * A quadrilateral, determined by four points, clockwise order. Typically
- * p1 is "top-left" and p4 is "bottom-left" following webkit's rectangle-to-
- * FloatQuad conversion.
- */
-class QuadF {
-    public PointF p1;
-    public PointF p2;
-    public PointF p3;
-    public PointF p4;
-
-    public QuadF() {
-        p1 = new PointF();
-        p2 = new PointF();
-        p3 = new PointF();
-        p4 = new PointF();
-    }
-
-    public void offset(float dx, float dy) {
-        p1.offset(dx, dy);
-        p2.offset(dx, dy);
-        p3.offset(dx, dy);
-        p4.offset(dx, dy);
-    }
-
-    /**
-     * Determines if the quadrilateral contains the given point. This does
-     * not work if the quadrilateral is self-intersecting or if any inner
-     * angle is reflex (greater than 180 degrees).
-     */
-    public boolean containsPoint(float x, float y) {
-        return isPointInTriangle(x, y, p1, p2, p3) ||
-                isPointInTriangle(x, y, p1, p3, p4);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder s = new StringBuilder("QuadF(");
-        s.append(p1.x).append(",").append(p1.y);
-        s.append(" - ");
-        s.append(p2.x).append(",").append(p2.y);
-        s.append(" - ");
-        s.append(p3.x).append(",").append(p3.y);
-        s.append(" - ");
-        s.append(p4.x).append(",").append(p4.y);
-        s.append(")");
-        return s.toString();
-    }
-
-    private static boolean isPointInTriangle(float x0, float y0,
-            PointF r1, PointF r2, PointF r3) {
-        // Use the barycentric technique
-        float x13 = r1.x - r3.x;
-        float y13 = r1.y - r3.y;
-        float x23 = r2.x - r3.x;
-        float y23 = r2.y - r3.y;
-        float x03 = x0 - r3.x;
-        float y03 = y0 - r3.y;
-
-        float determinant = (y23 * x13) - (x23 * y13);
-        float lambda1 = ((y23 * x03) - (x23 * y03))/determinant;
-        float lambda2 = ((x13 * y03) - (y13 * x03))/determinant;
-        float lambda3 = 1 - lambda1 - lambda2;
-        return lambda1 >= 0.0f && lambda2 >= 0.0f && lambda3 >= 0.0f;
-    }
-}
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
deleted file mode 100644
index f9f5b03..0000000
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.app.Activity;
-import android.app.SearchManager;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.Browser;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-
-class SelectActionModeCallback implements ActionMode.Callback {
-    private WebViewClassic mWebView;
-    private ActionMode mActionMode;
-    private boolean mIsTextSelected = true;
-
-    void setWebView(WebViewClassic webView) {
-        mWebView = webView;
-    }
-
-    void setTextSelected(boolean isTextSelected) {
-        mIsTextSelected = isTextSelected;
-    }
-
-    void finish() {
-        // It is possible that onCreateActionMode was never called, in the case
-        // where there is no ActionBar, for example.
-        if (mActionMode != null) {
-            mActionMode.finish();
-        }
-    }
-
-    // ActionMode.Callback implementation
-
-    @Override
-    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-        mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, menu);
-
-        final Context context = mWebView.getContext();
-        mode.setTitle(context.getString(com.android.internal.R.string.textSelectionCABTitle));
-        mode.setTitleOptionalHint(true);
-
-        // If the action mode UI we're running in isn't capable of taking window focus
-        // the user won't be able to type into the find on page UI. Disable this functionality.
-        // (Note that this should only happen in floating dialog windows.)
-        // This can be removed once we can handle multiple focusable windows at a time
-        // in a better way.
-        ClipboardManager cm = (ClipboardManager)(context
-                .getSystemService(Context.CLIPBOARD_SERVICE));
-        boolean isFocusable = mode.isUiFocusable();
-        boolean isEditable = mWebView.focusCandidateIsEditableText();
-        boolean canPaste = isEditable && cm.hasPrimaryClip() && isFocusable;
-        boolean canFind = !isEditable && isFocusable;
-        boolean canCut = isEditable && mIsTextSelected && isFocusable;
-        boolean canCopy = mIsTextSelected;
-        boolean canWebSearch = mIsTextSelected;
-        setMenuVisibility(menu, canFind, com.android.internal.R.id.find);
-        setMenuVisibility(menu, canPaste, com.android.internal.R.id.paste);
-        setMenuVisibility(menu, canCut, com.android.internal.R.id.cut);
-        setMenuVisibility(menu, canCopy, com.android.internal.R.id.copy);
-        setMenuVisibility(menu, canWebSearch, com.android.internal.R.id.websearch);
-        mActionMode = mode;
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-        return true;
-    }
-
-    @Override
-    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-        switch(item.getItemId()) {
-            case android.R.id.cut:
-                mWebView.cutSelection();
-                mode.finish();
-                break;
-
-            case android.R.id.copy:
-                mWebView.copySelection();
-                mode.finish();
-                break;
-
-            case android.R.id.paste:
-                mWebView.pasteFromClipboard();
-                mode.finish();
-                break;
-
-            case com.android.internal.R.id.share:
-                String selection = mWebView.getSelection();
-                Browser.sendString(mWebView.getContext(), selection);
-                mode.finish();
-                break;
-
-            case com.android.internal.R.id.select_all:
-                mWebView.selectAll();
-                break;
-
-            case com.android.internal.R.id.find:
-                String sel= mWebView.getSelection();
-                mode.finish();
-                mWebView.showFindDialog(sel, false);
-                break;
-            case com.android.internal.R.id.websearch:
-                mode.finish();
-                Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
-                i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
-                i.putExtra(SearchManager.QUERY, mWebView.getSelection());
-                if (!(mWebView.getContext() instanceof Activity)) {
-                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                }
-                mWebView.getContext().startActivity(i);
-                break;
-
-            default:
-                return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void onDestroyActionMode(ActionMode mode) {
-        mWebView.selectionDone();
-    }
-
-    private void setMenuVisibility(Menu menu, boolean visible, int resourceId) {
-        final MenuItem item = menu.findItem(resourceId);
-        if (item != null) {
-            item.setVisible(visible);
-        }
-    }
-}
diff --git a/core/java/android/webkit/SslCertLookupTable.java b/core/java/android/webkit/SslCertLookupTable.java
deleted file mode 100644
index 98ace4f..0000000
--- a/core/java/android/webkit/SslCertLookupTable.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.os.Bundle;
-import android.net.http.SslError;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * Stores the user's decision of whether to allow or deny an invalid certificate.
- *
- * This class is not threadsafe. It is used only on the WebCore thread. Also, it
- * is used only by the Chromium HTTP stack.
- */
-final class SslCertLookupTable {
-    private static SslCertLookupTable sTable;
-    // We store the most severe error we're willing to allow for each host.
-    private final Bundle table;
-
-    public static SslCertLookupTable getInstance() {
-        if (sTable == null) {
-            sTable = new SslCertLookupTable();
-        }
-        return sTable;
-    }
-
-    private SslCertLookupTable() {
-        table = new Bundle();
-    }
-
-    public void setIsAllowed(SslError sslError) {
-        String host;
-        try {
-            host = new URL(sslError.getUrl()).getHost();
-        } catch(MalformedURLException e) {
-            return;
-        }
-        table.putInt(host, sslError.getPrimaryError());
-    }
-
-    // We allow the decision to be re-used if it's for the same host and is for
-    // an error of equal or greater severity than this error.
-    public boolean isAllowed(SslError sslError) {
-        String host;
-        try {
-            host = new URL(sslError.getUrl()).getHost();
-        } catch(MalformedURLException e) {
-            return false;
-        }
-        return table.containsKey(host) && sslError.getPrimaryError() <= table.getInt(host);
-    }
-
-    public void clear() {
-        table.clear();
-    }
-}
diff --git a/core/java/android/webkit/SslClientCertLookupTable.java b/core/java/android/webkit/SslClientCertLookupTable.java
deleted file mode 100644
index c52b7e8..0000000
--- a/core/java/android/webkit/SslClientCertLookupTable.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2011 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 java.security.PrivateKey;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A simple class to store client certificates that user has chosen.
- */
-final class SslClientCertLookupTable {
-    private static SslClientCertLookupTable sTable;
-    private final Map<String, PrivateKey> privateKeys;
-    private final Map<String, byte[][]> certificateChains;
-    private final Set<String> denied;
-
-    public static synchronized SslClientCertLookupTable getInstance() {
-        if (sTable == null) {
-            sTable = new SslClientCertLookupTable();
-        }
-        return sTable;
-    }
-
-    private SslClientCertLookupTable() {
-        privateKeys = new HashMap<String, PrivateKey>();
-        certificateChains = new HashMap<String, byte[][]>();
-        denied = new HashSet<String>();
-    }
-
-    public void Allow(String host_and_port, PrivateKey privateKey, byte[][] chain) {
-        privateKeys.put(host_and_port, privateKey);
-        certificateChains.put(host_and_port, chain);
-        denied.remove(host_and_port);
-    }
-
-    public void Deny(String host_and_port) {
-        privateKeys.remove(host_and_port);
-        certificateChains.remove(host_and_port);
-        denied.add(host_and_port);
-    }
-
-    public boolean IsAllowed(String host_and_port) {
-        return privateKeys.containsKey(host_and_port);
-    }
-
-    public boolean IsDenied(String host_and_port) {
-        return denied.contains(host_and_port);
-    }
-
-    public PrivateKey PrivateKey(String host_and_port) {
-        return privateKeys.get(host_and_port);
-    }
-
-    public byte[][] CertificateChain(String host_and_port) {
-        return certificateChains.get(host_and_port);
-    }
-}
diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java
deleted file mode 100644
index 59fc0cb..0000000
--- a/core/java/android/webkit/UrlInterceptHandler.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2008 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.webkit.CacheManager.CacheResult;
-import android.webkit.PluginData;
-import java.util.Map;
-
-/**
- * @hide
- * @deprecated This interface was inteded to be used by Gears. Since Gears was
- * deprecated, so is this class.
- */
-@Deprecated
-public interface UrlInterceptHandler {
-
-    /**
-     * Given an URL, returns the CacheResult which contains the
-     * surrogate response for the request, or null if the handler is
-     * not interested.
-     *
-     * @param url URL string.
-     * @param headers The headers associated with the request. May be null.
-     * @return The CacheResult containing the surrogate response.
-     *
-     * @hide
-     * @deprecated Do not use, this interface is deprecated.
-     */
-    @Deprecated
-    public CacheResult service(String url, Map<String, String> headers);
-
-    /**
-     * Given an URL, returns the PluginData which contains the
-     * surrogate response for the request, or null if the handler is
-     * not interested.
-     *
-     * @param url URL string.
-     * @param headers The headers associated with the request. May be null.
-     * @return The PluginData containing the surrogate response.
-     *
-     * @hide
-     * @deprecated Do not use, this interface is deprecated.
-     */
-    @Deprecated
-    public PluginData getPluginData(String url, Map<String, String> headers);
-}
diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java
deleted file mode 100644
index bdf6747..0000000
--- a/core/java/android/webkit/UrlInterceptRegistry.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2008 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.webkit.CacheManager.CacheResult;
-import android.webkit.PluginData;
-import android.webkit.UrlInterceptHandler;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-
-/**
- * @hide
- * @deprecated This class was intended to be used by Gears. Since Gears was
- * deprecated, so is this class.
- */
-@Deprecated
-public final class UrlInterceptRegistry {
-
-    private final static String LOGTAG = "intercept";
-
-    private static boolean mDisabled = false;
-
-    private static LinkedList mHandlerList;
-
-    private static synchronized LinkedList getHandlers() {
-        if(mHandlerList == null)
-            mHandlerList = new LinkedList<UrlInterceptHandler>();
-        return mHandlerList;
-    }
-
-    /**
-     * set the flag to control whether url intercept is enabled or disabled
-     * 
-     * @param disabled true to disable the cache
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized void setUrlInterceptDisabled(boolean disabled) {
-        mDisabled = disabled;
-    }
-
-    /**
-     * get the state of the url intercept, enabled or disabled
-     * 
-     * @return return if it is disabled
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized boolean urlInterceptDisabled() {
-        return mDisabled;
-    }
-
-    /**
-     * Register a new UrlInterceptHandler. This handler will be called
-     * before any that were previously registered.
-     *
-     * @param handler The new UrlInterceptHandler object
-     * @return true if the handler was not previously registered.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized boolean registerHandler(
-            UrlInterceptHandler handler) {
-        if (!getHandlers().contains(handler)) {
-            getHandlers().addFirst(handler);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Unregister a previously registered UrlInterceptHandler.
-     *
-     * @param handler A previously registered UrlInterceptHandler.
-     * @return true if the handler was found and removed from the list.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized boolean unregisterHandler(
-            UrlInterceptHandler handler) {
-        return getHandlers().remove(handler);
-    }
-
-    /**
-     * Given an url, returns the CacheResult of the first
-     * UrlInterceptHandler interested, or null if none are.
-     *
-     * @return A CacheResult containing surrogate content.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized CacheResult getSurrogate(
-            String url, Map<String, String> headers) {
-        if (urlInterceptDisabled()) {
-            return null;
-        }
-        Iterator iter = getHandlers().listIterator();
-        while (iter.hasNext()) {
-            UrlInterceptHandler handler = (UrlInterceptHandler) iter.next();
-            CacheResult result = handler.service(url, headers);
-            if (result != null) {
-                return result;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Given an url, returns the PluginData of the first
-     * UrlInterceptHandler interested, or null if none are or if
-     * intercepts are disabled.
-     *
-     * @return A PluginData instance containing surrogate content.
-     *
-     * @hide
-     * @deprecated This class was intended to be used by Gears. Since Gears was
-     * deprecated, so is this class.
-     */
-    @Deprecated
-    public static synchronized PluginData getPluginData(
-            String url, Map<String, String> headers) {
-        if (urlInterceptDisabled()) {
-            return null;
-        }
-        Iterator iter = getHandlers().listIterator();
-        while (iter.hasNext()) {
-            UrlInterceptHandler handler = (UrlInterceptHandler) iter.next();
-            PluginData data = handler.getPluginData(url, headers);
-            if (data != null) {
-                return data;
-            }
-        }
-        return null;
-    }
-}
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
deleted file mode 100644
index 34065a1..0000000
--- a/core/java/android/webkit/ViewManager.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2009 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.util.DisplayMetrics;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsoluteLayout;
-
-import java.util.ArrayList;
-
-class ViewManager {
-    private final WebViewClassic mWebView;
-    private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
-    private boolean mHidden;
-    private boolean mReadyToDraw;
-    private boolean mZoomInProgress = false;
-
-    // Threshold at which a surface is prevented from further increasing in size
-    private final int MAX_SURFACE_AREA;
-    // GPU Limit (hard coded for now)
-    private static final int MAX_SURFACE_DIMENSION = 2048;
-
-    class ChildView {
-        int x;
-        int y;
-        int width;
-        int height;
-        View mView; // generic view to show
-
-        ChildView() {
-        }
-
-        void setBounds(int x, int y, int width, int height) {
-            this.x = x;
-            this.y = y;
-            this.width = width;
-            this.height = height;
-        }
-
-        void attachView(int x, int y, int width, int height) {
-            if (mView == null) {
-                return;
-            }
-            setBounds(x, y, width, height);
-
-            mWebView.mPrivateHandler.post(new Runnable() {
-                public void run() {
-                    // This method may be called multiple times. If the view is
-                    // already attached, just set the new LayoutParams,
-                    // otherwise attach the view and add it to the list of
-                    // children.
-                    requestLayout(ChildView.this);
-
-                    if (mView.getParent() == null) {
-                        attachViewOnUIThread();
-                    }
-                }
-            });
-        }
-
-        private void attachViewOnUIThread() {
-            mWebView.getWebView().addView(mView);
-            mChildren.add(this);
-            if (!mReadyToDraw) {
-                mView.setVisibility(View.GONE);
-            }
-        }
-
-        void removeView() {
-            if (mView == null) {
-                return;
-            }
-            mWebView.mPrivateHandler.post(new Runnable() {
-                public void run() {
-                    removeViewOnUIThread();
-                }
-            });
-        }
-
-        private void removeViewOnUIThread() {
-            mWebView.getWebView().removeView(mView);
-            mChildren.remove(this);
-        }
-    }
-
-    ViewManager(WebViewClassic w) {
-        mWebView = w;
-        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.
-         */
-        MAX_SURFACE_AREA = (int)(pixelArea * 2.75);
-    }
-
-    ChildView createView() {
-        return new ChildView();
-    }
-
-    /**
-     * This should only be called from the UI thread.
-     */
-    private void requestLayout(ChildView v) {
-
-        int width = mWebView.contentToViewDimension(v.width);
-        int height = mWebView.contentToViewDimension(v.height);
-        int x = mWebView.contentToViewX(v.x);
-        int y = mWebView.contentToViewY(v.y);
-
-        AbsoluteLayout.LayoutParams lp;
-        ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams();
-
-        if (layoutParams instanceof AbsoluteLayout.LayoutParams) {
-            lp = (AbsoluteLayout.LayoutParams) layoutParams;
-            lp.width = width;
-            lp.height = height;
-            lp.x = x;
-            lp.y = y;
-        } else {
-            lp = new AbsoluteLayout.LayoutParams(width, height, x, y);
-        }
-
-        // apply the layout to the view
-        v.mView.setLayoutParams(lp);
-
-        if(v.mView instanceof SurfaceView) {
-
-            final SurfaceView sView = (SurfaceView) v.mView;
-
-            if (sView.isFixedSize() && mZoomInProgress) {
-                /* If we're already fixed, and we're in a zoom, then do nothing
-                   about the size. Just wait until we get called at the end of
-                   the zoom session (with mZoomInProgress false) and we'll
-                   fixup our size then.
-                 */
-                return;
-            }
-
-            /* Compute proportional fixed width/height if necessary.
-             *
-             * NOTE: plugins (e.g. Flash) must not explicitly fix the size of
-             * their surface. The logic below will result in unexpected behavior
-             * for the plugin if they attempt to fix the size of the surface.
-             */
-            int fixedW = width;
-            int fixedH = height;
-            if (fixedW > MAX_SURFACE_DIMENSION || fixedH > MAX_SURFACE_DIMENSION) {
-                if (v.width > v.height) {
-                    fixedW = MAX_SURFACE_DIMENSION;
-                    fixedH = v.height * MAX_SURFACE_DIMENSION / v.width;
-                } else {
-                    fixedH = MAX_SURFACE_DIMENSION;
-                    fixedW = v.width * MAX_SURFACE_DIMENSION / v.height;
-                }
-            }
-            if (fixedW * fixedH > MAX_SURFACE_AREA) {
-                float area = MAX_SURFACE_AREA;
-                if (v.width > v.height) {
-                    fixedW = (int)Math.sqrt(area * v.width / v.height);
-                    fixedH = v.height * fixedW / v.width;
-                } else {
-                    fixedH = (int)Math.sqrt(area * v.height / v.width);
-                    fixedW = v.width * fixedH / v.height;
-                }
-            }
-
-            if (fixedW != width || fixedH != height) {
-                // if we get here, either our dimensions or area (or both)
-                // exeeded our max, so we had to compute fixedW and fixedH
-                sView.getHolder().setFixedSize(fixedW, fixedH);
-            } else if (!sView.isFixedSize() && mZoomInProgress) {
-                // just freeze where we were (view size) until we're done with
-                // the zoom progress
-                sView.getHolder().setFixedSize(sView.getWidth(),
-                                               sView.getHeight());
-            } else if (sView.isFixedSize() && !mZoomInProgress) {
-                /* The changing of visibility is a hack to get around a bug in
-                 * the framework that causes the surface to revert to the size
-                 * it was prior to being fixed before it redraws using the
-                 * values currently in its layout.
-                 *
-                 * The surface is destroyed when it is set to invisible and then
-                 * recreated at the new dimensions when it is made visible. The
-                 * same destroy/create step occurs without the change in
-                 * visibility, but then exhibits the behavior described in the
-                 * previous paragraph.
-                 */
-                if (sView.getVisibility() == View.VISIBLE) {
-                    sView.setVisibility(View.INVISIBLE);
-                    sView.getHolder().setSizeFromLayout();
-                    // setLayoutParams() only requests the layout. If we set it
-                    // to VISIBLE now, it will use the old dimension to set the
-                    // size. Post a message to ensure that it shows the new size.
-                    mWebView.mPrivateHandler.post(new Runnable() {
-                        public void run() {
-                            sView.setVisibility(View.VISIBLE);
-                        }
-                    });
-                } else {
-                    sView.getHolder().setSizeFromLayout();
-                }
-            }
-        }
-    }
-
-    void startZoom() {
-        mZoomInProgress = true;
-        for (ChildView v : mChildren) {
-            requestLayout(v);
-        }
-    }
-
-    void endZoom() {
-        mZoomInProgress = false;
-        for (ChildView v : mChildren) {
-            requestLayout(v);
-        }
-    }
-
-    void scaleAll() {
-        for (ChildView v : mChildren) {
-            requestLayout(v);
-        }
-    }
-
-    void hideAll() {
-        if (mHidden) {
-            return;
-        }
-        for (ChildView v : mChildren) {
-            v.mView.setVisibility(View.GONE);
-        }
-        mHidden = true;
-    }
-
-    void showAll() {
-        if (!mHidden) {
-            return;
-        }
-        for (ChildView v : mChildren) {
-            v.mView.setVisibility(View.VISIBLE);
-        }
-        mHidden = false;
-    }
-
-    void postResetStateAll() {
-        mWebView.mPrivateHandler.post(new Runnable() {
-            public void run() {
-                mReadyToDraw = false;
-            }
-        });
-    }
-
-    void postReadyToDrawAll() {
-        mWebView.mPrivateHandler.post(new Runnable() {
-            public void run() {
-                mReadyToDraw = true;
-                for (ChildView v : mChildren) {
-                    v.mView.setVisibility(View.VISIBLE);
-                }
-            }
-        });
-    }
-
-    ChildView hitTest(int contentX, int contentY) {
-        if (mHidden) {
-            return null;
-        }
-        for (ChildView v : mChildren) {
-            if (v.mView.getVisibility() == View.VISIBLE) {
-                if (contentX >= v.x && contentX < (v.x + v.width)
-                        && contentY >= v.y && contentY < (v.y + v.height)) {
-                    return v;
-                }
-            }
-        }
-        return null;
-    }
-}
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
deleted file mode 100644
index 1d44b96..0000000
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2011 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.graphics.Point;
-import android.webkit.WebViewCore.DrawData;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * @hide
- */
-class ViewStateSerializer {
-
-    private static final int WORKING_STREAM_STORAGE = 16 * 1024;
-
-    // VERSION = 1 was for pictures encoded using a previous copy of libskia
-    static final int VERSION = 2;
-
-    static boolean serializeViewState(OutputStream stream, DrawData draw)
-            throws IOException {
-        int baseLayer = draw.mBaseLayer;
-        if (baseLayer == 0) {
-            return false;
-        }
-        DataOutputStream dos = new DataOutputStream(stream);
-        dos.writeInt(VERSION);
-        dos.writeInt(draw.mContentSize.x);
-        dos.writeInt(draw.mContentSize.y);
-        return nativeSerializeViewState(baseLayer, dos,
-                new byte[WORKING_STREAM_STORAGE]);
-    }
-
-    static DrawData deserializeViewState(InputStream stream)
-            throws IOException {
-        DataInputStream dis = new DataInputStream(stream);
-        int version = dis.readInt();
-        if (version > VERSION) {
-            throw new IOException("Unexpected version: " + version);
-        }
-        int contentWidth = dis.readInt();
-        int contentHeight = dis.readInt();
-        int baseLayer = nativeDeserializeViewState(version, dis,
-                new byte[WORKING_STREAM_STORAGE]);
-
-        final WebViewCore.DrawData draw = new WebViewCore.DrawData();
-        draw.mViewState = new WebViewCore.ViewState();
-        draw.mContentSize = new Point(contentWidth, contentHeight);
-        draw.mBaseLayer = baseLayer;
-        stream.close();
-        return draw;
-    }
-
-    public static void dumpLayerHierarchy(int baseLayer, OutputStream out, int level) {
-        nativeDumpLayerHierarchy(baseLayer, level, out,
-                new byte[WORKING_STREAM_STORAGE]);
-    }
-
-
-    private static native void nativeDumpLayerHierarchy(int baseLayer, int level,
-            OutputStream out, byte[] storage);
-
-    private static native boolean nativeSerializeViewState(int baseLayer,
-            OutputStream stream, byte[] storage);
-
-    // Returns a pointer to the BaseLayer
-    private static native int nativeDeserializeViewState(int version,
-            InputStream stream, byte[] storage);
-
-    private ViewStateSerializer() {}
-}
diff --git a/core/java/android/webkit/WebBackForwardListClassic.java b/core/java/android/webkit/WebBackForwardListClassic.java
deleted file mode 100644
index 2a14e6b..0000000
--- a/core/java/android/webkit/WebBackForwardListClassic.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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 java.io.Serializable;
-import java.util.ArrayList;
-
-/* package */ class WebBackForwardListClassic extends WebBackForwardList implements Cloneable,
-        Serializable {
-
-    // Current position in the list.
-    private int mCurrentIndex;
-    // ArrayList of WebHistoryItems for maintaining our copy.
-    private ArrayList<WebHistoryItemClassic> mArray;
-    // Flag to indicate that the list is invalid
-    private boolean mClearPending;
-    // CallbackProxy to issue client callbacks.
-    private final CallbackProxy mCallbackProxy;
-
-    /*package*/ WebBackForwardListClassic(CallbackProxy proxy) {
-        mCurrentIndex = -1;
-        mArray = new ArrayList<WebHistoryItemClassic>();
-        mCallbackProxy = proxy;
-    }
-
-    public synchronized WebHistoryItemClassic getCurrentItem() {
-        return getItemAtIndex(mCurrentIndex);
-    }
-
-    public synchronized int getCurrentIndex() {
-        return mCurrentIndex;
-    }
-
-    public synchronized WebHistoryItemClassic getItemAtIndex(int index) {
-        if (index < 0 || index >= getSize()) {
-            return null;
-        }
-        return mArray.get(index);
-    }
-
-    public synchronized int getSize() {
-        return mArray.size();
-    }
-
-    /**
-     * Mark the back/forward list as having a pending clear. This is used on the
-     * UI side to mark the list as being invalid during the clearHistory method.
-     */
-    /*package*/ synchronized void setClearPending() {
-        mClearPending = true;
-    }
-
-    /**
-     * Return the status of the clear flag. This is used on the UI side to
-     * determine if the list is valid for checking things like canGoBack.
-     */
-    /*package*/ synchronized boolean getClearPending() {
-        return mClearPending;
-    }
-
-    /**
-     * Add a new history item to the list. This will remove all items after the
-     * current item and append the new item to the end of the list. Called from
-     * the WebCore thread only. Synchronized because the UI thread may be
-     * reading the array or the current index.
-     * @param item A new history item.
-     */
-    /*package*/ synchronized void addHistoryItem(WebHistoryItem item) {
-        // Update the current position because we are going to add the new item
-        // in that slot.
-        ++mCurrentIndex;
-        // If the current position is not at the end, remove all history items
-        // after the current item.
-        final int size = mArray.size();
-        final int newPos = mCurrentIndex;
-        if (newPos != size) {
-            for (int i = size - 1; i >= newPos; i--) {
-                final WebHistoryItem h = mArray.remove(i);
-            }
-        }
-        // Add the item to the list.
-        mArray.add((WebHistoryItemClassic) item);
-        if (mCallbackProxy != null) {
-            mCallbackProxy.onNewHistoryItem(item);
-        }
-    }
-
-    /**
-     * Clear the back/forward list. Called from the WebCore thread.
-     */
-    /*package*/ synchronized void close(int nativeFrame) {
-        // Clear the array first because nativeClose will call addHistoryItem
-        // with the current item.
-        mArray.clear();
-        mCurrentIndex = -1;
-        nativeClose(nativeFrame);
-        // Reset the clear flag
-        mClearPending = false;
-    }
-
-    /* Remove the item at the given index. Called by JNI only. */
-    private synchronized void removeHistoryItem(int index) {
-        // XXX: This is a special case. Since the callback is only triggered
-        // when removing the first item, we can assert that the index is 0.
-        // This lets us change the current index without having to query the
-        // native BackForwardList.
-        if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) {
-            throw new AssertionError();
-        }
-        final WebHistoryItem h = mArray.remove(index);
-        // XXX: If we ever add another callback for removing history items at
-        // any index, this will no longer be valid.
-        mCurrentIndex--;
-    }
-
-    public synchronized WebBackForwardListClassic clone() {
-        WebBackForwardListClassic l = new WebBackForwardListClassic(null);
-        if (mClearPending) {
-            // If a clear is pending, return a copy with only the current item.
-            l.addHistoryItem(getCurrentItem());
-            return l;
-        }
-        l.mCurrentIndex = mCurrentIndex;
-        int size = getSize();
-        l.mArray = new ArrayList<WebHistoryItemClassic>(size);
-        for (int i = 0; i < size; i++) {
-            // Add a copy of each WebHistoryItem
-            l.mArray.add(mArray.get(i).clone());
-        }
-        return l;
-    }
-
-    /**
-     * Set the new history index.
-     * @param newIndex The new history index.
-     */
-    /*package*/ synchronized void setCurrentIndex(int newIndex) {
-        mCurrentIndex = newIndex;
-        if (mCallbackProxy != null) {
-            mCallbackProxy.onIndexChanged(getItemAtIndex(newIndex), newIndex);
-        }
-    }
-
-    /**
-     * Restore the history index.
-     */
-    /*package*/ static native synchronized void restoreIndex(int nativeFrame,
-            int index);
-
-    /* Close the native list. */
-    private static native void nativeClose(int nativeFrame);
-}
diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java
deleted file mode 100644
index c27bb5f..0000000
--- a/core/java/android/webkit/WebCoreThreadWatchdog.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * 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.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.webkit.WebViewCore.EventHub;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-// A Runnable that will monitor if the WebCore thread is still
-// processing messages by pinging it every so often. It is safe
-// to call the public methods of this class from any thread.
-class WebCoreThreadWatchdog implements Runnable {
-
-    // A message with this id is sent by the WebCore thread to notify the
-    // Watchdog that the WebCore thread is still processing messages
-    // (i.e. everything is OK).
-    private static final int IS_ALIVE = 100;
-
-    // This message is placed in the Watchdog's queue and removed when we
-    // receive an IS_ALIVE. If it is ever processed, we consider the
-    // WebCore thread unresponsive.
-    private static final int TIMED_OUT = 101;
-
-    // Wait 10s after hearing back from the WebCore thread before checking it's still alive.
-    private static final int HEARTBEAT_PERIOD = 10 * 1000;
-
-    // If there's no callback from the WebCore thread for 30s, prompt the user the page has
-    // become unresponsive.
-    private static final int TIMEOUT_PERIOD = 30 * 1000;
-
-    // After the first timeout, use a shorter period before re-prompting the user.
-    private static final int SUBSEQUENT_TIMEOUT_PERIOD = 15 * 1000;
-
-    private Handler mWebCoreThreadHandler;
-    private Handler mHandler;
-    private boolean mPaused;
-
-    private Set<WebViewClassic> mWebViews;
-
-    private static WebCoreThreadWatchdog sInstance;
-
-    public synchronized static WebCoreThreadWatchdog start(Handler webCoreThreadHandler) {
-        if (sInstance == null) {
-            sInstance = new WebCoreThreadWatchdog(webCoreThreadHandler);
-            new Thread(sInstance, "WebCoreThreadWatchdog").start();
-        }
-        return sInstance;
-    }
-
-    public synchronized static void registerWebView(WebViewClassic w) {
-        if (sInstance != null) {
-            sInstance.addWebView(w);
-        }
-    }
-
-    public synchronized static void unregisterWebView(WebViewClassic w) {
-        if (sInstance != null) {
-            sInstance.removeWebView(w);
-        }
-    }
-
-    public synchronized static void pause() {
-        if (sInstance != null) {
-            sInstance.pauseWatchdog();
-        }
-    }
-
-    public synchronized static void resume() {
-        if (sInstance != null) {
-            sInstance.resumeWatchdog();
-        }
-    }
-
-    private void addWebView(WebViewClassic w) {
-        if (mWebViews == null) {
-            mWebViews = new HashSet<WebViewClassic>();
-        }
-        mWebViews.add(w);
-    }
-
-    private void removeWebView(WebViewClassic w) {
-        mWebViews.remove(w);
-    }
-
-    private WebCoreThreadWatchdog(Handler webCoreThreadHandler) {
-        mWebCoreThreadHandler = webCoreThreadHandler;
-    }
-
-    private void pauseWatchdog() {
-        mPaused = true;
-
-        if (mHandler == null) {
-            return;
-        }
-
-        mHandler.removeMessages(TIMED_OUT);
-        mHandler.removeMessages(IS_ALIVE);
-        mWebCoreThreadHandler.removeMessages(EventHub.HEARTBEAT);
-    }
-
-    private void resumeWatchdog() {
-        if (!mPaused) {
-            // Do nothing if we get a call to resume without being paused.
-            // This can happen during the initialisation of the WebView.
-            return;
-        }
-
-        mPaused = false;
-
-        if (mHandler == null) {
-            return;
-        }
-
-        mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT,
-                mHandler.obtainMessage(IS_ALIVE)).sendToTarget();
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
-    }
-
-    private void createHandler() {
-        synchronized (WebCoreThreadWatchdog.class) {
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                    case IS_ALIVE:
-                        synchronized(WebCoreThreadWatchdog.class) {
-                            if (mPaused) {
-                                return;
-                            }
-
-                            // The WebCore thread still seems alive. Reset the countdown timer.
-                            removeMessages(TIMED_OUT);
-                            sendMessageDelayed(obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
-                            mWebCoreThreadHandler.sendMessageDelayed(
-                                    mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT,
-                                            mHandler.obtainMessage(IS_ALIVE)),
-                                    HEARTBEAT_PERIOD);
-                        }
-                        break;
-
-                    case TIMED_OUT:
-                        boolean postedDialog = false;
-                        synchronized (WebCoreThreadWatchdog.class) {
-                            Iterator<WebViewClassic> it = mWebViews.iterator();
-                            // Check each WebView we are aware of and find one that is capable of
-                            // showing the user a prompt dialog.
-                            while (it.hasNext()) {
-                                WebView activeView = it.next().getWebView();
-
-                                if (activeView.getWindowToken() != null &&
-                                        activeView.getViewRootImpl() != null) {
-                                    postedDialog = activeView.post(new PageNotRespondingRunnable(
-                                            activeView.getContext(), this));
-
-                                    if (postedDialog) {
-                                        // We placed the message into the UI thread for an attached
-                                        // WebView so we've made our best attempt to display the
-                                        // "page not responding" dialog to the user. Although the
-                                        // message is in the queue, there is no guarantee when/if
-                                        // the runnable will execute. In the case that the runnable
-                                        // never executes, the user will need to terminate the
-                                        // process manually.
-                                        break;
-                                    }
-                                }
-                            }
-
-                            if (!postedDialog) {
-                                // There's no active webview we can use to show the dialog, so
-                                // wait again. If we never get a usable view, the user will
-                                // never get the chance to terminate the process, and will
-                                // need to do it manually.
-                                sendMessageDelayed(obtainMessage(TIMED_OUT),
-                                        SUBSEQUENT_TIMEOUT_PERIOD);
-                            }
-                        }
-                        break;
-                    }
-                }
-            };
-        }
-    }
-
-    @Override
-    public void run() {
-        Looper.prepare();
-
-        createHandler();
-
-        // Send the initial control to WebViewCore and start the timeout timer as long as we aren't
-        // paused.
-        synchronized (WebCoreThreadWatchdog.class) {
-            if (!mPaused) {
-                mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT,
-                        mHandler.obtainMessage(IS_ALIVE)).sendToTarget();
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
-            }
-        }
-
-        Looper.loop();
-    }
-
-    private class PageNotRespondingRunnable implements Runnable {
-        Context mContext;
-        private Handler mWatchdogHandler;
-
-        public PageNotRespondingRunnable(Context context, Handler watchdogHandler) {
-            mContext = context;
-            mWatchdogHandler = watchdogHandler;
-        }
-
-        @Override
-        public void run() {
-            // This must run on the UI thread as it is displaying an AlertDialog.
-            assert Looper.getMainLooper().getThread() == Thread.currentThread();
-            new AlertDialog.Builder(mContext)
-                    .setMessage(com.android.internal.R.string.webpage_unresponsive)
-                    .setPositiveButton(com.android.internal.R.string.force_close,
-                            new DialogInterface.OnClickListener() {
-                                @Override
-                                public void onClick(DialogInterface dialog, int which) {
-                                    // User chose to force close.
-                                    Process.killProcess(Process.myPid());
-                                }
-                            })
-                    .setNegativeButton(com.android.internal.R.string.wait,
-                            new DialogInterface.OnClickListener() {
-                                @Override
-                                public void onClick(DialogInterface dialog, int which) {
-                                    // The user chose to wait. The last HEARTBEAT message
-                                    // will still be in the WebCore thread's queue, so all
-                                    // we need to do is post another TIMED_OUT so that the
-                                    // user will get prompted again if the WebCore thread
-                                    // doesn't sort itself out.
-                                    mWatchdogHandler.sendMessageDelayed(
-                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
-                                            SUBSEQUENT_TIMEOUT_PERIOD);
-                                }
-                            })
-                    .setOnCancelListener(
-                            new DialogInterface.OnCancelListener() {
-                                @Override
-                                public void onCancel(DialogInterface dialog) {
-                                    mWatchdogHandler.sendMessageDelayed(
-                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
-                                            SUBSEQUENT_TIMEOUT_PERIOD);
-                                }
-                            })
-                    .setIconAttribute(android.R.attr.alertDialogIcon)
-                    .show();
-        }
-    }
-}
diff --git a/core/java/android/webkit/WebHistoryItemClassic.java b/core/java/android/webkit/WebHistoryItemClassic.java
deleted file mode 100644
index 1620fbf..0000000
--- a/core/java/android/webkit/WebHistoryItemClassic.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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.graphics.Bitmap;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/* package */ class WebHistoryItemClassic extends WebHistoryItem implements Cloneable {
-    // Global identifier count.
-    private static int sNextId = 0;
-    // Unique identifier.
-    private final int mId;
-    // A point to a native WebHistoryItem instance which contains the actual data
-    private int mNativeBridge;
-    // The favicon for this item.
-    private Bitmap mFavicon;
-    // The pre-flattened data used for saving the state.
-    private byte[] mFlattenedData;
-    // The apple-touch-icon url for use when adding the site to the home screen,
-    // as obtained from a <link> element in the page.
-    private String mTouchIconUrlFromLink;
-    // If no <link> is specified, this holds the default location of the
-    // apple-touch-icon.
-    private String mTouchIconUrlServerDefault;
-    // Custom client data that is not flattened or read by native code.
-    private Object mCustomData;
-
-    /**
-     * Basic constructor that assigns a unique id to the item. Called by JNI
-     * only.
-     */
-    private WebHistoryItemClassic(int nativeBridge) {
-        synchronized (WebHistoryItemClassic.class) {
-            mId = sNextId++;
-        }
-        mNativeBridge = nativeBridge;
-        nativeRef(mNativeBridge);
-    }
-
-    protected void finalize() throws Throwable {
-        if (mNativeBridge != 0) {
-            nativeUnref(mNativeBridge);
-            mNativeBridge = 0;
-        }
-    }
-
-    /**
-     * Construct a new WebHistoryItem with initial flattened data.
-     * @param data The pre-flattened data coming from restoreState.
-     */
-    /*package*/ WebHistoryItemClassic(byte[] data) {
-        mFlattenedData = data;
-        synchronized (WebHistoryItemClassic.class) {
-            mId = sNextId++;
-        }
-    }
-
-    /**
-     * Construct a clone of a WebHistoryItem from the given item.
-     * @param item The history item to clone.
-     */
-    private WebHistoryItemClassic(WebHistoryItemClassic item) {
-        mFlattenedData = item.mFlattenedData;
-        mId = item.mId;
-        mFavicon = item.mFavicon;
-        mNativeBridge = item.mNativeBridge;
-        if (mNativeBridge != 0) {
-            nativeRef(mNativeBridge);
-        }
-    }
-
-    @Deprecated
-    public int getId() {
-        return mId;
-    }
-
-    public String getUrl() {
-        if (mNativeBridge == 0) return null;
-        return nativeGetUrl(mNativeBridge);
-    }
-
-    public String getOriginalUrl() {
-        if (mNativeBridge == 0) return null;
-        return nativeGetOriginalUrl(mNativeBridge);
-    }
-
-    public String getTitle() {
-        if (mNativeBridge == 0) return null;
-        return nativeGetTitle(mNativeBridge);
-    }
-
-    public Bitmap getFavicon() {
-        if (mFavicon == null && mNativeBridge != 0) {
-            mFavicon = nativeGetFavicon(mNativeBridge);
-        }
-        return mFavicon;
-    }
-
-    /**
-     * Return the touch icon url.
-     * If no touch icon <link> tag was specified, returns
-     * <host>/apple-touch-icon.png. The DownloadTouchIcon class that
-     * attempts to retrieve the touch icon will handle the case where
-     * that file does not exist. An icon set by a <link> tag is always
-     * used in preference to an icon saved on the server.
-     * @hide
-     */
-    public String getTouchIconUrl() {
-        if (mTouchIconUrlFromLink != null) {
-            return mTouchIconUrlFromLink;
-        } else if (mTouchIconUrlServerDefault != null) {
-            return mTouchIconUrlServerDefault;
-        }
-
-        try {
-            URL url = new URL(getOriginalUrl());
-            mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(),
-                    "/apple-touch-icon.png").toString();
-        } catch (MalformedURLException e) {
-            return null;
-        }
-        return mTouchIconUrlServerDefault;
-    }
-
-    /**
-     * Return the custom data provided by the client.
-     * @hide
-     */
-    public Object getCustomData() {
-        return mCustomData;
-    }
-
-    /**
-     * Set the custom data field.
-     * @param data An Object containing any data the client wishes to associate
-     *             with the item.
-     * @hide
-     */
-    public void setCustomData(Object data) {
-        // NOTE: WebHistoryItems are used in multiple threads. However, the
-        // public facing apis are all getters with the exception of this one
-        // api. Since this api is exclusive to clients, we don't make any
-        // promises about thread safety.
-        mCustomData = data;
-    }
-
-    /**
-     * Set the favicon.
-     * @param icon A Bitmap containing the favicon for this history item.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
-     */
-    /*package*/ void setFavicon(Bitmap icon) {
-        mFavicon = icon;
-    }
-
-    /**
-     * Set the touch icon url. Will not overwrite an icon that has been
-     * set already from a <link> tag, unless the new icon is precomposed.
-     * @hide
-     */
-    /*package*/ void setTouchIconUrl(String url, boolean precomposed) {
-        if (precomposed || mTouchIconUrlFromLink == null) {
-            mTouchIconUrlFromLink = url;
-        }
-    }
-
-    /**
-     * Get the pre-flattened data.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
-     */
-    /*package*/ byte[] getFlattenedData() {
-        if (mNativeBridge != 0) {
-            return nativeGetFlattenedData(mNativeBridge);
-        }
-        return mFlattenedData;
-    }
-
-    /**
-     * Inflate this item.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
-     */
-    /*package*/ void inflate(int nativeFrame) {
-        mNativeBridge = inflate(nativeFrame, mFlattenedData);
-        mFlattenedData = null;
-    }
-
-    public synchronized WebHistoryItemClassic clone() {
-        return new WebHistoryItemClassic(this);
-    }
-
-    /* Natively inflate this item, this method is called in the WebCore thread.
-     */
-    private native int inflate(int nativeFrame, byte[] data);
-    private native void nativeRef(int nptr);
-    private native void nativeUnref(int nptr);
-    private native String nativeGetTitle(int nptr);
-    private native String nativeGetUrl(int nptr);
-    private native String nativeGetOriginalUrl(int nptr);
-    private native byte[] nativeGetFlattenedData(int nptr);
-    private native Bitmap nativeGetFavicon(int nptr);
-
-}
diff --git a/core/java/android/webkit/WebIconDatabaseClassic.java b/core/java/android/webkit/WebIconDatabaseClassic.java
deleted file mode 100644
index d6c4c33..0000000
--- a/core/java/android/webkit/WebIconDatabaseClassic.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * 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.ContentResolver;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Browser;
-import android.util.Log;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Vector;
-
-class WebIconDatabaseClassic extends WebIconDatabase {
-    private static final String LOGTAG = "WebIconDatabase";
-    // Global instance of a WebIconDatabase
-    private static WebIconDatabaseClassic sIconDatabase;
-    // EventHandler for handling messages before and after the WebCore thread is
-    // ready.
-    private final EventHandler mEventHandler = new EventHandler();
-
-    // Class to handle messages before WebCore is ready
-    private static class EventHandler extends Handler {
-        // Message ids
-        static final int OPEN         = 0;
-        static final int CLOSE        = 1;
-        static final int REMOVE_ALL   = 2;
-        static final int REQUEST_ICON = 3;
-        static final int RETAIN_ICON  = 4;
-        static final int RELEASE_ICON = 5;
-        static final int BULK_REQUEST_ICON = 6;
-        // Message for dispatching icon request results
-        private static final int ICON_RESULT = 10;
-        // Actual handler that runs in WebCore thread
-        private Handler mHandler;
-        // Vector of messages before the WebCore thread is ready
-        private Vector<Message> mMessages = new Vector<Message>();
-        // Class to handle a result dispatch
-        private class IconResult {
-            private final String mUrl;
-            private final Bitmap mIcon;
-            private final IconListener mListener;
-            IconResult(String url, Bitmap icon, IconListener l) {
-                mUrl = url;
-                mIcon = icon;
-                mListener = l;
-            }
-            void dispatch() {
-                mListener.onReceivedIcon(mUrl, mIcon);
-            }
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            // Note: This is the message handler for the UI thread.
-            switch (msg.what) {
-                case ICON_RESULT:
-                    ((IconResult) msg.obj).dispatch();
-                    break;
-            }
-        }
-
-        // Called by WebCore thread to create the actual handler
-        private synchronized void createHandler() {
-            if (mHandler == null) {
-                mHandler = new Handler() {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        // Note: This is the message handler for the WebCore
-                        // thread.
-                        switch (msg.what) {
-                            case OPEN:
-                                nativeOpen((String) msg.obj);
-                                break;
-
-                            case CLOSE:
-                                nativeClose();
-                                break;
-
-                            case REMOVE_ALL:
-                                nativeRemoveAllIcons();
-                                break;
-
-                            case REQUEST_ICON:
-                                IconListener l = (IconListener) msg.obj;
-                                String url = msg.getData().getString("url");
-                                requestIconAndSendResult(url, l);
-                                break;
-
-                            case BULK_REQUEST_ICON:
-                                bulkRequestIcons(msg);
-                                break;
-
-                            case RETAIN_ICON:
-                                nativeRetainIconForPageUrl((String) msg.obj);
-                                break;
-
-                            case RELEASE_ICON:
-                                nativeReleaseIconForPageUrl((String) msg.obj);
-                                break;
-                        }
-                    }
-                };
-                // Transfer all pending messages
-                for (int size = mMessages.size(); size > 0; size--) {
-                    mHandler.sendMessage(mMessages.remove(0));
-                }
-                mMessages = null;
-            }
-        }
-
-        private synchronized boolean hasHandler() {
-            return mHandler != null;
-        }
-
-        private synchronized void postMessage(Message msg) {
-            if (mMessages != null) {
-                mMessages.add(msg);
-            } else {
-                mHandler.sendMessage(msg);
-            }
-        }
-
-        private void bulkRequestIcons(Message msg) {
-            HashMap map = (HashMap) msg.obj;
-            IconListener listener = (IconListener) map.get("listener");
-            ContentResolver cr = (ContentResolver) map.get("contentResolver");
-            String where = (String) map.get("where");
-
-            Cursor c = null;
-            try {
-                c = cr.query(
-                        Browser.BOOKMARKS_URI,
-                        new String[] { Browser.BookmarkColumns.URL },
-                        where, null, null);
-                if (c.moveToFirst()) {
-                    do {
-                        String url = c.getString(0);
-                        requestIconAndSendResult(url, listener);
-                    } while (c.moveToNext());
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "BulkRequestIcons", e);
-            } finally {
-                if (c != null) c.close();
-            }
-        }
-
-        private void requestIconAndSendResult(String url, IconListener listener) {
-            Bitmap icon = nativeIconForPageUrl(url);
-            if (icon != null) {
-                sendMessage(obtainMessage(ICON_RESULT,
-                            new IconResult(url, icon, listener)));
-            }
-        }
-    }
-
-    @Override
-    public void open(String path) {
-        if (path != null) {
-            // Make the directories and parents if they don't exist
-            File db = new File(path);
-            if (!db.exists()) {
-                db.mkdirs();
-            }
-            mEventHandler.postMessage(
-                    Message.obtain(null, EventHandler.OPEN, db.getAbsolutePath()));
-        }
-    }
-
-    @Override
-    public void close() {
-        mEventHandler.postMessage(
-                Message.obtain(null, EventHandler.CLOSE));
-    }
-
-    @Override
-    public void removeAllIcons() {
-        mEventHandler.postMessage(
-                Message.obtain(null, EventHandler.REMOVE_ALL));
-    }
-
-    /**
-     * Request the Bitmap representing the icon for the given page
-     * url. If the icon exists, the listener will be called with the result.
-     * @param url The page's url.
-     * @param listener An implementation on IconListener to receive the result.
-     */
-    public void requestIconForPageUrl(String url, IconListener listener) {
-        if (listener == null || url == null) {
-            return;
-        }
-        Message msg = Message.obtain(null, EventHandler.REQUEST_ICON, listener);
-        msg.getData().putString("url", url);
-        mEventHandler.postMessage(msg);
-    }
-
-    /** {@hide}
-     */
-    public void bulkRequestIconForPageUrl(ContentResolver cr, String where,
-            IconListener listener) {
-        if (listener == null) {
-            return;
-        }
-
-        // Special case situation: we don't want to add this message to the
-        // queue if there is no handler because we may never have a real
-        // handler to service the messages and the cursor will never get
-        // closed.
-        if (mEventHandler.hasHandler()) {
-            // Don't use Bundle as it is parcelable.
-            HashMap<String, Object> map = new HashMap<String, Object>();
-            map.put("contentResolver", cr);
-            map.put("where", where);
-            map.put("listener", listener);
-            Message msg =
-                    Message.obtain(null, EventHandler.BULK_REQUEST_ICON, map);
-            mEventHandler.postMessage(msg);
-        }
-    }
-
-    @Override
-    public void retainIconForPageUrl(String url) {
-        if (url != null) {
-            mEventHandler.postMessage(
-                    Message.obtain(null, EventHandler.RETAIN_ICON, url));
-        }
-    }
-
-    @Override
-    public void releaseIconForPageUrl(String url) {
-        if (url != null) {
-            mEventHandler.postMessage(
-                    Message.obtain(null, EventHandler.RELEASE_ICON, url));
-        }
-    }
-
-    /**
-     * Get the global instance of WebIconDatabase.
-     * @return A single instance of WebIconDatabase. It will be the same
-     *         instance for the current process each time this method is
-     *         called.
-     */
-    public static WebIconDatabaseClassic getInstance() {
-        // XXX: Must be created in the UI thread.
-        if (sIconDatabase == null) {
-            sIconDatabase = new WebIconDatabaseClassic();
-        }
-        return sIconDatabase;
-    }
-
-    /**
-     * Create the internal handler and transfer all pending messages.
-     * XXX: Called by WebCore thread only!
-     */
-    /*package*/ void createHandler() {
-        mEventHandler.createHandler();
-    }
-
-    /**
-     * Private constructor to avoid anyone else creating an instance.
-     */
-    private WebIconDatabaseClassic() {}
-
-    // Native functions
-    private static native void nativeOpen(String path);
-    private static native void nativeClose();
-    private static native void nativeRemoveAllIcons();
-    private static native Bitmap nativeIconForPageUrl(String url);
-    private static native void nativeRetainIconForPageUrl(String url);
-    private static native void nativeReleaseIconForPageUrl(String url);
-}
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
deleted file mode 100644
index c10a429..0000000
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ /dev/null
@@ -1,1744 +0,0 @@
-/*
- * 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.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-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.1.1";
-
-    // 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         mAllowUniversalAccessFromFileURLs = false;
-    private boolean         mAllowFileAccessFromFileURLs = 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;
-    private boolean         mLinkPrefetchEnabled = 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         mMediaPlaybackRequiresUserGesture = 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;
-    private boolean         mPasswordEchoEnabled = true;
-
-    // AutoFill Profile data
-    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;
-
-        // SDK specific settings. See issue 6212665
-        if (mContext.getApplicationInfo().targetSdkVersion <
-                Build.VERSION_CODES.JELLY_BEAN) {
-            mAllowUniversalAccessFromFileURLs = true;
-            mAllowFileAccessFromFileURLs = true;
-        }
-        try {
-            mPasswordEchoEnabled =
-                    Settings.System.getInt(context.getContentResolver(),
-                        Settings.System.TEXT_SHOW_PASSWORD) != 0;
-        } catch (SettingNotFoundException e) {
-            mPasswordEchoEnabled = true;
-        }
-    }
-
-    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;
-        }
-        return getDefaultUserAgentForLocale(mContext, locale);
-    }
-
-    /**
-     * Returns the default User-Agent used by a WebView.
-     * An instance of WebView could use a different User-Agent if a call
-     * is made to {@link WebSettings#setUserAgent(int)} or
-     * {@link WebSettings#setUserAgentString(String)}.
-     *
-     * @param context a Context object used to access application assets
-     * @param locale The Locale to use in the User-Agent string.
-     * @see WebViewFactoryProvider#getDefaultUserAgent(Context)
-     * @see WebView#getDefaultUserAgent(Context)
-     */
-    public static String getDefaultUserAgentForLocale(Context context, Locale locale) {
-        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 = context.getResources().getText(
-            com.android.internal.R.string.web_user_agent_target_content).toString();
-        final String base = context.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#setMediaPlaybackRequiresUserGesture(boolean)
-     */
-    @Override
-    public void setMediaPlaybackRequiresUserGesture(boolean support) {
-        if (mMediaPlaybackRequiresUserGesture != support) {
-            mMediaPlaybackRequiresUserGesture = support;
-            postSync();
-        }
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getMediaPlaybackRequiresUserGesture()
-     */
-    @Override
-    public boolean getMediaPlaybackRequiresUserGesture() {
-        return mMediaPlaybackRequiresUserGesture;
-    }
-
-    /**
-     * @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) {
-            mTextSize = textZoom;
-            postSync();
-        }
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getTextZoom()
-     */
-    @Override
-    public synchronized int getTextZoom() {
-        return mTextSize;
-    }
-
-    /**
-     * 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.
-     */
-    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.
-     */
-    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) {
-        if (l == LayoutAlgorithm.TEXT_AUTOSIZING) {
-            throw new IllegalArgumentException(
-                    "WebViewClassic does not support TEXT_AUTOSIZING layout mode");
-        }
-        // 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).
-     */
-    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();
-            mWebView.updateJavaScriptEnabled(flag);
-        }
-    }
-
-    /**
-     * @see android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs
-     */
-    @Override
-    public synchronized void setAllowUniversalAccessFromFileURLs(boolean flag) {
-        if (mAllowUniversalAccessFromFileURLs != flag) {
-            mAllowUniversalAccessFromFileURLs = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @see android.webkit.WebSettings#setAllowFileAccessFromFileURLs
-     */
-    @Override
-    public synchronized void setAllowFileAccessFromFileURLs(boolean flag) {
-        if (mAllowFileAccessFromFileURLs != flag) {
-            mAllowFileAccessFromFileURLs = 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
-     */
-    public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
-        if (mHardwareAccelSkia != flag) {
-            mHardwareAccelSkia = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is using hardware accelerated skia
-     */
-    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
-     */
-    public synchronized void setShowVisualIndicator(boolean flag) {
-        if (mShowVisualIndicator != flag) {
-            mShowVisualIndicator = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @return True if the WebView is showing the visual indicator
-     */
-    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.
-     */
-    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.
-     * Only used by LayoutTestController.
-     * @param flag Whether XSS Auditor should be enabled.
-     */
-    public synchronized void setXSSAuditorEnabled(boolean flag) {
-        if (mXSSAuditorEnabled != flag) {
-            mXSSAuditorEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * Enables/disables HTML5 link "prefetch" parameter.
-     */
-    public synchronized void setLinkPrefetchEnabled(boolean flag) {
-        if (mLinkPrefetchEnabled != flag) {
-            mLinkPrefetchEnabled = flag;
-            postSync();
-        }
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getJavaScriptEnabled()
-     */
-    @Override
-    public synchronized boolean getJavaScriptEnabled() {
-        return mJavaScriptEnabled;
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getAllowUniversalFileAccessFromFileURLs
-     */
-    @Override
-    public synchronized boolean getAllowUniversalAccessFromFileURLs() {
-        return mAllowUniversalAccessFromFileURLs;
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getAllowFileAccessFromFileURLs
-     */
-    @Override
-    public synchronized boolean getAllowFileAccessFromFileURLs() {
-        return mAllowFileAccessFromFileURLs;
-    }
-
-    /**
-     * @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.
-     */
-    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.
-     */
-    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
-     */
-    public boolean forceUserScalable() {
-        return mForceUserScalable;
-    }
-
-    /**
-     * Sets whether viewport metatag can disable zooming.
-     * @param flag Whether or not to forceably enable user scalable.
-     */
-    public synchronized void setForceUserScalable(boolean flag) {
-        mForceUserScalable = flag;
-    }
-
-    synchronized void setSyntheticLinksEnabled(boolean flag) {
-        if (mSyntheticLinksEnabled != flag) {
-            mSyntheticLinksEnabled = flag;
-            postSync();
-        }
-    }
-
-    public synchronized void setAutoFillEnabled(boolean enabled) {
-        // AutoFill is always disabled in private browsing mode.
-        boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled;
-        if (mAutoFillEnabled != autoFillEnabled) {
-            mAutoFillEnabled = autoFillEnabled;
-            postSync();
-        }
-    }
-
-    public synchronized boolean getAutoFillEnabled() {
-        return mAutoFillEnabled;
-    }
-
-    public synchronized void setAutoFillProfile(AutoFillProfile profile) {
-        if (mAutoFillProfile != profile) {
-            mAutoFillProfile = profile;
-            postSync();
-        }
-    }
-
-    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));
-        }
-    }
-
-    public void setProperty(String key, String value) {
-        if (mWebView.nativeSetProperty(key, value)) {
-            mWebView.invalidate();
-        }
-    }
-
-    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/WebStorageClassic.java b/core/java/android/webkit/WebStorageClassic.java
deleted file mode 100644
index 62de5e6..0000000
--- a/core/java/android/webkit/WebStorageClassic.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * 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.os.Handler;
-import android.os.Message;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/** @hide */
-public class WebStorageClassic extends WebStorage {
-    // Global instance of a WebStorage
-    private static WebStorageClassic sWebStorage;
-
-    // Message ids
-    static final int UPDATE = 0;
-    static final int SET_QUOTA_ORIGIN = 1;
-    static final int DELETE_ORIGIN = 2;
-    static final int DELETE_ALL = 3;
-    static final int GET_ORIGINS = 4;
-    static final int GET_USAGE_ORIGIN = 5;
-    static final int GET_QUOTA_ORIGIN = 6;
-
-    // Message ids on the UI thread
-    static final int RETURN_ORIGINS = 0;
-    static final int RETURN_USAGE_ORIGIN = 1;
-    static final int RETURN_QUOTA_ORIGIN = 2;
-
-    private static final String ORIGINS = "origins";
-    private static final String ORIGIN = "origin";
-    private static final String CALLBACK = "callback";
-    private static final String USAGE = "usage";
-    private static final String QUOTA = "quota";
-
-    private Map <String, Origin> mOrigins;
-
-    private Handler mHandler = null;
-    private Handler mUIHandler = null;
-
-    /**
-     * @hide
-     * Message handler, UI side
-     * @hide
-     */
-    public void createUIHandler() {
-        if (mUIHandler == null) {
-            mUIHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                        case RETURN_ORIGINS: {
-                            Map values = (Map) msg.obj;
-                            Map origins = (Map) values.get(ORIGINS);
-                            ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK);
-                            callback.onReceiveValue(origins);
-                            } break;
-
-                        case RETURN_USAGE_ORIGIN: {
-                            Map values = (Map) msg.obj;
-                            ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
-                            callback.onReceiveValue((Long)values.get(USAGE));
-                            } break;
-
-                        case RETURN_QUOTA_ORIGIN: {
-                            Map values = (Map) msg.obj;
-                            ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
-                            callback.onReceiveValue((Long)values.get(QUOTA));
-                            } break;
-                    }
-                }
-            };
-        }
-    }
-
-    /**
-     * Message handler, WebCore side
-     * @hide
-     */
-    public synchronized void createHandler() {
-        if (mHandler == null) {
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                        case SET_QUOTA_ORIGIN: {
-                            Origin website = (Origin) msg.obj;
-                            nativeSetQuotaForOrigin(website.getOrigin(),
-                                                    website.getQuota());
-                            } break;
-
-                        case DELETE_ORIGIN: {
-                            Origin website = (Origin) msg.obj;
-                            nativeDeleteOrigin(website.getOrigin());
-                            } break;
-
-                        case DELETE_ALL:
-                            nativeDeleteAllData();
-                            break;
-
-                        case GET_ORIGINS: {
-                            syncValues();
-                            ValueCallback callback = (ValueCallback) msg.obj;
-                            Map origins = new HashMap(mOrigins);
-                            Map values = new HashMap<String, Object>();
-                            values.put(CALLBACK, callback);
-                            values.put(ORIGINS, origins);
-                            postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
-                            } break;
-
-                        case GET_USAGE_ORIGIN: {
-                            syncValues();
-                            Map values = (Map) msg.obj;
-                            String origin = (String) values.get(ORIGIN);
-                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
-                            Origin website = mOrigins.get(origin);
-                            Map retValues = new HashMap<String, Object>();
-                            retValues.put(CALLBACK, callback);
-                            if (website != null) {
-                                long usage = website.getUsage();
-                                retValues.put(USAGE, new Long(usage));
-                            }
-                            postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues));
-                            } break;
-
-                        case GET_QUOTA_ORIGIN: {
-                            syncValues();
-                            Map values = (Map) msg.obj;
-                            String origin = (String) values.get(ORIGIN);
-                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
-                            Origin website = mOrigins.get(origin);
-                            Map retValues = new HashMap<String, Object>();
-                            retValues.put(CALLBACK, callback);
-                            if (website != null) {
-                                long quota = website.getQuota();
-                                retValues.put(QUOTA, new Long(quota));
-                            }
-                            postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues));
-                            } break;
-
-                        case UPDATE:
-                            syncValues();
-                            break;
-                    }
-                }
-            };
-        }
-    }
-
-    /*
-     * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(),
-     * we need to get the values from WebCore, but we cannot block while doing so
-     * as we used to do, as this could result in a full deadlock (other WebCore
-     * messages received while we are still blocked here, see http://b/2127737).
-     *
-     * We have to do everything asynchronously, by providing a callback function.
-     * We post a message on the WebCore thread (mHandler) that will get the result
-     * from WebCore, and we post it back on the UI thread (using mUIHandler).
-     * We can then use the callback function to return the value.
-     */
-
-    @Override
-    public void getOrigins(ValueCallback<Map> callback) {
-        if (callback != null) {
-            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-                syncValues();
-                callback.onReceiveValue(mOrigins);
-            } else {
-                postMessage(Message.obtain(null, GET_ORIGINS, callback));
-            }
-        }
-    }
-
-    /**
-     * Returns a list of origins having a database
-     * should only be called from WebViewCore.
-     */
-    Collection<Origin> getOriginsSync() {
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            update();
-            return mOrigins.values();
-        }
-        return null;
-    }
-
-    @Override
-    public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
-        if (callback == null) {
-            return;
-        }
-        if (origin == null) {
-            callback.onReceiveValue(null);
-            return;
-        }
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            syncValues();
-            Origin website = mOrigins.get(origin);
-            callback.onReceiveValue(new Long(website.getUsage()));
-        } else {
-            HashMap values = new HashMap<String, Object>();
-            values.put(ORIGIN, origin);
-            values.put(CALLBACK, callback);
-            postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values));
-        }
-    }
-
-    @Override
-    public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
-        if (callback == null) {
-            return;
-        }
-        if (origin == null) {
-            callback.onReceiveValue(null);
-            return;
-        }
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            syncValues();
-            Origin website = mOrigins.get(origin);
-            callback.onReceiveValue(new Long(website.getUsage()));
-        } else {
-            HashMap values = new HashMap<String, Object>();
-            values.put(ORIGIN, origin);
-            values.put(CALLBACK, callback);
-            postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values));
-        }
-    }
-
-    @Override
-    public void setQuotaForOrigin(String origin, long quota) {
-        if (origin != null) {
-            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-                nativeSetQuotaForOrigin(origin, quota);
-            } else {
-                postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
-                    new Origin(origin, quota)));
-            }
-        }
-    }
-
-    @Override
-    public void deleteOrigin(String origin) {
-        if (origin != null) {
-            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-                nativeDeleteOrigin(origin);
-            } else {
-                postMessage(Message.obtain(null, DELETE_ORIGIN,
-                    new Origin(origin)));
-            }
-        }
-    }
-
-    @Override
-    public void deleteAllData() {
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            nativeDeleteAllData();
-        } else {
-            postMessage(Message.obtain(null, DELETE_ALL));
-        }
-    }
-
-    /**
-     * Sets the maximum size of the ApplicationCache.
-     * This should only ever be called on the WebKit thread.
-     * Not part of the base-class API: this is only used by dump render tree.
-     */
-    public void setAppCacheMaximumSize(long size) {
-        nativeSetAppCacheMaximumSize(size);
-    }
-
-    /**
-     * Utility function to send a message to our handler
-     */
-    private synchronized void postMessage(Message msg) {
-        if (mHandler != null) {
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    /**
-     * Utility function to send a message to the handler on the UI thread
-     */
-    private void postUIMessage(Message msg) {
-        if (mUIHandler != null) {
-            mUIHandler.sendMessage(msg);
-        }
-    }
-
-    /**
-     * Get the singleton instance of this class.
-     * @return The singleton {@link WebStorage} instance.
-     */
-    public static WebStorageClassic getInstance() {
-      if (sWebStorage == null) {
-          sWebStorage = new WebStorageClassic();
-      }
-      return sWebStorage;
-    }
-
-    /**
-     * @hide
-     * Post a Sync request
-     */
-    public void update() {
-        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
-            syncValues();
-        } else {
-            postMessage(Message.obtain(null, UPDATE));
-        }
-    }
-
-    /**
-     * Run on the WebCore thread
-     * set the local values with the current ones
-     */
-    private void syncValues() {
-        Set<String> tmp = nativeGetOrigins();
-        mOrigins = new HashMap<String, Origin>();
-        for (String origin : tmp) {
-            Origin website = new Origin(origin,
-                                 nativeGetQuotaForOrigin(origin),
-                                 nativeGetUsageForOrigin(origin));
-            mOrigins.put(origin, website);
-        }
-    }
-
-    WebStorageClassic() {}
-
-    // Native functions
-    private static native Set nativeGetOrigins();
-    private static native long nativeGetUsageForOrigin(String origin);
-    private static native long nativeGetQuotaForOrigin(String origin);
-    private static native void nativeSetQuotaForOrigin(String origin, long quota);
-    private static native void nativeDeleteOrigin(String origin);
-    private static native void nativeDeleteAllData();
-    private static native void nativeSetAppCacheMaximumSize(long size);
-}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
deleted file mode 100644
index 911073d..0000000
--- a/core/java/android/webkit/WebTextView.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007 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.util.Log;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-// TODO: Move these to a better place.
-/* package */ abstract class WebTextView {
-
-    private static final String LOGTAG = "WebTextView";
-
-    // Types used with setType.  Keep in sync with CachedInput.h
-    static final int NORMAL_TEXT_FIELD = 0;
-    static final int TEXT_AREA = 1;
-    static final int PASSWORD = 2;
-    static final int SEARCH = 3;
-    static final int EMAIL = 4;
-    static final int NUMBER = 5;
-    static final int TELEPHONE = 6;
-    static final int URL = 7;
-
-    static final int FORM_NOT_AUTOFILLABLE = -1;
-
-    static String urlForAutoCompleteData(String urlString) {
-        // Remove any fragment or query string.
-        URL url = null;
-        try {
-            url = new URL(urlString);
-        } catch (MalformedURLException e) {
-            Log.e(LOGTAG, "Unable to parse URL "+url);
-        }
-
-        return url != null ? url.getProtocol() + "://" + url.getHost() + url.getPath() : null;
-    }
-
-}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
deleted file mode 100644
index 3c377d3..0000000
--- a/core/java/android/webkit/WebViewClassic.java
+++ /dev/null
@@ -1,8810 +0,0 @@
-/*
- * 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.accessibilityservice.AccessibilityServiceInfo;
-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.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.PointF;
-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.Build;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.print.PrintDocumentAdapter;
-import android.security.KeyChain;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.Log;
-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.ViewRootImpl;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-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.TextSelectionData;
-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.Scroller;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import junit.framework.Assert;
-
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-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.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-/**
- * Implements a backend provider for the {@link WebView} public API.
- * @hide
- */
-// 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
-@SuppressWarnings("deprecation")
-public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
-        WebViewProvider.ViewDelegate {
-    /**
-     * InputConnection used for ContentEditable. This captures changes
-     * to the text and sends them either as key strokes or text changes.
-     */
-    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;
-        private boolean mIsAutoFillable;
-        private boolean mIsAutoCompleteEnabled;
-        private String mName;
-        private int mBatchLevel;
-
-        public WebViewInputConnection() {
-            super(mWebView, true);
-        }
-
-        public void setAutoFillable(int queryId) {
-            mIsAutoFillable = getSettings().getAutoFillEnabled()
-                    && (queryId != WebTextView.FORM_NOT_AUTOFILLABLE);
-            int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
-            if (variation != EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD
-                    && (mIsAutoFillable || mIsAutoCompleteEnabled)) {
-                if (mName != null && mName.length() > 0) {
-                    requestFormData(mName, mFieldPointer, mIsAutoFillable,
-                            mIsAutoCompleteEnabled);
-                }
-            }
-        }
-
-        @Override
-        public boolean beginBatchEdit() {
-            if (mBatchLevel == 0) {
-                beginTextBatch();
-            }
-            mBatchLevel++;
-            return false;
-        }
-
-        @Override
-        public boolean endBatchEdit() {
-            mBatchLevel--;
-            if (mBatchLevel == 0) {
-                commitTextBatch();
-            }
-            return false;
-        }
-
-        public boolean getIsAutoFillable() {
-            return mIsAutoFillable;
-        }
-
-        @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);
-            finishComposingText();
-        }
-
-        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);
-            updateSelection();
-            if (limitedText != text) {
-                int lastCaret = start + limitedText.length();
-                finishComposingText();
-                setSelection(lastCaret, lastCaret);
-            }
-            return true;
-        }
-
-        @Override
-        public boolean commitText(CharSequence text, int newCursorPosition) {
-            setComposingText(text, newCursorPosition);
-            finishComposingText();
-            return true;
-        }
-
-        @Override
-        public boolean deleteSurroundingText(int leftLength, int rightLength) {
-            // This code is from BaseInputConnection#deleteSurroundText.
-            // We have to delete the same text in webkit.
-            Editable content = getEditable();
-            int a = Selection.getSelectionStart(content);
-            int b = Selection.getSelectionEnd(content);
-
-            if (a > b) {
-                int tmp = a;
-                a = b;
-                b = tmp;
-            }
-
-            int ca = getComposingSpanStart(content);
-            int cb = getComposingSpanEnd(content);
-            if (cb < ca) {
-                int tmp = ca;
-                ca = cb;
-                cb = tmp;
-            }
-            if (ca != -1 && cb != -1) {
-                if (ca < a) a = ca;
-                if (cb > b) b = cb;
-            }
-
-            int endDelete = Math.min(content.length(), b + rightLength);
-            if (endDelete > b) {
-                setNewText(b, endDelete, "");
-            }
-            int startDelete = Math.max(0, a - leftLength);
-            if (startDelete < a) {
-                setNewText(startDelete, a, "");
-            }
-            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) {
-                if (initData.mIsTextFieldNext) {
-                    imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
-                }
-                if (initData.mIsTextFieldPrev) {
-                    imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
-                }
-            }
-            int action = EditorInfo.IME_ACTION_GO;
-            switch (type) {
-                case WebTextView.NORMAL_TEXT_FIELD:
-                    break;
-                case WebTextView.TEXT_AREA:
-                    inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
-                            | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
-                            | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
-                    action = EditorInfo.IME_ACTION_NONE;
-                    break;
-                case WebTextView.PASSWORD:
-                    inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-                    break;
-                case WebTextView.SEARCH:
-                    action = 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;
-                    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
-                    break;
-                case WebTextView.TELEPHONE:
-                    // inputType needs to be overwritten because of the different class.
-                    inputType = InputType.TYPE_CLASS_PHONE;
-                    break;
-                case WebTextView.URL:
-                    // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
-                    // exclude it for now.
-                    inputType |= InputType.TYPE_TEXT_VARIATION_URI;
-                    break;
-                default:
-                    break;
-            }
-            imeOptions |= action;
-            mHint = initData.mLabel;
-            mInputType = inputType;
-            mImeOptions = imeOptions;
-            mMaxLength = initData.mMaxLength;
-            mIsAutoCompleteEnabled = initData.mIsAutoCompleteEnabled;
-            mName = initData.mName;
-            mAutoCompletePopup.clearAdapter();
-        }
-
-        public void setupEditorInfo(EditorInfo outAttrs) {
-            outAttrs.inputType = mInputType;
-            outAttrs.imeOptions = mImeOptions;
-            outAttrs.hintText = mHint;
-            outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
-
-            Editable editable = getEditable();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            if (selectionStart < 0 || selectionEnd < 0) {
-                selectionStart = editable.length();
-                selectionEnd = selectionStart;
-            }
-            outAttrs.initialSelStart = selectionStart;
-            outAttrs.initialSelEnd = selectionEnd;
-        }
-
-        @Override
-        public boolean setSelection(int start, int end) {
-            boolean result = super.setSelection(start, end);
-            updateSelection();
-            return result;
-        }
-
-        @Override
-        public boolean setComposingRegion(int start, int end) {
-            boolean result = super.setComposingRegion(start, end);
-            updateSelection();
-            return result;
-        }
-
-        /**
-         * Send the selection and composing spans to the IME.
-         */
-        private void updateSelection() {
-            Editable editable = getEditable();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            int composingStart = getComposingSpanStart(editable);
-            int composingEnd = getComposingSpanEnd(editable);
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                imm.updateSelection(mWebView, selectionStart, selectionEnd,
-                        composingStart, composingEnd);
-            }
-        }
-
-        /**
-         * 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();
-            int selectionStart = Selection.getSelectionStart(editable);
-            int selectionEnd = Selection.getSelectionEnd(editable);
-            if (selectionStart == selectionEnd) {
-                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);
-            }
-            if (mAutoCompletePopup != null) {
-                StringBuilder newText = new StringBuilder();
-                newText.append(editable.subSequence(0, start));
-                newText.append(text);
-                newText.append(editable.subSequence(end, editable.length()));
-                mAutoCompletePopup.setText(newText.toString());
-            }
-            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(Point cursorBottom, Point cursorTop,
-                int windowLeft, int windowTop) {
-            measureContent();
-
-            int width = mContentView.getMeasuredWidth();
-            int height = mContentView.getMeasuredHeight();
-            int y = cursorTop.y - height;
-            int x = cursorTop.x - (width / 2);
-            if (y < windowTop) {
-                // There's not enough room vertically, move it below the
-                // handle.
-                ensureSelectionHandles();
-                y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight();
-                x = cursorBottom.x - (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));
-        }
-    }
-
-    // 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;
-
-    // The rate at which edit text is scrolled in content pixels per millisecond
-    static private final float TEXT_SCROLL_RATE = 0.01f;
-
-    // The presumed scroll rate for the first scroll of edit text
-    static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16;
-
-    // Buffer pixels of the caret rectangle when moving edit text into view
-    // after resize.
-    static private final int EDIT_RECT_BUFFER = 10;
-
-    static private final long SELECTION_HANDLE_ANIMATION_MS = 150;
-
-    // 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;
-
-    // Reference to the save password dialog so it can be dimissed in
-    // destroy if not done before.
-    private AlertDialog mSavePasswordDialog = null;
-
-    static final String LOGTAG = "webview";
-
-    private ZoomManager mZoomManager;
-
-    private final Rect mInvScreenRect = new Rect();
-    private final Rect mScreenRect = new Rect();
-    private final RectF mVisibleContentRect = new RectF();
-    private boolean mIsWebViewVisible = true;
-    WebViewInputConnection mInputConnection = null;
-    private int mFieldPointer;
-    private PastePopupWindow mPasteWindow;
-    private AutoCompletePopup mAutoCompletePopup;
-    Rect mEditTextContentBounds = new Rect();
-    Rect mEditTextContent = new Rect();
-    int mEditTextLayerId;
-    boolean mIsEditingText = false;
-    ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>();
-    boolean mIsBatchingTextChanges = false;
-    private long mLastEditScroll = 0;
-
-    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.
-            if (level > TRIM_MEMORY_UI_HIDDEN) {
-                HTML5VideoInline.cleanupSurfaceTexture();
-                HTML5VideoView.release();
-            }
-            WebViewClassic.nativeOnTrimMemory(level);
-        }
-    }
-
-    // A final CallbackProxy shared by WebViewCore and BrowserFrame.
-    private CallbackProxy mCallbackProxy;
-
-    private WebViewDatabaseClassic 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();
-    // 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
-     * TODO: Some of this is now unnecessary as it is handled by
-     * WebInputTouchDispatcher (such as click, long press, and double tap).
-     */
-    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;
-    private static final int TOUCH_DRAG_TEXT_MODE = 10;
-
-    // true when the touch movement exceeds the slop
-    private boolean mConfirmMove;
-    private boolean mTouchInEditText;
-
-    // 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
-
-    // Used by OverScrollGlow
-    OverScroller mScroller;
-    Scroller mEditTextScroller;
-
-    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;
-
-    // Lazily-instantiated instance for injecting accessibility.
-    private AccessibilityInjector mAccessibilityInjector;
-
-    /**
-     * 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 Point mSelectOffset;
-    private Point mSelectCursorBase = new Point();
-    private Rect mSelectHandleBaseBounds = new Rect();
-    private int mSelectCursorBaseLayerId;
-    private QuadF mSelectCursorBaseTextQuad = new QuadF();
-    private Point mSelectCursorExtent = new Point();
-    private Rect mSelectHandleExtentBounds = new Rect();
-    private int mSelectCursorExtentLayerId;
-    private QuadF mSelectCursorExtentTextQuad = new QuadF();
-    private Point mSelectDraggingCursor;
-    private QuadF mSelectDraggingTextQuad;
-    private boolean mIsCaretSelection;
-    static final int HANDLE_ID_BASE = 0;
-    static final int HANDLE_ID_EXTENT = 1;
-
-    // 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 boolean mShowTapHighlight;
-
-    // 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 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 WEBCORE_INITIALIZED_MSG_ID         = 107;
-    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
-    static final int UPDATE_ZOOM_RANGE                  = 109;
-    static final int TAKE_FOCUS                         = 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 SHOW_FULLSCREEN                    = 120;
-    static final int HIDE_FULLSCREEN                    = 121;
-    static final int UPDATE_MATCH_COUNT                 = 126;
-    static final int CENTER_FIT_RECT                    = 127;
-    static final int SET_SCROLLBAR_MODES                = 129;
-    static final int HIT_TEST_RESULT                    = 130;
-    static final int SAVE_WEBARCHIVE_FINISHED           = 131;
-    static final int SET_AUTOFILLABLE                   = 132;
-    static final int AUTOFILL_COMPLETE                  = 133;
-    static final int SCREEN_ON                          = 134;
-    static final int UPDATE_ZOOM_DENSITY                = 135;
-    static final int EXIT_FULLSCREEN_VIDEO              = 136;
-    static final int COPY_TO_CLIPBOARD                  = 137;
-    static final int INIT_EDIT_FIELD                    = 138;
-    static final int REPLACE_TEXT                       = 139;
-    static final int CLEAR_CARET_HANDLE                 = 140;
-    static final int KEY_PRESS                          = 141;
-    static final int RELOCATE_AUTO_COMPLETE_POPUP       = 142;
-    static final int FOCUS_NODE_CHANGED                 = 143;
-    static final int AUTOFILL_FORM                      = 144;
-    static final int SCROLL_EDIT_TEXT                   = 145;
-    static final int EDIT_TEXT_SIZE_CHANGED             = 146;
-    static final int SHOW_CARET_HANDLE                  = 147;
-    static final int UPDATE_CONTENT_BOUNDS              = 148;
-    static final int SCROLL_HANDLE_INTO_VIEW            = 149;
-
-    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;
-        "", //             = 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;
-        "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;
-
-    /**
-     * 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 WebViewInputDispatcher mInputDispatcher;
-
-    // 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;
-
-    // Used to notify listeners about find-on-page results.
-    private WebView.FindListener mFindListener;
-
-    // Used to prevent resending save password message
-    private Message mResumeMsg;
-
-    /**
-     * 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();
-    }
-
-    /**
-     * See {@link WebViewProvider#init(Map, boolean)}
-     */
-    @Override
-    public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
-        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 = WebViewDatabaseClassic.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();
-        mEditTextScroller = new Scroller(context);
-
-        // Calculate channel distance
-        calculateChannelDistance(context);
-    }
-
-    /**
-     * Calculate sChannelDistance based on the screen information.
-     * @param context A Context object used to access application assets.
-     */
-    private void calculateChannelDistance(Context context) {
-        // The channel distance is adjusted for density and screen size
-        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        final double screenSize = Math.hypot((double)(metrics.widthPixels/metrics.densityDpi),
-                (double)(metrics.heightPixels/metrics.densityDpi));
-        if (screenSize < 3.0) {
-            sChannelDistance = 16;
-        } else if (screenSize < 5.0) {
-            sChannelDistance = 22;
-        } else if (screenSize < 7.0) {
-            sChannelDistance = 28;
-        } else {
-            sChannelDistance = 34;
-        }
-        sChannelDistance = (int)(sChannelDistance * metrics.density);
-        if (sChannelDistance < 16) sChannelDistance = 16;
-
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "sChannelDistance : " + sChannelDistance
-                    + ", density : " + metrics.density
-                    + ", screenSize : " + screenSize
-                    + ", metrics.heightPixels : " + metrics.heightPixels
-                    + ", metrics.widthPixels : " + metrics.widthPixels
-                    + ", metrics.densityDpi : " + metrics.densityDpi);
-        }
-    }
-
-    // WebViewProvider bindings
-
-    static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
-        Factory() {
-            // Touch JniUtil and WebViewCore in case this is being called from
-            // WebViewFactory.Preloader, to ensure that the JNI libraries that they use are
-            // preloaded in the zygote.
-            try {
-                Class.forName("android.webkit.JniUtil");
-                Class.forName("android.webkit.WebViewCore");
-            } catch (ClassNotFoundException e) {
-                Log.e(LOGTAG, "failed to load JNI libraries");
-                throw new AndroidRuntimeException(e);
-            }
-        }
-
-        @Override
-        public String findAddress(String addr) {
-            return WebViewClassic.findAddress(addr);
-        }
-        @Override
-        public void setPlatformNotificationsEnabled(boolean enable) {
-            if (enable) {
-                WebViewClassic.enablePlatformNotifications();
-            } else {
-                WebViewClassic.disablePlatformNotifications();
-            }
-        }
-
-        @Override
-        public Statics getStatics() { return this; }
-
-        @Override
-        public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
-            return new WebViewClassic(webView, privateAccess);
-        }
-
-        @Override
-        public GeolocationPermissions getGeolocationPermissions() {
-            return GeolocationPermissionsClassic.getInstance();
-        }
-
-        @Override
-        public CookieManager getCookieManager() {
-            return CookieManagerClassic.getInstance();
-        }
-
-        @Override
-        public WebIconDatabase getWebIconDatabase() {
-            return WebIconDatabaseClassic.getInstance();
-        }
-
-        @Override
-        public WebStorage getWebStorage() {
-            return WebStorageClassic.getInstance();
-        }
-
-        @Override
-        public WebViewDatabase getWebViewDatabase(Context context) {
-            return WebViewDatabaseClassic.getInstance(context);
-        }
-
-        @Override
-        public String getDefaultUserAgent(Context context) {
-            return WebSettingsClassic.getDefaultUserAgentForLocale(context,
-                    Locale.getDefault());
-        }
-
-        @Override
-        public void setWebContentsDebuggingEnabled(boolean enable) {
-            // no-op for WebViewClassic.
-        }
-    }
-
-    private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
-        switch (eventType) {
-        case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
-            HitTestResult hitTest = getHitTestResult();
-            if (hitTest != null) {
-                mWebView.performLongClick();
-            }
-            break;
-        case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
-            mZoomManager.handleDoubleTap(event.getX(), event.getY());
-            break;
-        case WebViewInputDispatcher.EVENT_TYPE_TOUCH:
-            onHandleUiTouchEvent(event);
-            break;
-        case WebViewInputDispatcher.EVENT_TYPE_CLICK:
-            if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
-                mWebView.playSoundEffect(SoundEffectConstants.CLICK);
-                overrideLoading(mFocusedNode.mIntentUrl);
-            }
-            break;
-        }
-    }
-
-    private void onHandleUiTouchEvent(MotionEvent ev) {
-        final ScaleGestureDetector detector =
-                mZoomManager.getScaleGestureDetector();
-
-        int action = ev.getActionMasked();
-        final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
-        final boolean configChanged =
-            action == MotionEvent.ACTION_POINTER_UP ||
-            action == MotionEvent.ACTION_POINTER_DOWN;
-        final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
-
-        // Determine focal point
-        float sumX = 0, sumY = 0;
-        final int count = ev.getPointerCount();
-        for (int i = 0; i < count; i++) {
-            if (skipIndex == i) continue;
-            sumX += ev.getX(i);
-            sumY += ev.getY(i);
-        }
-        final int div = pointerUp ? count - 1 : count;
-        float x = sumX / div;
-        float y = sumY / div;
-
-        if (configChanged) {
-            mLastTouchX = Math.round(x);
-            mLastTouchY = Math.round(y);
-            mLastTouchTime = ev.getEventTime();
-            mWebView.cancelLongPress();
-            mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-        }
-
-        if (detector != null) {
-            detector.onTouchEvent(ev);
-            if (detector.isInProgress()) {
-                mLastTouchTime = ev.getEventTime();
-
-                if (!mZoomManager.supportsPanDuringZoom()) {
-                    return;
-                }
-                mTouchMode = TOUCH_DRAG_MODE;
-                if (mVelocityTracker == null) {
-                    mVelocityTracker = VelocityTracker.obtain();
-                }
-            }
-        }
-
-        if (action == MotionEvent.ACTION_POINTER_DOWN) {
-            cancelTouch();
-            action = MotionEvent.ACTION_DOWN;
-        } 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));
-    }
-
-    // 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);
-    }
-
-    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);
-    }
-
-    void updateJavaScriptEnabled(boolean enabled) {
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().updateJavaScriptEnabled(enabled);
-        }
-    }
-
-    private void init() {
-        OnTrimMemoryListener.init(mContext);
-        mWebView.setWillNotDraw(false);
-        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 = WebViewCore.getFixedDisplayDensity(mContext);
-        // 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;
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (!mWebView.isEnabled()) {
-            // Only default actions are supported while disabled.
-            return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
-        }
-
-        if (getAccessibilityInjector().supportsAccessibilityAction(action)) {
-            return getAccessibilityInjector().performAccessibilityAction(action, arguments);
-        }
-
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
-            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                final int convertedContentHeight = contentToViewY(getContentHeight());
-                final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
-                        - mWebView.getPaddingBottom();
-                final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
-                final boolean canScrollBackward = (getScrollY() > 0);
-                final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
-                if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
-                    mWebView.scrollBy(0, adjustedViewHeight);
-                    return true;
-                }
-                if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
-                    mWebView.scrollBy(0, -adjustedViewHeight);
-                    return true;
-                }
-                return false;
-            }
-        }
-
-        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
-    }
-
-    @Override
-    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
-      return null;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        if (!mWebView.isEnabled()) {
-            // Only default actions are supported while disabled.
-            return;
-        }
-
-        info.setScrollable(isScrollableForAccessibility());
-
-        final int convertedContentHeight = contentToViewY(getContentHeight());
-        final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
-                - mWebView.getPaddingBottom();
-        final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
-        final boolean canScrollBackward = (getScrollY() > 0);
-        final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
-
-        if (canScrollForward) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-        }
-
-        if (canScrollForward) {
-            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-        }
-
-        getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info);
-    }
-
-    @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));
-    }
-
-    /* package */ void handleSelectionChangedWebCoreThread(String selection, int token) {
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().onSelectionStringChangedWebCoreThread(selection, token);
-        }
-    }
-
-    private boolean isAccessibilityInjectionEnabled() {
-        final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
-        if (!manager.isEnabled()) {
-            return false;
-        }
-
-        // Accessibility scripts should be injected only when a speaking service
-        // is enabled. This may need to change later to accommodate Braille.
-        final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
-                AccessibilityServiceInfo.FEEDBACK_SPOKEN);
-        if (services.isEmpty()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private AccessibilityInjector getAccessibilityInjector() {
-        if (mAccessibilityInjector == null) {
-            mAccessibilityInjector = new AccessibilityInjector(this);
-        }
-        return mAccessibilityInjector;
-    }
-
-    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 = WebViewCore.getFixedDisplayDensity(mContext)
-                * 100 / zoomDensity;
-        updateDefaultZoomDensity(density);
-    }
-
-    /* package */ void updateDefaultZoomDensity(float density) {
-        mNavSlop = (int) (16 * density);
-        mZoomManager.updateDefaultZoomDensity(density);
-    }
-
-    /* package */ int getScaledNavSlop() {
-        return viewToContentDimension(mNavSlop);
-    }
-
-    /* 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 {
-            if (mResumeMsg != null) {
-                Log.w(LOGTAG, "onSavePassword should not be called while dialog is up");
-                resumeMsg.sendToTarget();
-                return true;
-            }
-            mResumeMsg = resumeMsg;
-            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;
-
-            mSavePasswordDialog = 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) {
-                            if (mResumeMsg != null) {
-                                resumeMsg.sendToTarget();
-                                mResumeMsg = null;
-                            }
-                            mSavePasswordDialog = null;
-                        }
-                    })
-                    .setNeutralButton(com.android.internal.R.string.save_password_remember,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (mResumeMsg != null) {
-                                remember.sendToTarget();
-                                mResumeMsg = null;
-                            }
-                            mSavePasswordDialog = null;
-                        }
-                    })
-                    .setNegativeButton(com.android.internal.R.string.save_password_never,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (mResumeMsg != null) {
-                                neverRemember.sendToTarget();
-                                mResumeMsg = null;
-                            }
-                            mSavePasswordDialog = null;
-                        }
-                    })
-                    .setOnDismissListener(new DialogInterface.OnDismissListener() {
-                        @Override
-                        public void onDismiss(DialogInterface dialog) {
-                            if (mResumeMsg != null) {
-                                resumeMsg.sendToTarget();
-                                mResumeMsg = null;
-                            }
-                            mSavePasswordDialog = null;
-                        }
-                    }).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;
-        }
-    }
-
-    /**
-     * See {@link WebView#setHorizontalScrollbarOverlay(boolean)}
-     */
-    @Override
-    public void setHorizontalScrollbarOverlay(boolean overlay) {
-        mOverlayHorizontalScrollbar = overlay;
-    }
-
-    /**
-     * See {@link WebView#setVerticalScrollbarOverlay(boolean)
-     */
-    @Override
-    public void setVerticalScrollbarOverlay(boolean overlay) {
-        mOverlayVerticalScrollbar = overlay;
-    }
-
-    /**
-     * See {@link WebView#overlayHorizontalScrollbar()}
-     */
-    @Override
-    public boolean overlayHorizontalScrollbar() {
-        return mOverlayHorizontalScrollbar;
-    }
-
-    /**
-     * See {@link WebView#overlayVerticalScrollbar()}
-     */
-    @Override
-    public boolean overlayVerticalScrollbar() {
-        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
-     */
-    protected int getTitleHeight() {
-        if (mWebView instanceof TitleBarDelegate) {
-            return ((TitleBarDelegate) mWebView).getTitleHeight();
-        }
-        return 0;
-    }
-
-    /**
-     * See {@link WebView#getVisibleTitleHeight()}
-     */
-    @Override
-    @Deprecated
-    public int getVisibleTitleHeight() {
-        // Actually, this method returns the height of the embedded title bar if one is set via the
-        // hidden setEmbeddedTitleBar method.
-        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;
-    }
-
-    /**
-     * See {@link WebView#getCertificate()}
-     */
-    @Override
-    public SslCertificate getCertificate() {
-        return mCertificate;
-    }
-
-    /**
-     * See {@link WebView#setCertificate(SslCertificate)}
-     */
-    @Override
-    public void setCertificate(SslCertificate certificate) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "setCertificate=" + certificate);
-        }
-        // here, the certificate can be null (if the site is not secure)
-        mCertificate = certificate;
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods called by activity
-    //-------------------------------------------------------------------------
-
-    /**
-     * See {@link WebView#savePassword(String, String, String)}
-     */
-    @Override
-    public void savePassword(String host, String username, String password) {
-        mDatabase.setUsernamePassword(host, username, password);
-    }
-
-    /**
-     * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)}
-     */
-    @Override
-    public void setHttpAuthUsernamePassword(String host, String realm,
-            String username, String password) {
-        mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
-    }
-
-    /**
-     * See {@link WebView#getHttpAuthUsernamePassword(String, String)}
-     */
-    @Override
-    public String[] getHttpAuthUsernamePassword(String host, String realm) {
-        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 ActionModes) no longer relevant.
-     */
-    private void clearHelpers() {
-        hideSoftKeyboard();
-        clearActionModes();
-        dismissFullScreenMode();
-        cancelDialogs();
-    }
-
-    private void cancelDialogs() {
-        if (mListBoxDialog != null) {
-            mListBoxDialog.cancel();
-            mListBoxDialog = null;
-        }
-        if (mSavePasswordDialog != null) {
-            mSavePasswordDialog.dismiss();
-            mSavePasswordDialog = null;
-        }
-    }
-
-    /**
-     * See {@link WebView#destroy()}
-     */
-    @Override
-    public void destroy() {
-        if (mWebView.getViewRootImpl() != null) {
-            Log.e(LOGTAG, Log.getStackTraceString(
-                    new Throwable("Error: WebView.destroy() called while still attached!")));
-        }
-        ensureFunctorDetached();
-        destroyJava();
-        destroyNative();
-    }
-
-    private void ensureFunctorDetached() {
-        if (mWebView.isHardwareAccelerated()) {
-            int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
-            ViewRootImpl viewRoot = mWebView.getViewRootImpl();
-            if (drawGLFunction != 0 && viewRoot != null) {
-                viewRoot.detachFunctor(drawGLFunction);
-            }
-        }
-    }
-
-    private void destroyJava() {
-        mCallbackProxy.blockMessages();
-        if (mAccessibilityInjector != null) {
-            mAccessibilityInjector.destroy();
-            mAccessibilityInjector = null;
-        }
-        if (mWebViewCore != 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);
-        }
-    }
-
-    private void destroyNative() {
-        if (mNativeClass == 0) return;
-        int nptr = mNativeClass;
-        mNativeClass = 0;
-        if (Thread.currentThread() == mPrivateHandler.getLooper().getThread()) {
-            // We are on the main thread and can safely delete
-            nativeDestroy(nptr);
-        } else {
-            mPrivateHandler.post(new DestroyNativeRunnable(nptr));
-        }
-    }
-
-    private static class DestroyNativeRunnable implements Runnable {
-
-        private int mNativePtr;
-
-        public DestroyNativeRunnable(int nativePtr) {
-            mNativePtr = nativePtr;
-        }
-
-        @Override
-        public void run() {
-            // nativeDestroy also does a stopGL()
-            nativeDestroy(mNativePtr);
-        }
-
-    }
-
-    /**
-     * See {@link WebView#enablePlatformNotifications()}
-     */
-    @Deprecated
-    public static void enablePlatformNotifications() {
-        synchronized (WebViewClassic.class) {
-            sNotificationsEnabled = true;
-            Context context = JniUtil.getContext();
-            if (context != null)
-                setupProxyListener(context);
-        }
-    }
-
-    /**
-     * See {@link WebView#disablePlatformNotifications()}
-     */
-    @Deprecated
-    public static void disablePlatformNotifications() {
-        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
-     *
-     * This is an implementation detail.
-     */
-    public void setJsFlags(String flags) {
-        mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
-    }
-
-    /**
-     * See {@link WebView#setNetworkAvailable(boolean)}
-     */
-    @Override
-    public void setNetworkAvailable(boolean networkUp) {
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
-                networkUp ? 1 : 0, 0);
-    }
-
-    /**
-     * Inform WebView about the current network type.
-     */
-    public void setNetworkType(String type, String subtype) {
-        Map<String, String> map = new HashMap<String, String>();
-        map.put("type", type);
-        map.put("subtype", subtype);
-        mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
-    }
-
-    /**
-     * See {@link WebView#saveState(Bundle)}
-     */
-    @Override
-    public WebBackForwardList saveState(Bundle outState) {
-        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.
-        WebBackForwardListClassic 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++) {
-            WebHistoryItemClassic 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;
-    }
-
-    /**
-     * See {@link WebView#savePicture(Bundle, File)}
-     */
-    @Override
-    @Deprecated
-    public boolean savePicture(Bundle b, final File dest) {
-        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();
-    }
-
-    /**
-     * See {@link WebView#restorePicture(Bundle, File)};
-     */
-    @Override
-    @Deprecated
-    public boolean restorePicture(Bundle b, File src) {
-        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
-     * @param callback The {@link ValueCallback} to call with the result
-     */
-    public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
-        if (mWebViewCore == null) {
-            callback.onReceiveValue(false);
-            return;
-        }
-        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
-                new WebViewCore.SaveViewStateRequest(stream, callback));
-    }
-
-    /**
-     * Loads the view data from the input stream. See
-     * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
-     * @param stream The {@link InputStream} to load from
-     */
-    public void loadViewState(InputStream stream) {
-        mBlockWebkitViewMessages = true;
-        new AsyncTask<InputStream, Void, DrawData>() {
-
-            @Override
-            protected DrawData doInBackground(InputStream... params) {
-                try {
-                    return ViewStateSerializer.deserializeViewState(params[0]);
-                } catch (IOException e) {
-                    return null;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(DrawData draw) {
-                if (draw == null) {
-                    Log.e(LOGTAG, "Failed to load view state!");
-                    return;
-                }
-                int viewWidth = getViewWidth();
-                int viewHeight = getViewHeightWithTitle() - getTitleHeight();
-                draw.mViewSize = new Point(viewWidth, viewHeight);
-                draw.mViewState.mDefaultScale = getDefaultZoomScale();
-                mLoadedPicture = draw;
-                setNewPicture(mLoadedPicture, true);
-                mLoadedPicture.mViewState = null;
-            }
-
-        }.execute(stream);
-    }
-
-    /**
-     * Clears the view state set with {@link #loadViewState(InputStream)}.
-     * This WebView will then switch to showing the content from webkit
-     */
-    public void clearViewState() {
-        mBlockWebkitViewMessages = false;
-        mLoadedPicture = null;
-        invalidate();
-    }
-
-    /**
-     * See {@link WebView#restoreState(Bundle)}
-     */
-    @Override
-    public WebBackForwardList restoreState(Bundle inState) {
-        WebBackForwardListClassic returnList = null;
-        if (inState == null) {
-            return returnList;
-        }
-        if (inState.containsKey("index") && inState.containsKey("history")) {
-            mCertificate = SslCertificate.restoreState(
-                inState.getBundle("certificate"));
-
-            final WebBackForwardListClassic 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 WebHistoryItemClassic(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();
-            if (isAccessibilityInjectionEnabled()) {
-                getAccessibilityInjector().addAccessibilityApisIfNecessary();
-            }
-            // Send a restore state message.
-            mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
-        }
-        return returnList;
-    }
-
-    /**
-     * See {@link WebView#loadUrl(String, Map)}
-     */
-    @Override
-    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
-        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();
-    }
-
-    /**
-     * See {@link WebView#loadUrl(String)}
-     */
-    @Override
-    public void loadUrl(String url) {
-        loadUrlImpl(url);
-    }
-
-    private void loadUrlImpl(String url) {
-        if (url == null) {
-            return;
-        }
-        loadUrlImpl(url, null);
-    }
-
-    /**
-     * See {@link WebView#postUrl(String, byte[])}
-     */
-    @Override
-    public void postUrl(String url, byte[] postData) {
-        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);
-        }
-    }
-
-    /**
-     * See {@link WebView#loadData(String, String, String)}
-     */
-    @Override
-    public void loadData(String data, String mimeType, String encoding) {
-        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());
-    }
-
-    /**
-     * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}
-     */
-    @Override
-    public void loadDataWithBaseURL(String baseUrl, String data,
-            String mimeType, String encoding, String historyUrl) {
-
-        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();
-    }
-
-    @Override
-    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
-        // K-only API not implemented in WebViewClassic.
-        throw new IllegalStateException("This API not supported on Android 4.3 and earlier");
-    }
-
-    /**
-     * See {@link WebView#saveWebArchive(String)}
-     */
-    @Override
-    public void saveWebArchive(String filename) {
-        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;
-    }
-
-    /**
-     * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)}
-     */
-    @Override
-    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
-        saveWebArchiveImpl(basename, autoname, callback);
-    }
-
-    private void saveWebArchiveImpl(String basename, boolean autoname,
-            ValueCallback<String> callback) {
-        mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
-            new SaveWebArchiveMessage(basename, autoname, callback));
-    }
-
-    /**
-     * See {@link WebView#stopLoading()}
-     */
-    @Override
-    public void stopLoading() {
-        // TODO: should we clear all the messages in the queue before sending
-        // STOP_LOADING?
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.STOP_LOADING);
-    }
-
-    /**
-     * See {@link WebView#reload()}
-     */
-    @Override
-    public void reload() {
-        clearHelpers();
-        switchOutDrawHistory();
-        mWebViewCore.sendMessage(EventHub.RELOAD);
-    }
-
-    /**
-     * See {@link WebView#canGoBack()}
-     */
-    @Override
-    public boolean canGoBack() {
-        WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() > 0;
-            }
-        }
-    }
-
-    /**
-     * See {@link WebView#goBack()}
-     */
-    @Override
-    public void goBack() {
-        goBackOrForwardImpl(-1);
-    }
-
-    /**
-     * See {@link WebView#canGoForward()}
-     */
-    @Override
-    public boolean canGoForward() {
-        WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                return l.getCurrentIndex() < l.getSize() - 1;
-            }
-        }
-    }
-
-    /**
-     * See {@link WebView#goForward()}
-     */
-    @Override
-    public void goForward() {
-        goBackOrForwardImpl(1);
-    }
-
-    /**
-     * See {@link WebView#canGoBackOrForward(int)}
-     */
-    @Override
-    public boolean canGoBackOrForward(int steps) {
-        WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
-        synchronized (l) {
-            if (l.getClearPending()) {
-                return false;
-            } else {
-                int newIndex = l.getCurrentIndex() + steps;
-                return newIndex >= 0 && newIndex < l.getSize();
-            }
-        }
-    }
-
-    /**
-     * See {@link WebView#goBackOrForward(int)}
-     */
-    @Override
-    public void goBackOrForward(int steps) {
-        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);
-        }
-    }
-
-    /**
-     * See {@link WebView#isPrivateBrowsingEnabled()}
-     */
-    @Override
-    public boolean isPrivateBrowsingEnabled() {
-        WebSettingsClassic settings = getSettings();
-        return (settings != null) ? settings.isPrivateBrowsingEnabled() : false;
-    }
-
-    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;
-    }
-
-    /**
-     * See {@link WebView#pageUp(boolean)}
-     */
-    @Override
-    public boolean pageUp(boolean top) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        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);
-    }
-
-    /**
-     * See {@link WebView#pageDown(boolean)}
-     */
-    @Override
-    public boolean pageDown(boolean bottom) {
-        if (mNativeClass == 0) {
-            return false;
-        }
-        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);
-    }
-
-    /**
-     * See {@link WebView#clearView()}
-     */
-    @Override
-    public void clearView() {
-        mContentWidth = 0;
-        mContentHeight = 0;
-        setBaseLayer(0, false, false);
-        mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
-    }
-
-    /**
-     * See {@link WebView#capturePicture()}
-     */
-    @Override
-    public Picture capturePicture() {
-        if (mNativeClass == 0) return null;
-        Picture result = new Picture();
-        nativeCopyBaseContentToPicture(result);
-        return result;
-    }
-
-    /**
-     * See {@link WebView#createPrintDocumentAdapter()}
-     */
-    @Override
-    public PrintDocumentAdapter createPrintDocumentAdapter() {
-        // K-only API not implemented in WebViewClassic.
-        throw new IllegalStateException("This API not supported on Android 4.3 and earlier");
-    }
-
-    /**
-     * See {@link WebView#getScale()}
-     */
-    @Override
-    public float getScale() {
-        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);
-    }
-
-    /**
-     * See {@link WebView#setInitialScale(int)}
-     */
-    @Override
-    public void setInitialScale(int scaleInPercent) {
-        mZoomManager.setInitialScaleInPercent(scaleInPercent);
-    }
-
-    /**
-     * See {@link WebView#invokeZoomPicker()}
-     */
-    @Override
-    public void invokeZoomPicker() {
-        if (!getSettings().supportZoom()) {
-            Log.w(LOGTAG, "This WebView doesn't support zoom.");
-            return;
-        }
-        clearHelpers();
-        mZoomManager.invokeZoomPicker();
-    }
-
-    /**
-     * See {@link WebView#getHitTestResult()}
-     */
-    @Override
-    public HitTestResult getHitTestResult() {
-        return mInitialHitTestResult;
-    }
-
-    // No left edge for double-tap zoom alignment
-    static final int NO_LEFTEDGE = -1;
-
-    int getBlockLeftEdge(int x, int y, float 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 (rect.width() > readingWidth) {
-                    // stop when bounding box doesn't fit the screen width
-                    // at reading scale
-                    break;
-                }
-
-                left = rect.left;
-            }
-        }
-
-        return left;
-    }
-
-    /**
-     * See {@link WebView#requestFocusNodeHref(Message)}
-     */
-    @Override
-    public void requestFocusNodeHref(Message hrefMsg) {
-        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;
-        }
-        mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
-                contentX, contentY, hrefMsg);
-    }
-
-    /**
-     * See {@link WebView#requestImageRef(Message)}
-     */
-    @Override
-    public void requestImageRef(Message msg) {
-        if (0 == mNativeClass) return; // client isn't initialized
-        String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null;
-        Bundle data = msg.getData();
-        data.putString("url", url);
-        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());
-    }
-
-    /**
-     * 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.
-     */
-    /*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) {
-            invalidate();
-            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()));
-                }
-            }
-            invalidate();
-        }
-        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,
-                        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 final Rect mTempContentVisibleRect = 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(mTempContentVisibleRect);
-        viewToContentVisibleRect(r, mTempContentVisibleRect);
-    }
-
-    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();
-    }
-
-    @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_TEXT_MODE) {
-            scrollEditText(scrollX, scrollY);
-            return;
-        }
-        if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
-            scrollLayerTo(scrollX, scrollY);
-            animateHandles();
-            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);
-
-        animateHandles();
-
-        if (mOverScrollGlow != null) {
-            mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
-        }
-    }
-
-    /**
-     * See {@link WebView#getUrl()}
-     */
-    @Override
-    public String getUrl() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getUrl() : null;
-    }
-
-    /**
-     * See {@link WebView#getOriginalUrl()}
-     */
-    @Override
-    public String getOriginalUrl() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getOriginalUrl() : null;
-    }
-
-    /**
-     * See {@link WebView#getTitle()}
-     */
-    @Override
-    public String getTitle() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTitle() : null;
-    }
-
-    /**
-     * See {@link WebView#getFavicon()}
-     */
-    @Override
-    public Bitmap getFavicon() {
-        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getFavicon() : null;
-    }
-
-    /**
-     * See {@link WebView#getTouchIconUrl()}
-     */
-    @Override
-    public String getTouchIconUrl() {
-        WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem();
-        return h != null ? h.getTouchIconUrl() : null;
-    }
-
-    /**
-     * See {@link WebView#getProgress()}
-     */
-    @Override
-    public int getProgress() {
-        return mCallbackProxy.getProgress();
-    }
-
-    /**
-     * See {@link WebView#getContentHeight()}
-     */
-    @Override
-    public int getContentHeight() {
-        return mContentHeight;
-    }
-
-    /**
-     * See {@link WebView#getContentWidth()}
-     */
-    @Override
-    public int getContentWidth() {
-        return mContentWidth;
-    }
-
-    public int getPageBackgroundColor() {
-        if (mNativeClass == 0) return Color.WHITE;
-        return nativeGetBackgroundColor(mNativeClass);
-    }
-
-    /**
-     * See {@link WebView#pauseTimers()}
-     */
-    @Override
-    public void pauseTimers() {
-        mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
-    }
-
-    /**
-     * See {@link WebView#resumeTimers()}
-     */
-    @Override
-    public void resumeTimers() {
-        mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
-    }
-
-    /**
-     * See {@link WebView#onPause()}
-     */
-    @Override
-    public void onPause() {
-        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);
-            }
-
-            cancelDialogs();
-            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);
-        }
-    }
-
-    /**
-     * See {@link WebView#onResume()}
-     */
-    @Override
-    public void onResume() {
-        if (mIsPaused) {
-            mIsPaused = false;
-            mWebViewCore.sendMessage(EventHub.ON_RESUME);
-            if (mNativeClass != 0) {
-                nativeSetPauseDrawing(mNativeClass, false);
-            }
-        }
-        // 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();
-    }
-
-    /**
-     * See {@link WebView#isPaused()}
-     */
-    @Override
-    public boolean isPaused() {
-        return mIsPaused;
-    }
-
-    /**
-     * See {@link WebView#freeMemory()}
-     */
-    @Override
-    public void freeMemory() {
-        mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
-    }
-
-    /**
-     * See {@link WebView#clearCache(boolean)}
-     */
-    @Override
-    public void clearCache(boolean includeDiskFiles) {
-        // 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);
-    }
-
-    /**
-     * See {@link WebView#clearFormData()}
-     */
-    @Override
-    public void clearFormData() {
-        if (mAutoCompletePopup != null) {
-            mAutoCompletePopup.clearAdapter();
-        }
-    }
-
-    /**
-     * See {@link WebView#clearHistory()}
-     */
-    @Override
-    public void clearHistory() {
-        mCallbackProxy.getBackForwardList().setClearPending();
-        mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
-    }
-
-    /**
-     * See {@link WebView#clearSslPreferences()}
-     */
-    @Override
-    public void clearSslPreferences() {
-        mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
-    }
-
-    /**
-     * See {@link WebView#copyBackForwardList()}
-     */
-    @Override
-    public WebBackForwardListClassic copyBackForwardList() {
-        return mCallbackProxy.getBackForwardList().clone();
-    }
-
-    /**
-     * See {@link WebView#setFindListener(WebView.FindListener)}.
-     * @hide
-     */
-     @Override
-    public void setFindListener(WebView.FindListener listener) {
-         mFindListener = listener;
-     }
-
-    /**
-     * See {@link WebView#findNext(boolean)}
-     */
-    @Override
-    public void findNext(boolean forward) {
-        if (0 == mNativeClass) return; // client isn't initialized
-        if (mFindRequest != null) {
-            mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0, mFindRequest);
-        }
-    }
-
-    /**
-     * See {@link WebView#findAll(String)}
-     */
-    @Override
-    public int findAll(String find) {
-        return findAllBody(find, false);
-    }
-
-    @Override
-    public void findAllAsync(String find) {
-        findAllBody(find, true);
-    }
-
-    private int findAllBody(String find, boolean isAsync) {
-        if (0 == mNativeClass) return 0; // client isn't initialized
-        mFindRequest = null;
-        if (find == null) return 0;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        mFindRequest = new WebViewCore.FindAllRequest(find);
-        if (isAsync) {
-            mWebViewCore.sendMessage(EventHub.FIND_ALL, mFindRequest);
-            return 0; // no need to wait for response
-        }
-        synchronized(mFindRequest) {
-            try {
-                mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, mFindRequest);
-                while (mFindRequest.mMatchCount == -1) {
-                    mFindRequest.wait();
-                }
-            }
-            catch (InterruptedException e) {
-                return 0;
-            }
-            return mFindRequest.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.
-     */
-    @Override
-    public boolean showFindDialog(String text, boolean showIme) {
-        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(getWebView());
-        if (showIme) {
-            mFindCallback.showSoftInput();
-        } else if (text != null) {
-            mFindCallback.setText(text);
-            mFindCallback.findAll();
-            return true;
-        }
-        if (text == null) {
-            text = mFindRequest == null ? null : mFindRequest.mSearchText;
-        }
-        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;
-    }
-
-    // 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 find request sent.
-    private WebViewCore.FindAllRequest mFindRequest = null;
-
-    /**
-     * 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) {
-        return findAddress(addr, false);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * See {@link WebView#clearMatches()}
-     */
-    @Override
-    public void clearMatches() {
-        if (mNativeClass == 0)
-            return;
-        mWebViewCore.removeMessages(EventHub.FIND_ALL);
-        mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
-    }
-
-
-    /**
-     * Called when the find ActionMode ends.
-     */
-    @Override
-    public 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();
-    }
-
-    /**
-     * See {@link WebView#documentHasImages(Message)}
-     */
-    @Override
-    public void documentHasImages(Message response) {
-        if (response == null) {
-            return;
-        }
-        mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
-    }
-
-    /**
-     * Request the scroller to abort any ongoing animation
-     */
-    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;
-                } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
-                    oldX = getTextScrollX();
-                    oldY = getTextScrollY();
-                    rangeX = getMaxTextScrollX();
-                    rangeY = getMaxTextScrollY();
-                    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) {
-                    // Update the layer position instead of WebView.
-                    scrollLayerTo(x, y);
-                } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
-                    scrollEditText(x, y);
-                } else {
-                    setScrollXRaw(x);
-                    setScrollYRaw(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) {
-        int dx = mScrollingLayerRect.left - x;
-        int dy = mScrollingLayerRect.top - y;
-        if ((dx == 0 && dy == 0) || mNativeClass == 0) {
-            return;
-        }
-        if (mSelectingText) {
-            if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorBase.offset(dx, dy);
-                mSelectCursorBaseTextQuad.offset(dx, dy);
-            }
-            if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
-                mSelectCursorExtent.offset(dx, dy);
-                mSelectCursorExtentTextQuad.offset(dx, dy);
-            }
-        }
-        if (mAutoCompletePopup != null &&
-                mCurrentScrollingLayerId == mEditTextLayerId) {
-            mEditTextContentBounds.offset(dx, dy);
-            mAutoCompletePopup.resetRect();
-        }
-        nativeScrollLayer(mNativeClass, 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) {
-        abortAnimation();
-        x = pinLocX(x);
-        y = pinLocY(y);
-        int dx = x - getScrollX();
-        int dy = y - getScrollY();
-
-        if ((dx | dy) == 0) {
-            return false;
-        }
-        if (animate) {
-            //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
-            mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
-                    animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
-            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);
-
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().onPageStarted(url);
-        }
-
-        // Don't start out editing.
-        mIsEditingText = 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) {
-        mZoomManager.onPageFinished(url);
-
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().onPageFinished(url);
-        }
-    }
-
-    // scale from content to view coordinates, and pin
-    private void contentScrollTo(int cx, int cy, boolean animate) {
-        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, animate, 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);
-        }
-    }
-
-    /**
-     * See {@link WebView#setWebViewClient(WebViewClient)}
-     */
-    @Override
-    public void setWebViewClient(WebViewClient client) {
-        mCallbackProxy.setWebViewClient(client);
-    }
-
-    /**
-     * Gets the WebViewClient
-     * @return the current WebViewClient instance.
-     *
-     * This is an implementation detail.
-     */
-    public WebViewClient getWebViewClient() {
-        return mCallbackProxy.getWebViewClient();
-    }
-
-    /**
-     * See {@link WebView#setDownloadListener(DownloadListener)}
-     */
-    @Override
-    public void setDownloadListener(DownloadListener listener) {
-        mCallbackProxy.setDownloadListener(listener);
-    }
-
-    /**
-     * See {@link WebView#setWebChromeClient(WebChromeClient)}
-     */
-    @Override
-    public void setWebChromeClient(WebChromeClient client) {
-        mCallbackProxy.setWebChromeClient(client);
-    }
-
-    /**
-     * Gets the chrome handler.
-     * @return the current WebChromeClient instance.
-     *
-     * 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.
-     */
-    public void setWebBackForwardListClient(WebBackForwardListClient client) {
-        mCallbackProxy.setWebBackForwardListClient(client);
-    }
-
-    /**
-     * Gets the WebBackForwardListClient.
-     */
-    public WebBackForwardListClient getWebBackForwardListClient() {
-        return mCallbackProxy.getWebBackForwardListClient();
-    }
-
-    /**
-     * See {@link WebView#setPictureListener(PictureListener)}
-     */
-    @Override
-    @Deprecated
-    public void setPictureListener(PictureListener listener) {
-        mPictureListener = listener;
-    }
-
-    /* FIXME: Debug only! Remove for SDK! */
-    public void externalRepresentation(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
-    }
-
-    /* FIXME: Debug only! Remove for SDK! */
-    public void documentAsText(Message callback) {
-        mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
-    }
-
-    /**
-     * See {@link WebView#addJavascriptInterface(Object, String)}
-     */
-    @Override
-    public void addJavascriptInterface(Object object, String name) {
-
-        if (object == null) {
-            return;
-        }
-        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-
-        arg.mObject = object;
-        arg.mInterfaceName = name;
-
-        // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
-        // methods that are accessible from JS.
-        if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            arg.mRequireAnnotation = true;
-        } else {
-            arg.mRequireAnnotation = false;
-        }
-        mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
-    }
-
-    /**
-     * See {@link WebView#removeJavascriptInterface(String)}
-     */
-    @Override
-    public void removeJavascriptInterface(String interfaceName) {
-        if (mWebViewCore != null) {
-            WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-            arg.mInterfaceName = interfaceName;
-            mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
-        }
-    }
-
-    /**
-     * See {@link WebView#getSettings()}
-     * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used
-     * to access extension APIs.
-     */
-    @Override
-    public WebSettingsClassic getSettings() {
-        return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
-    }
-
-    /**
-     * See {@link WebView#getPluginList()}
-     */
-    @Deprecated
-    public static synchronized PluginList getPluginList() {
-        return new PluginList();
-    }
-
-    /**
-     * See {@link WebView#refreshPlugins(boolean)}
-     */
-    @Deprecated
-    public void refreshPlugins(boolean reloadOpenPages) {
-    }
-
-    //-------------------------------------------------------------------------
-    // Override View methods
-    //-------------------------------------------------------------------------
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            destroy();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private void drawContent(Canvas canvas) {
-        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));
-        if (mTouchMode == TOUCH_DRAG_MODE) {
-            if (mHeldMotionless == MOTIONLESS_PENDING) {
-                mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                mHeldMotionless = MOTIONLESS_FALSE;
-            }
-            if (mHeldMotionless == MOTIONLESS_FALSE) {
-                mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
-                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 && mShowTextSelectionExtra) {
-            extras = DRAW_EXTRAS_SELECTION;
-        }
-
-        calcOurContentVisibleRectF(mVisibleContentRect);
-        if (canvas.isHardwareAccelerated()) {
-            Rect invScreenRect = mIsWebViewVisible ? mInvScreenRect : null;
-            Rect screenRect = mIsWebViewVisible ? mScreenRect : null;
-
-            int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect,
-                    screenRect, 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);
-            nativeDraw(canvas, mVisibleContentRect, mBackgroundColor, extras);
-            canvas.setDrawFilter(null);
-        }
-
-        canvas.restoreToCount(saveCount);
-        drawTextSelectionHandles(canvas);
-
-        if (extras == DRAW_EXTRAS_CURSOR_RING) {
-            if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
-                mTouchMode = TOUCH_SHORTPRESS_MODE;
-            }
-        }
-    }
-
-    /**
-     * 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);
-        }
-
-        canvas.translate(0, getTitleHeight());
-        drawContent(canvas);
-        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() {
-        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.getScaleGestureDetector();
-        if (detector != null && detector.isInProgress()) {
-            return false;
-        }
-
-        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.
-     *
-     * 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) {
-        if (mWebViewCore == null) {
-            return false;
-        }
-        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;
-
-    void setBaseLayer(int layer, boolean showVisualIndicator,
-            boolean isPictureAfterFirstLayout) {
-        if (mNativeClass == 0)
-            return;
-        boolean queueFull;
-        final int scrollingLayer = (mTouchMode == TOUCH_DRAG_LAYER_MODE)
-                ? mCurrentScrollingLayerId : 0;
-        queueFull = nativeSetBaseLayer(mNativeClass, layer,
-                                       showVisualIndicator, isPictureAfterFirstLayout,
-                                       scrollingLayer);
-
-        if (queueFull) {
-            mWebViewCore.pauseWebKitDraw();
-        } else {
-            mWebViewCore.resumeWebKitDraw();
-        }
-
-        if (mHTML5VideoViewProxy != null) {
-            mHTML5VideoViewProxy.setBaseLayer(layer);
-        }
-    }
-
-    int getBaseLayer() {
-        if (mNativeClass == 0) {
-            return 0;
-        }
-        return nativeGetBaseLayer(mNativeClass);
-    }
-
-    private void onZoomAnimationStart() {
-    }
-
-    private void onZoomAnimationEnd() {
-        mPrivateHandler.sendEmptyMessage(RELOCATE_AUTO_COMPLETE_POPUP);
-    }
-
-    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 class SelectionHandleAlpha {
-        private int mAlpha = 0;
-        private int mTargetAlpha = 0;
-
-        public void setAlpha(int alpha) {
-            mAlpha = alpha;
-            // TODO: Use partial invalidate
-            invalidate();
-        }
-
-        public int getAlpha() {
-            return mAlpha;
-        }
-
-        public void setTargetAlpha(int alpha) {
-            mTargetAlpha = alpha;
-        }
-
-        public int getTargetAlpha() {
-            return mTargetAlpha;
-        }
-
-    }
-
-    private void startSelectingText() {
-        mSelectingText = true;
-        mShowTextSelectionExtra = true;
-        animateHandles();
-    }
-
-    private void animateHandle(boolean canShow, ObjectAnimator animator,
-            Point selectionPoint, int selectionLayerId,
-            SelectionHandleAlpha alpha) {
-        boolean isVisible = canShow && mSelectingText
-                && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint)
-                || isHandleVisible(selectionPoint, selectionLayerId));
-        int targetValue = isVisible ? 255 : 0;
-        if (targetValue != alpha.getTargetAlpha()) {
-            alpha.setTargetAlpha(targetValue);
-            animator.setIntValues(targetValue);
-            animator.setDuration(SELECTION_HANDLE_ANIMATION_MS);
-            animator.start();
-        }
-    }
-
-    private void animateHandles() {
-        boolean canShowBase = mSelectingText;
-        boolean canShowExtent = mSelectingText && !mIsCaretSelection;
-        animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase,
-                mSelectCursorBaseLayerId, mBaseAlpha);
-        animateHandle(canShowExtent, mExtentHandleAlphaAnimator,
-                mSelectCursorExtent, mSelectCursorExtentLayerId,
-                mExtentAlpha);
-    }
-
-    private void endSelectingText() {
-        mSelectingText = false;
-        mShowTextSelectionExtra = false;
-        animateHandles();
-    }
-
-    private void ensureSelectionHandles() {
-        if (mSelectHandleCenter == null) {
-            mSelectHandleCenter = mContext.getResources().getDrawable(
-                    com.android.internal.R.drawable.text_select_handle_middle).mutate();
-            mSelectHandleLeft = mContext.getResources().getDrawable(
-                    com.android.internal.R.drawable.text_select_handle_left).mutate();
-            mSelectHandleRight = mContext.getResources().getDrawable(
-                    com.android.internal.R.drawable.text_select_handle_right).mutate();
-            // All handles have the same height, so we can save effort with
-            // this assumption.
-            mSelectOffset = new Point(0,
-                    -mSelectHandleLeft.getIntrinsicHeight());
-        }
-    }
-
-    private void drawHandle(Point point, int handleId, Rect bounds,
-            int alpha, Canvas canvas) {
-        int offset;
-        int width;
-        int height;
-        Drawable drawable;
-        boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId);
-        if (isLeft) {
-            drawable = mSelectHandleLeft;
-            width = mSelectHandleLeft.getIntrinsicWidth();
-            height = mSelectHandleLeft.getIntrinsicHeight();
-            // Magic formula copied from TextView
-            offset = (width * 3) / 4;
-        } else {
-            drawable = mSelectHandleRight;
-            width = mSelectHandleRight.getIntrinsicWidth();
-            height = mSelectHandleRight.getIntrinsicHeight();
-            // Magic formula copied from TextView
-            offset = width / 4;
-        }
-        int x = contentToViewDimension(point.x);
-        int y = contentToViewDimension(point.y);
-        bounds.set(x - offset, y, x - offset + width, y + height);
-        drawable.setBounds(bounds);
-        drawable.setAlpha(alpha);
-        drawable.draw(canvas);
-    }
-
-    private void drawTextSelectionHandles(Canvas canvas) {
-        if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) {
-            return;
-        }
-        ensureSelectionHandles();
-        if (mIsCaretSelection) {
-            // Caret handle is centered
-            int x = contentToViewDimension(mSelectCursorBase.x) -
-                    (mSelectHandleCenter.getIntrinsicWidth() / 2);
-            int y = contentToViewDimension(mSelectCursorBase.y);
-            mSelectHandleBaseBounds.set(x, y,
-                    x + mSelectHandleCenter.getIntrinsicWidth(),
-                    y + mSelectHandleCenter.getIntrinsicHeight());
-            mSelectHandleCenter.setBounds(mSelectHandleBaseBounds);
-            mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha());
-            mSelectHandleCenter.draw(canvas);
-        } else {
-            drawHandle(mSelectCursorBase, HANDLE_ID_BASE,
-                    mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas);
-            drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT,
-                    mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas);
-        }
-    }
-
-    private boolean isHandleVisible(Point selectionPoint, int layerId) {
-        boolean isVisible = true;
-        if (mIsEditingText) {
-            isVisible = mEditTextContentBounds.contains(selectionPoint.x,
-                    selectionPoint.y);
-        }
-        if (isVisible) {
-            isVisible = nativeIsPointVisible(mNativeClass, layerId,
-                    selectionPoint.x, selectionPoint.y);
-        }
-        return isVisible;
-    }
-
-    /**
-     * 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.x;
-        handles[1] = mSelectCursorBase.y;
-        handles[2] = mSelectCursorExtent.x;
-        handles[3] = mSelectCursorExtent.y;
-    }
-
-    // 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();
-            }
-        }
-    }
-
-    /**
-     *  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();
-            mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection);
-        }
-        mInputConnection.setupEditorInfo(outAttrs);
-        return mInputConnection;
-    }
-
-    private void relocateAutoCompletePopup() {
-        if (mAutoCompletePopup != null) {
-            mAutoCompletePopup.resetRect();
-            mAutoCompletePopup.setText(mInputConnection.getEditable());
-        }
-    }
-
-    /**
-     * 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);
-        }
-        // 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))) {
-            imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
-        }
-    }
-
-    /**
-     * Called by AutoCompletePopup 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();
-        }
-    }
-
-    /*
-     * This class requests an Adapter for the AutoCompletePopup 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 AutoCompletePopup 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());
-                    mAutoCompletePopup.setIsAutoFillProfileSet(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());
-                    mAutoCompletePopup.setIsAutoFillProfileSet(false);
-                }
-            }
-
-            if (mAutoComplete) {
-                pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
-            }
-
-            if (pastEntries.size() > 0) {
-                ArrayAdapter<String> adapter = new ArrayAdapter<String>(
-                        mContext,
-                        com.android.internal.R.layout.web_text_view_dropdown,
-                        pastEntries);
-                mUpdateMessage.obj = adapter;
-                mUpdateMessage.sendToTarget();
-            }
-        }
-    }
-
-    /**
-     * Dump the display tree to "/sdcard/displayTree.txt"
-     *
-     * 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"
-     *
-     * 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"
-     *
-     * 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.
-     *
-     * debug only
-     */
-    public void setUseMockDeviceOrientation() {
-        mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION);
-    }
-
-    /**
-     * Sets use of the Geolocation mock client. Also resets that client. Called
-     * by DRT on UI thread, need to proxy to WebCore thread.
-     *
-     * debug only
-     */
-    public void setUseMockGeolocation() {
-        mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * debug only
-     */
-    public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
-        mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * debug only
-     */
-    public void setMockGeolocationError(int code, String message) {
-        mWebViewCore.setMockGeolocationError(code, message);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * debug only
-     */
-    public void setMockGeolocationPermission(boolean allow) {
-        mWebViewCore.setMockGeolocationPermission(allow);
-    }
-
-    /**
-     * Called by DRT on WebCore thread.
-     *
-     * 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
-            sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
-            sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, 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;
-    }
-
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (mAutoCompletePopup != null) {
-            return mAutoCompletePopup.onKeyPreIme(keyCode, event);
-        }
-        return false;
-    }
-
-    @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;
-        }
-
-        // See if the accessibility injector needs to handle this event.
-        if (isAccessibilityInjectionEnabled()
-                && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
-            return true;
-        }
-
-        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 (isEnterActionKey(keyCode)) {
-            switchOutDrawHistory();
-            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 (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;
-            }
-        }
-
-        // pass the key to DOM
-        sendKeyEvent(event);
-        // return true as DOM handles the key
-        return true;
-    }
-
-    @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
-                && mInitialHitTestResult != null
-                && mInitialHitTestResult.getType() == HitTestResult.PHONE_TYPE) {
-            String text = mInitialHitTestResult.getExtra();
-            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;
-        }
-
-        // See if the accessibility injector needs to handle this event.
-        if (isAccessibilityInjectionEnabled()
-                && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
-            return true;
-        }
-
-        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
-            }
-        }
-
-        // pass the key to DOM
-        sendKeyEvent(event);
-        // return true as DOM handles the key
-        return true;
-    }
-
-    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()) {
-            Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
-                    contentToViewY(mSelectCursorBase.y));
-            Point cursorTop = calculateBaseCaretTop();
-            cursorTop.set(contentToViewX(cursorTop.x),
-                    contentToViewY(cursorTop.y));
-
-            int[] location = new int[2];
-            mWebView.getLocationInWindow(location);
-            int offsetX = location[0] - getScrollX();
-            int offsetY = location[1] - getScrollY();
-            cursorPoint.offset(offsetX, offsetY);
-            cursorTop.offset(offsetX, offsetY);
-            if (mPasteWindow == null) {
-                mPasteWindow = new PastePopupWindow();
-            }
-            mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]);
-        }
-    }
-
-    /**
-     * Given segment AB, this finds the point C along AB that is closest to
-     * point and then returns it scale along AB. The scale factor is AC/AB.
-     *
-     * @param x The x coordinate of the point near segment AB that determines
-     * the scale factor.
-     * @param y The y coordinate of the point near segment AB that determines
-     * the scale factor.
-     * @param a The first point of the line segment.
-     * @param b The second point of the line segment.
-     * @return The scale factor AC/AB, where C is the point on AB closest to
-     *         point.
-     */
-    private static float scaleAlongSegment(int x, int y, PointF a, PointF b) {
-        // The bottom line of the text box is line AB
-        float abX = b.x - a.x;
-        float abY = b.y - a.y;
-        float ab2 = (abX * abX) + (abY * abY);
-
-        // The line from first point in text bounds to bottom is AP
-        float apX = x - a.x;
-        float apY = y - a.y;
-        float abDotAP = (apX * abX) + (apY * abY);
-        float scale = abDotAP / ab2;
-        return scale;
-    }
-
-    private Point calculateBaseCaretTop() {
-        return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad);
-    }
-
-    private Point calculateDraggingCaretTop() {
-        return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad);
-    }
-
-    /**
-     * Assuming arbitrary shape of a quadralateral forming text bounds, this
-     * calculates the top of a caret.
-     */
-    private static Point calculateCaretTop(Point base, QuadF quad) {
-        float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3);
-        int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x));
-        int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y));
-        return new Point(x, y);
-    }
-
-    private void hidePasteButton() {
-        if (mPasteWindow != null) {
-            mPasteWindow.hide();
-        }
-    }
-
-    private void syncSelectionCursors() {
-        mSelectCursorBaseLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE,
-                        mSelectCursorBase, mSelectCursorBaseTextQuad);
-        mSelectCursorExtentLayerId =
-                nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT,
-                        mSelectCursorExtent, mSelectCursorExtentTextQuad);
-    }
-
-    private boolean setupWebkitSelect() {
-        syncSelectionCursors();
-        if (!mIsCaretSelection && !startSelectActionMode()) {
-            selectionDone();
-            return false;
-        }
-        startSelectingText();
-        mTouchMode = TOUCH_DRAG_MODE;
-        return true;
-    }
-
-    private void updateWebkitSelection(boolean isSnapped) {
-        int handleId = (mSelectDraggingCursor == mSelectCursorBase)
-                ? HANDLE_ID_BASE : HANDLE_ID_EXTENT;
-        int x = mSelectDraggingCursor.x;
-        int y = mSelectDraggingCursor.y;
-        if (isSnapped) {
-            // "center" the cursor in the snapping quad
-            Point top = calculateDraggingCaretTop();
-            x = Math.round((top.x + x) / 2);
-            y = Math.round((top.y + y) / 2);
-        }
-        mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
-        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT,
-                x, y, (Integer)handleId);
-    }
-
-    private void resetCaretTimer() {
-        mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-        if (!mSelectionStarted) {
-            mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
-                    CARET_HANDLE_STAMINA_MS);
-        }
-    }
-
-    /**
-     * Select all of the text in this WebView.
-     *
-     * 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();
-            endSelectingText();
-            // finish is idempotent, so this is fine even if selectionDone was
-            // called by mSelectCallback.onDestroyActionMode
-            if (mSelectCallback != null) {
-                mSelectCallback.finish();
-                mSelectCallback = null;
-            }
-            invalidate(); // redraw without selection
-            mAutoScrollX = 0;
-            mAutoScrollY = 0;
-            mSentAutoScrollMessage = false;
-        }
-    }
-
-    /**
-     * Copy the selection to the clipboard
-     *
-     * 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
-     *
-     * 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.
-     *
-     * 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.coerceToText(mContext);
-            if (mInputConnection != null) {
-                mInputConnection.replaceSelection(pasteText);
-            }
-        }
-    }
-
-    /**
-     * 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);
-
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().toggleAccessibilityFeedback(true);
-        }
-
-        updateHwAccelerated();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        clearHelpers();
-        mZoomManager.dismissZoomPicker();
-        if (mWebView.hasWindowFocus()) setActive(false);
-
-        if (isAccessibilityInjectionEnabled()) {
-            getAccessibilityInjector().toggleAccessibilityFeedback(false);
-        }
-
-        updateHwAccelerated();
-
-        ensureFunctorDetached();
-    }
-
-    @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 = true;
-                setFocusControllerActive(true);
-            } else {
-                mDrawCursorRing = false;
-                setFocusControllerActive(false);
-            }
-        } 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) {
-            mDrawCursorRing = true;
-            setFocusControllerActive(true);
-        } else {
-            mDrawCursorRing = false;
-            setFocusControllerActive(false);
-            mKeysPressed.clear();
-        }
-        if (!mTouchHighlightRegion.isEmpty()) {
-            mWebView.invalidate(mTouchHighlightRegion.getBounds());
-        }
-    }
-
-    // updateRectsForGL() happens almost every draw call, in order to avoid creating
-    // any object in this code path, we move the local variable out to be a private
-    // final member, and we marked them as mTemp*.
-    private final Point mTempVisibleRectOffset = new Point();
-    private final Rect mTempVisibleRect = new Rect();
-
-    void updateRectsForGL() {
-        // 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(mTempVisibleRect, mTempVisibleRectOffset);
-        mInvScreenRect.set(mTempVisibleRect);
-        if (visible) {
-            // Then need to invert the Y axis, just for GL
-            View rootView = mWebView.getRootView();
-            int rootViewHeight = rootView.getHeight();
-            mScreenRect.set(mInvScreenRect);
-            int savedWebViewBottom = mInvScreenRect.bottom;
-            mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl();
-            mInvScreenRect.top = rootViewHeight - savedWebViewBottom;
-            mIsWebViewVisible = true;
-        } else {
-            mIsWebViewVisible = false;
-        }
-
-        mTempVisibleRect.offset(-mTempVisibleRectOffset.x, -mTempVisibleRectOffset.y);
-        viewToContentVisibleRect(mVisibleContentRect, mTempVisibleRect);
-
-        nativeUpdateDrawGLFunction(mNativeClass, mIsWebViewVisible ? mInvScreenRect : null,
-                mIsWebViewVisible ? mScreenRect : null,
-                mVisibleContentRect, getScale());
-    }
-
-    // Input : viewRect, rect in view/screen coordinate.
-    // Output: contentRect, rect in content/document coordinate.
-    private void viewToContentVisibleRect(RectF contentRect, Rect viewRect) {
-        contentRect.left = viewToContentXf(viewRect.left) / mWebView.getScaleX();
-        // 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.
-        contentRect.top = viewToContentYf(viewRect.top + getVisibleTitleHeightImpl())
-                / mWebView.getScaleY();
-        contentRect.right = viewToContentXf(viewRect.right) / mWebView.getScaleX();
-        contentRect.bottom = viewToContentYf(viewRect.bottom) / mWebView.getScaleY();
-    }
-
-    @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);
-        }
-        updateRectsForGL();
-        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);
-        }
-        if (mIsEditingText) {
-            scrollEditIntoView();
-        }
-        relocateAutoCompletePopup();
-    }
-
-    /**
-     * Scrolls the edit field into view using the minimum scrolling necessary.
-     * If the edit field is too large to fit in the visible window, the caret
-     * dimensions are used so that at least the caret is visible.
-     * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the
-     * edit rectangle to ensure a margin with the edge of the screen.
-     */
-    private void scrollEditIntoView() {
-        Rect visibleRect = new Rect(viewToContentX(getScrollX()),
-                viewToContentY(getScrollY()),
-                viewToContentX(getScrollX() + getWidth()),
-                viewToContentY(getScrollY() + getViewHeightWithTitle()));
-        if (visibleRect.contains(mEditTextContentBounds)) {
-            return; // no need to scroll
-        }
-        syncSelectionCursors();
-        nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect);
-        final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER));
-        Rect showRect = new Rect(
-                Math.max(0, mEditTextContentBounds.left - buffer),
-                Math.max(0, mEditTextContentBounds.top - buffer),
-                mEditTextContentBounds.right + buffer,
-                mEditTextContentBounds.bottom + buffer);
-        Point caretTop = calculateBaseCaretTop();
-        if (visibleRect.width() < mEditTextContentBounds.width()) {
-            // The whole edit won't fit in the width, so use the caret rect
-            if (mSelectCursorBase.x < caretTop.x) {
-                showRect.left = Math.max(0, mSelectCursorBase.x - buffer);
-                showRect.right = caretTop.x + buffer;
-            } else {
-                showRect.left = Math.max(0, caretTop.x - buffer);
-                showRect.right = mSelectCursorBase.x + buffer;
-            }
-        }
-        if (visibleRect.height() < mEditTextContentBounds.height()) {
-            // The whole edit won't fit in the height, so use the caret rect
-            if (mSelectCursorBase.y > caretTop.y) {
-                showRect.top = Math.max(0, caretTop.y - buffer);
-                showRect.bottom = mSelectCursorBase.y + buffer;
-            } else {
-                showRect.top = Math.max(0, mSelectCursorBase.y - buffer);
-                showRect.bottom = caretTop.y + buffer;
-            }
-        }
-
-        if (visibleRect.contains(showRect)) {
-            return; // no need to scroll
-        }
-
-        int scrollX = viewToContentX(getScrollX());
-        if (visibleRect.left > showRect.left) {
-            // We are scrolled too far
-            scrollX += showRect.left - visibleRect.left;
-        } else if (visibleRect.right < showRect.right) {
-            // We aren't scrolled enough to include the right
-            scrollX += showRect.right - visibleRect.right;
-        }
-        int scrollY = viewToContentY(getScrollY());
-        if (visibleRect.top > showRect.top) {
-            scrollY += showRect.top - visibleRect.top;
-        } else if (visibleRect.bottom < showRect.bottom) {
-            scrollY += showRect.bottom - visibleRect.bottom;
-        }
-
-        contentScrollTo(scrollX, scrollY, 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;
-        }
-        return mWebViewPrivate.super_dispatchKeyEvent(event);
-    }
-    
-    private static final int SNAP_BOUND = 16;
-    private static int sChannelDistance = 16;
-    private int mFirstTouchX = -1; // the first touched point
-    private int mFirstTouchY = -1;
-    private int mDistanceX = 0;
-    private int mDistanceY = 0;
-
-    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) {
-        if (mNativeClass == 0)
-            return;
-
-        int contentX = viewToContentX((int) x + getScrollX());
-        int contentY = viewToContentY((int) y + getScrollY());
-        mCurrentScrollingLayerId = nativeScrollableLayer(mNativeClass,
-                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;
-        }
-        int x = viewToContentX((int) event.getX() + getScrollX());
-        int y = viewToContentY((int) event.getY() + getScrollY());
-        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, x, y);
-        mWebViewPrivate.super_onHoverEvent(event);
-        return true;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
-            return false;
-        }
-
-        if (mInputDispatcher == null) {
-            return false;
-        }
-
-        if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode()
-                && !mWebView.isFocused()) {
-            mWebView.requestFocus();
-        }
-
-        if (mInputDispatcher.postPointerEvent(ev, getScrollX(),
-                getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) {
-            mInputDispatcher.dispatchUiEvents();
-            return true;
-        } else {
-            Log.w(LOGTAG, "mInputDispatcher rejected the event!");
-            return false;
-        }
-    }
-
-    /*
-    * 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 void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
-        ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
-
-        long eventTime = event.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: {
-                mConfirmMove = false;
-
-                // Channel Scrolling
-                mFirstTouchX = x;
-                mFirstTouchY = y;
-                mDistanceX = mDistanceY = 0;
-
-                if (!mEditTextScroller.isFinished()) {
-                    mEditTextScroller.abortAnimation();
-                }
-                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);
-                    removeTouchHighlight();
-                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
-                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
-                    } else {
-                        mTouchMode = TOUCH_INIT_MODE;
-                    }
-                } else { // the normal case
-                    mTouchMode = TOUCH_INIT_MODE;
-                    if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
-                        EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
-                                (eventTime - mLastTouchUpTime), eventTime);
-                    }
-                    mSelectionStarted = false;
-                    if (mSelectingText) {
-                        ensureSelectionHandles();
-                        int shiftedY = y - getTitleHeight() + getScrollY();
-                        int shiftedX = x + getScrollX();
-                        if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorBase;
-                            mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
-                            if (mIsCaretSelection) {
-                                mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
-                                hidePasteButton();
-                            }
-                        } else if (mSelectHandleExtentBounds
-                                .contains(shiftedX, shiftedY)) {
-                            mSelectionStarted = true;
-                            mSelectDraggingCursor = mSelectCursorExtent;
-                            mSelectDraggingTextQuad = mSelectCursorExtentTextQuad;
-                        } else if (mIsCaretSelection) {
-                            selectionDone();
-                        }
-                        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);
-                }
-                startTouch(x, y, eventTime);
-                if (mIsEditingText) {
-                    mTouchInEditText = mEditTextContentBounds
-                            .contains(contentX, contentY);
-                }
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
-                        >= mTouchSlopSquare) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                    mConfirmMove = true;
-                    if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
-                        mTouchMode = TOUCH_INIT_MODE;
-                    }
-                    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) {
-                        int handleX = contentX +
-                                viewToContentDimension(mSelectOffset.x);
-                        int handleY = contentY +
-                                viewToContentDimension(mSelectOffset.y);
-                        mSelectDraggingCursor.set(handleX, handleY);
-                        boolean inCursorText =
-                                mSelectDraggingTextQuad.containsPoint(handleX, handleY);
-                        boolean inEditBounds = mEditTextContentBounds
-                                .contains(handleX, handleY);
-                        if (mIsEditingText && !inEditBounds) {
-                            beginScrollEdit();
-                        } else {
-                            endScrollEdit();
-                        }
-                        boolean snapped = false;
-                        if (inCursorText || (mIsEditingText && !inEditBounds)) {
-                            snapDraggingCursor();
-                            snapped = true;
-                        }
-                        updateWebkitSelection(snapped);
-                        if (!inCursorText && mIsEditingText && inEditBounds) {
-                            // Visually snap even if we have moved the handle.
-                            snapDraggingCursor();
-                        }
-                        mLastTouchX = x;
-                        mLastTouchY = y;
-                        invalidate();
-                    }
-                    break;
-                }
-
-                if (mTouchMode == TOUCH_DONE_MODE) {
-                    // no dragging during scroll zoom animation, or when prevent
-                    // default is yes
-                    break;
-                }
-                if (mVelocityTracker == null) {
-                    Log.e(LOGTAG, "Got null mVelocityTracker when "
-                            + " mTouchMode = " + mTouchMode);
-                } else {
-                    mVelocityTracker.addMovement(event);
-                }
-
-                if (mTouchMode != TOUCH_DRAG_MODE &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE &&
-                        mTouchMode != TOUCH_DRAG_TEXT_MODE) {
-
-                    if (!mConfirmMove) {
-                        break;
-                    }
-
-                    if ((detector == null || !detector.isInProgress())
-                            && SNAP_NONE == mSnapScrollMode) {
-                        int ax = Math.abs(x - mFirstTouchX);
-                        int ay = Math.abs(y - mFirstTouchY);
-                        if (ax < SNAP_BOUND && ay < SNAP_BOUND) {
-                            break;
-                        } else if (ax < SNAP_BOUND) {
-                            mSnapScrollMode = SNAP_Y;
-                        } else if (ay < SNAP_BOUND) {
-                            mSnapScrollMode = SNAP_X;
-                        }
-                    }
-
-                    mTouchMode = TOUCH_DRAG_MODE;
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-                    deltaX = 0;
-                    deltaY = 0;
-
-                    startScrollingLayer(x, y);
-                    startDrag();
-                }
-
-                // do pan
-                boolean keepScrollBarsVisible = false;
-                if (deltaX == 0 && deltaY == 0) {
-                    keepScrollBarsVisible = true;
-                } else {
-                    if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
-                        mDistanceX += Math.abs(deltaX);
-                        mDistanceY += Math.abs(deltaY);
-                        if (mSnapScrollMode == SNAP_X) {
-                            if (mDistanceY > sChannelDistance) {
-                                mSnapScrollMode = SNAP_NONE;
-                            } else if (mDistanceX > sChannelDistance) {
-                                mDistanceX = mDistanceY = 0;
-                        }
-                    } else {
-                            if (mDistanceX > sChannelDistance) {
-                                mSnapScrollMode = SNAP_NONE;
-                            } else if (mDistanceY > sChannelDistance) {
-                                mDistanceX = mDistanceY = 0;
-                            }
-                        }
-                    }
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
-                            deltaY = 0;
-                        } else {
-                            deltaX = 0;
-                        }
-                    }
-                    if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
-                        mHeldMotionless = MOTIONLESS_FALSE;
-                    } else {
-                        mHeldMotionless = MOTIONLESS_TRUE;
-                        keepScrollBarsVisible = true;
-                    }
-
-                    mLastTouchTime = eventTime;
-                    boolean allDrag = doDrag(deltaX, deltaY);
-                    if (allDrag) {
-                        mLastTouchX = x;
-                        mLastTouchY = y;
-                    } else {
-                        int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
-                        int roundedDeltaX = contentToViewDimension(contentDeltaX);
-                        int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
-                        int roundedDeltaY = contentToViewDimension(contentDeltaY);
-                        mLastTouchX -= roundedDeltaX;
-                        mLastTouchY -= roundedDeltaY;
-                    }
-                }
-
-                break;
-            }
-            case MotionEvent.ACTION_UP: {
-                mFirstTouchX  = mFirstTouchY = -1;
-                if (mIsEditingText && mSelectionStarted) {
-                    endScrollEdit();
-                    mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW,
-                            TEXT_SCROLL_FIRST_SCROLL_MS);
-                    if (!mConfirmMove && mIsCaretSelection) {
-                        showPasteWindow();
-                        stopTouch();
-                        break;
-                    }
-                }
-                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);
-                        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) {
-                            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());
-                            }
-                            break;
-                        }
-                    case TOUCH_DRAG_MODE:
-                    case TOUCH_DRAG_LAYER_MODE:
-                    case TOUCH_DRAG_TEXT_MODE:
-                        mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
-                        // 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");
-                            } else {
-                                mVelocityTracker.addMovement(event);
-                            }
-                            // 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();
-                }
-                cancelTouch();
-                break;
-            }
-        }
-    }
-
-    /**
-     * Returns the text scroll speed in content pixels per millisecond based on
-     * the touch location.
-     * @param coordinate The x or y touch coordinate in content space
-     * @param min The minimum coordinate (x or y) of the edit content bounds
-     * @param max The maximum coordinate (x or y) of the edit content bounds
-     */
-    private static float getTextScrollSpeed(int coordinate, int min, int max) {
-        if (coordinate < min) {
-            return (coordinate - min) * TEXT_SCROLL_RATE;
-        } else if (coordinate >= max) {
-            return (coordinate - max + 1) * TEXT_SCROLL_RATE;
-        } else {
-            return 0.0f;
-        }
-    }
-
-    private static int getSelectionCoordinate(int coordinate, int min, int max) {
-        return Math.max(Math.min(coordinate, max), min);
-    }
-
-    private void beginScrollEdit() {
-        if (mLastEditScroll == 0) {
-            mLastEditScroll = SystemClock.uptimeMillis() -
-                    TEXT_SCROLL_FIRST_SCROLL_MS;
-            scrollEditWithCursor();
-        }
-    }
-
-    private void scrollDraggedSelectionHandleIntoView() {
-        if (mSelectDraggingCursor == null) {
-            return;
-        }
-        int x = mSelectDraggingCursor.x;
-        int y = mSelectDraggingCursor.y;
-        if (!mEditTextContentBounds.contains(x,y)) {
-            int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER);
-            int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER);
-            int deltaX = left + right;
-            int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER);
-            int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER);
-            int deltaY = above + below;
-            if (deltaX != 0 || deltaY != 0) {
-                int scrollX = getTextScrollX() + deltaX;
-                int scrollY = getTextScrollY() + deltaY;
-                scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
-                scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
-                scrollEditText(scrollX, scrollY);
-            }
-        }
-    }
-
-    private void endScrollEdit() {
-        mLastEditScroll = 0;
-    }
-
-    private static int clampBetween(int value, int min, int max) {
-        return Math.max(min, Math.min(value, max));
-    }
-
-    private static int getTextScrollDelta(float speed, long deltaT) {
-        float distance = speed * deltaT;
-        int intDistance = (int)Math.floor(distance);
-        float probability = distance - intDistance;
-        if (Math.random() < probability) {
-            intDistance++;
-        }
-        return intDistance;
-    }
-    /**
-     * Scrolls edit text a distance based on the last touch point,
-     * the last scroll time, and the edit text content bounds.
-     */
-    private void scrollEditWithCursor() {
-        if (mLastEditScroll != 0) {
-            int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x);
-            float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left,
-                    mEditTextContentBounds.right);
-            int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y);
-            float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top,
-                    mEditTextContentBounds.bottom);
-            if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) {
-                endScrollEdit();
-            } else {
-                long currentTime = SystemClock.uptimeMillis();
-                long timeSinceLastUpdate = currentTime - mLastEditScroll;
-                int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate);
-                int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate);
-                int scrollX = getTextScrollX() + deltaX;
-                scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
-                int scrollY = getTextScrollY() + deltaY;
-                scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
-
-                mLastEditScroll = currentTime;
-                if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) {
-                    // By probability no text scroll this time. Try again later.
-                    mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT,
-                            TEXT_SCROLL_FIRST_SCROLL_MS);
-                } else {
-                    int selectionX = getSelectionCoordinate(x,
-                            mEditTextContentBounds.left, mEditTextContentBounds.right);
-                    int selectionY = getSelectionCoordinate(y,
-                            mEditTextContentBounds.top, mEditTextContentBounds.bottom);
-                    int oldX = mSelectDraggingCursor.x;
-                    int oldY = mSelectDraggingCursor.y;
-                    mSelectDraggingCursor.set(selectionX, selectionY);
-                    updateWebkitSelection(false);
-                    scrollEditText(scrollX, scrollY);
-                    mSelectDraggingCursor.set(oldX, oldY);
-                }
-            }
-        }
-    }
-
-    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;
-    }
-
-    private void startDrag() {
-        WebViewCore.reducePriority();
-        // to get better performance, pause updating the picture
-        WebViewCore.pauseUpdatePicture(mWebViewCore);
-        nativeSetIsScrolling(true);
-
-        if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
-                || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
-            mZoomManager.invokeZoomPicker();
-        }
-    }
-
-    private boolean doDrag(int deltaX, int deltaY) {
-        boolean allDrag = true;
-        if ((deltaX | deltaY) != 0) {
-            int oldX = getScrollX();
-            int oldY = getScrollY();
-            int rangeX = computeMaxScrollX();
-            int rangeY = computeMaxScrollY();
-            final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
-            final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
-
-            // Assume page scrolling and change below if we're wrong
-            mTouchMode = TOUCH_DRAG_MODE;
-
-            // Check special scrolling before going to main page scrolling.
-            if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) {
-                // Edit text scrolling
-                oldX = getTextScrollX();
-                rangeX = getMaxTextScrollX();
-                deltaX = contentX;
-                oldY = getTextScrollY();
-                rangeY = getMaxTextScrollY();
-                deltaY = contentY;
-                mTouchMode = TOUCH_DRAG_TEXT_MODE;
-                allDrag = false;
-            } else if (mCurrentScrollingLayerId != 0) {
-                // 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 = clampBetween(maxX, 0,
-                        mScrollingLayerRect.left + contentX);
-                final int resultY = clampBetween(maxY, 0,
-                        mScrollingLayerRect.top + contentY);
-
-                if (resultX != mScrollingLayerRect.left
-                        || resultY != mScrollingLayerRect.top
-                        || (contentX | contentY) == 0) {
-                    // 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;
-                    allDrag = false;
-                }
-            }
-
-            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();
-        return allDrag;
-    }
-
-    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();
-            }
-            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);
-        removeTouchHighlight();
-        mHeldMotionless = MOTIONLESS_TRUE;
-        mTouchMode = TOUCH_DONE_MODE;
-    }
-
-    private void snapDraggingCursor() {
-        float scale = scaleAlongSegment(
-                mSelectDraggingCursor.x, mSelectDraggingCursor.y,
-                mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3);
-        // clamp scale to ensure point is on the bottom segment
-        scale = Math.max(0.0f, scale);
-        scale = Math.min(scale, 1.0f);
-        float newX = scaleCoordinate(scale,
-                mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x);
-        float newY = scaleCoordinate(scale,
-                mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y);
-        int x = Math.round(newX);
-        int y = Math.round(newY);
-        if (mIsEditingText) {
-            x = clampBetween(x, mEditTextContentBounds.left,
-                    mEditTextContentBounds.right);
-            y = clampBetween(y, mEditTextContentBounds.top,
-                    mEditTextContentBounds.bottom);
-        }
-        mSelectDraggingCursor.set(x, y);
-    }
-
-    private static float scaleCoordinate(float scale, float coord1, float coord2) {
-        float diff = coord2 - coord1;
-        return coord1 + (scale * diff);
-    }
-
-    @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());
-
-                        abortAnimation();
-                        int oldTouchMode = mTouchMode;
-                        startScrollingLayer(event.getX(), event.getY());
-                        doDrag(hdelta, vdelta);
-                        mTouchMode = oldTouchMode;
-                        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 mShowTextSelectionExtra = 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 mTrackballDown = false;
-    private long mTrackballUpTime = 0;
-    private long mLastCursorTime = 0;
-    private Rect mLastCursorBounds;
-    private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha();
-    private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha();
-    private ObjectAnimator mBaseHandleAlphaAnimator =
-            ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0);
-    private ObjectAnimator mExtentHandleAlphaAnimator =
-            ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0);
-
-    // 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;
-
-    @Override
-    public void setMapTrackballToArrowKeys(boolean setMap) {
-        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 (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;
-        }
-        return 0;
-    }
-
-    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) {
-                for (int i = 0; i < count; i++) {
-                    letPageHandleNavKey(selectKeyCode, time, true, metaState);
-                }
-                letPageHandleNavKey(selectKeyCode, time, false, metaState);
-            }
-            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;
-        }
-    }
-
-    @Override
-    public void flingScroll(int vx, int vy) {
-        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;
-        } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
-            scrollX = getTextScrollX();
-            scrollY = getTextScrollY();
-            maxX = getMaxTextScrollX();
-            maxY = getMaxTextScrollY();
-            // No overscrolling for edit text.
-            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);
-
-        invalidate();
-    }
-
-    /**
-     * See {@link WebView#getZoomControls()}
-     */
-    @Override
-    @Deprecated
-    public View getZoomControls() {
-        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();
-    }
-
-    /**
-     * See {@link WebView#canZoomIn()}
-     */
-    @Override
-    public boolean canZoomIn() {
-        return mZoomManager.canZoomIn();
-    }
-
-    /**
-     * See {@link WebView#canZoomOut()}
-     */
-    @Override
-    public boolean canZoomOut() {
-        return mZoomManager.canZoomOut();
-    }
-
-    /**
-     * See {@link WebView#zoomIn()}
-     */
-    @Override
-    public boolean zoomIn() {
-        return mZoomManager.zoomIn();
-    }
-
-    /**
-     * See {@link WebView#zoomOut()}
-     */
-    @Override
-    public boolean zoomOut() {
-        return mZoomManager.zoomOut();
-    }
-
-    /*
-     * 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) {
-        // Check if we are destroyed
-        if (mWebViewCore == null) return false;
-        // 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;
-        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;
-            }
-            mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection);
-        }
-        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
-        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()));
-        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;
-        sendBatchableInputMessage(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, null, 1000);
-    }
-
-    public synchronized WebViewCore getWebViewCore() {
-        return mWebViewCore;
-    }
-
-    private boolean canTextScroll(int directionX, int directionY) {
-        int scrollX = getTextScrollX();
-        int scrollY = getTextScrollY();
-        int maxScrollX = getMaxTextScrollX();
-        int maxScrollY = getMaxTextScrollY();
-        boolean canScrollX = (directionX > 0)
-                ? (scrollX < maxScrollX)
-                : (scrollX > 0);
-        boolean canScrollY = (directionY > 0)
-                ? (scrollY < maxScrollY)
-                : (scrollY > 0);
-        return canScrollX || canScrollY;
-    }
-
-    private int getTextScrollX() {
-        return -mEditTextContent.left;
-    }
-
-    private int getTextScrollY() {
-        return -mEditTextContent.top;
-    }
-
-    private int getMaxTextScrollX() {
-        return Math.max(0, mEditTextContent.width() - mEditTextContentBounds.width());
-    }
-
-    private int getMaxTextScrollY() {
-        return Math.max(0, mEditTextContent.height() - mEditTextContentBounds.height());
-    }
-
-    //-------------------------------------------------------------------------
-    // 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 implements WebViewInputDispatcher.UiCallbacks {
-        @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 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 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)) {
-                            break;
-                        }
-                    }
-                    final Point p = (Point) msg.obj;
-                    contentScrollTo(p.x, p.y, msg.arg1 == 1);
-                    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 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);
-                    nativeCreate(msg.arg1, drawableDir, ActivityManager.isHighEndGfx());
-                    if (mDelaySetPicture != null) {
-                        setNewPicture(mDelaySetPicture, true);
-                        mDelaySetPicture = null;
-                    }
-                    if (mIsPaused) {
-                        nativeSetPauseDrawing(mNativeClass, true);
-                    }
-                    mInputDispatcher = new WebViewInputDispatcher(this,
-                            mWebViewCore.getInputDispatcherCallbacks());
-                    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 (mInputConnection != null &&
-                                mFieldPointer == msg.arg1) {
-                            mInputConnection.setTextAndKeepSelection(text);
-                        }
-                    }
-                    break;
-                case UPDATE_TEXT_SELECTION_MSG_ID:
-                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
-                            (WebViewCore.TextSelectionData) msg.obj);
-                    break;
-                case TAKE_FOCUS:
-                    int direction = msg.arg1;
-                    View focusSearch = mWebView.focusSearch(direction);
-                    if (focusSearch != null && focusSearch != mWebView) {
-                        focusSearch.requestFocus();
-                    }
-                    break;
-                case CLEAR_TEXT_ENTRY:
-                    hideSoftKeyboard();
-                    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:
-                    if (mFieldPointer == msg.arg1) {
-                        ArrayAdapter<String> adapter = (ArrayAdapter<String>)msg.obj;
-                        mAutoCompletePopup.setAdapter(adapter);
-                    }
-                    break;
-
-                case LONG_PRESS_CENTER:
-                    // as this is shared by keydown and trackballdown, reset all
-                    // the states
-                    mGotCenterDown = false;
-                    mTrackballDown = false;
-                    mWebView.performLongClick();
-                    break;
-
-                case WEBCORE_NEED_TOUCH_EVENTS:
-                    mInputDispatcher.setWebKitWantsTouchEvents(msg.arg1 != 0);
-                    break;
-
-                case REQUEST_KEYBOARD:
-                    if (msg.arg1 == 0) {
-                        hideSoftKeyboard();
-                    } else {
-                        displaySoftKeyboard(false);
-                    }
-                    break;
-
-                case DRAG_HELD_MOTIONLESS:
-                    mHeldMotionless = MOTIONLESS_TRUE;
-                    invalidate();
-                    break;
-
-                case SCREEN_ON:
-                    mWebView.setKeepScreenOn(msg.arg1 == 1);
-                    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 SHOW_RECT_MSG_ID: {
-                    WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
-                    int left = contentToViewX(data.mLeft);
-                    int width = contentToViewDimension(data.mWidth);
-                    int maxWidth = contentToViewDimension(data.mContentWidth);
-                    int viewWidth = getViewWidth();
-                    int x = (int) (left + data.mXPercentInDoc * width -
-                                   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 FOCUS_NODE_CHANGED:
-                    mIsEditingText = (msg.arg1 == mFieldPointer);
-                    if (mAutoCompletePopup != null && !mIsEditingText) {
-                        mAutoCompletePopup.clearAdapter();
-                    }
-                    // fall through to HIT_TEST_RESULT
-                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 (mInputConnection != null) {
-                        mInputConnection.setAutoFillable(mAutoFillData.getQueryId());
-                        mAutoCompletePopup.setAutoFillQueryId(mAutoFillData.getQueryId());
-                    }
-                    break;
-
-                case AUTOFILL_COMPLETE:
-                    if (mAutoCompletePopup != null) {
-                        ArrayList<String> pastEntries = new ArrayList<String>();
-                        mAutoCompletePopup.setAdapter(new ArrayAdapter<String>(
-                                mContext,
-                                com.android.internal.R.layout.web_text_view_dropdown,
-                                pastEntries));
-                    }
-                    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);
-                        mEditTextContentBounds.set(initData.mContentBounds);
-                        mEditTextLayerId = initData.mNodeLayerId;
-                        nativeMapLayerRect(mNativeClass, mEditTextLayerId,
-                                mEditTextContentBounds);
-                        mEditTextContent.set(initData.mClientRect);
-                        relocateAutoCompletePopup();
-                    }
-                    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);
-                    selectionDone();
-                    break;
-                }
-
-                case UPDATE_MATCH_COUNT: {
-                    WebViewCore.FindAllRequest request = (WebViewCore.FindAllRequest)msg.obj;
-                    if (request == null) {
-                        if (mFindCallback != null) {
-                            mFindCallback.updateMatchCount(0, 0, true);
-                        }
-                    } else if (request == mFindRequest) {
-                        int matchCount, matchIndex;
-                        synchronized (mFindRequest) {
-                            matchCount = request.mMatchCount;
-                            matchIndex = request.mMatchIndex;
-                        }
-                        if (mFindCallback != null) {
-                            mFindCallback.updateMatchCount(matchIndex, matchCount, false);
-                        }
-                        if (mFindListener != null) {
-                            mFindListener.onFindResultReceived(matchIndex, matchCount, true);
-                        }
-                    }
-                    break;
-                }
-
-                case CLEAR_CARET_HANDLE:
-                    if (mIsCaretSelection) {
-                        selectionDone();
-                    }
-                    break;
-
-                case KEY_PRESS:
-                    sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null);
-                    break;
-
-                case RELOCATE_AUTO_COMPLETE_POPUP:
-                    relocateAutoCompletePopup();
-                    break;
-
-                case AUTOFILL_FORM:
-                    mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM,
-                            msg.arg1, /* unused */0);
-                    break;
-
-                case EDIT_TEXT_SIZE_CHANGED:
-                    if (msg.arg1 == mFieldPointer) {
-                        mEditTextContent.set((Rect)msg.obj);
-                    }
-                    break;
-
-                case SHOW_CARET_HANDLE:
-                    if (!mSelectingText && mIsEditingText && mIsCaretSelection) {
-                        setupWebkitSelect();
-                        resetCaretTimer();
-                        showPasteWindow();
-                    }
-                    break;
-
-                case UPDATE_CONTENT_BOUNDS:
-                    mEditTextContentBounds.set((Rect) msg.obj);
-                    nativeMapLayerRect(mNativeClass, mEditTextLayerId,
-                            mEditTextContentBounds);
-                    break;
-
-                case SCROLL_EDIT_TEXT:
-                    scrollEditWithCursor();
-                    break;
-
-                case SCROLL_HANDLE_INTO_VIEW:
-                    scrollDraggedSelectionHandleIntoView();
-                    break;
-
-                default:
-                    super.handleMessage(msg);
-                    break;
-            }
-        }
-
-        @Override
-        public Looper getUiLooper() {
-            return getLooper();
-        }
-
-        @Override
-        public void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
-            onHandleUiEvent(event, eventType, flags);
-        }
-
-        @Override
-        public Context getContext() {
-            return WebViewClassic.this.getContext();
-        }
-
-        @Override
-        public boolean shouldInterceptTouchEvent(MotionEvent event) {
-            if (!mSelectingText) {
-                return false;
-            }
-            ensureSelectionHandles();
-            int y = Math.round(event.getY() - getTitleHeight() + getScrollY());
-            int x = Math.round(event.getX() + getScrollX());
-            boolean isPressingHandle;
-            if (mIsCaretSelection) {
-                isPressingHandle = mSelectHandleCenter.getBounds()
-                        .contains(x, y);
-            } else {
-                isPressingHandle =
-                        mSelectHandleBaseBounds.contains(x, y)
-                        || mSelectHandleExtentBounds.contains(x, y);
-            }
-            return isPressingHandle;
-        }
-
-        @Override
-        public void showTapHighlight(boolean show) {
-            if (mShowTapHighlight != show) {
-                mShowTapHighlight = show;
-                invalidate();
-            }
-        }
-
-        @Override
-        public void clearPreviousHitTest() {
-            setHitTestResult(null);
-        }
-    }
-
-    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 mDrawCursorRing && !mFocusedNode.mEditable;
-        }
-        if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
-            return false;
-        }
-        return mShowTapHighlight;
-    }
-
-
-    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 if (DebugFlags.WEB_VIEW) {
-                    Log.d(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);
-    }
-
-    long mLastSwapTime;
-    double mAverageSwapFps;
-
-    /** Called by JNI when pages are swapped (only occurs with hardware
-     * acceleration) */
-    protected void pageSwapCallback(boolean notifyAnimationStarted) {
-        if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
-            long now = System.currentTimeMillis();
-            long diff = now - mLastSwapTime;
-            mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
-            Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
-            mLastSwapTime = now;
-        }
-        mWebViewCore.resumeWebKitDraw();
-        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);
-        }
-
-        if (mPictureListener != null) {
-            // trigger picture listener for hardware layers. Software layers are
-            // triggered in setNewPicture
-            Picture picture = mContext.getApplicationInfo().targetSdkVersion <
-                    Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
-            if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
-            mPictureListener.onNewPicture(getWebView(), picture);
-        }
-    }
-
-    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,
-                    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;
-            contentScrollTo(scrollX, scrollY, false);
-            if (!mDrawHistory) {
-                // As we are on a new page, hide the keyboard
-                hideSoftKeyboard();
-            }
-        }
-        mSendScrollEvent = true;
-
-        int functor = 0;
-        boolean forceInval = isPictureAfterFirstLayout;
-        ViewRootImpl viewRoot = mWebView.getViewRootImpl();
-        if (mWebView.isHardwareAccelerated()
-                && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE
-                && viewRoot != null) {
-            functor = nativeGetDrawGLFunction(mNativeClass);
-            if (functor != 0) {
-                // force an invalidate if functor attach not successful
-                forceInval |= !viewRoot.attachFunctor(functor);
-            }
-        }
-
-        if (functor == 0
-                || forceInval
-                || mWebView.getLayerType() != View.LAYER_TYPE_NONE) {
-            // invalidate the screen so that the next repaint will show new content
-            // TODO: partial invalidate
-            mWebView.invalidate();
-        }
-
-        // update the zoom information based on the new picture
-        if (mZoomManager.onNewPicture(draw))
-            invalidate();
-
-        if (isPictureAfterFirstLayout) {
-            mViewManager.postReadyToDrawAll();
-        }
-        scrollEditWithCursor();
-
-        if (mPictureListener != null) {
-            if (!mWebView.isHardwareAccelerated()
-                    || mWebView.getLayerType() == View.LAYER_TYPE_SOFTWARE) {
-                // trigger picture listener for software layers. Hardware layers are
-                // triggered in pageSwapCallback
-                Picture picture = mContext.getApplicationInfo().targetSdkVersion <
-                        Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
-                if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
-                mPictureListener.onNewPicture(getWebView(), picture);
-            }
-        }
-    }
-
-    /**
-     * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
-     * and UPDATE_TEXT_SELECTION_MSG_ID.
-     */
-    private void updateTextSelectionFromMessage(int nodePointer,
-            int textGeneration, WebViewCore.TextSelectionData data) {
-        if (textGeneration == mTextGeneration) {
-            if (mInputConnection != null && mFieldPointer == nodePointer) {
-                mInputConnection.setSelection(data.mStart, data.mEnd);
-            }
-        }
-        nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
-
-        if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR)
-                || (!mSelectingText && data.mStart != data.mEnd
-                        && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) {
-            selectionDone();
-            mShowTextSelectionExtra = true;
-            invalidate();
-            return;
-        }
-
-        if (data.mSelectTextPtr != 0 &&
-                (data.mStart != data.mEnd ||
-                (mFieldPointer == nodePointer && mFieldPointer != 0) ||
-                (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) {
-            mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0;
-            mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0);
-            if (mIsCaretSelection &&
-                    (mInputConnection == null ||
-                    mInputConnection.getEditable().length() == 0)) {
-                // There's no text, don't show caret handle.
-                selectionDone();
-            } else {
-                if (!mSelectingText) {
-                    setupWebkitSelect();
-                } else {
-                    syncSelectionCursors();
-                }
-                animateHandles();
-                if (mIsCaretSelection) {
-                    resetCaretTimer();
-                }
-            }
-        } else {
-            selectionDone();
-        }
-        invalidate();
-    }
-
-    private void scrollEditText(int scrollX, int scrollY) {
-        // Scrollable edit text. Scroll it.
-        float maxScrollX = getMaxTextScrollX();
-        float scrollPercentX = ((float)scrollX)/maxScrollX;
-        mEditTextContent.offsetTo(-scrollX, -scrollY);
-        mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT);
-        mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
-                scrollY, (Float)scrollPercentX);
-        animateHandles();
-    }
-
-    private void beginTextBatch() {
-        mIsBatchingTextChanges = true;
-    }
-
-    private void commitTextBatch() {
-        if (mWebViewCore != null) {
-            mWebViewCore.sendMessages(mBatchedTextChanges);
-        }
-        mBatchedTextChanges.clear();
-        mIsBatchingTextChanges = false;
-    }
-
-    void sendBatchableInputMessage(int what, int arg1, int arg2,
-            Object obj) {
-        if (mWebViewCore == null) {
-            return;
-        }
-        Message message = Message.obtain(null, what, arg1, arg2, obj);
-        if (mIsBatchingTextChanges) {
-            mBatchedTextChanges.add(message);
-        } else {
-            mWebViewCore.sendMessage(message);
-        }
-    }
-
-    // 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() {
-            if (mWebViewCore == null
-                    || getWebView().getWindowToken() == null
-                    || getWebView().getViewRootImpl() == null) {
-                // We've been detached and/or destroyed since this was posted
-                return;
-            }
-            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);
-                        if (mListBoxDialog != null) {
-                            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) {
-                 if (mWebViewCore != null) {
-                    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));
-    }
-
-    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;
-        if (down) {
-            keyEventAction = KeyEvent.ACTION_DOWN;
-        } else {
-            keyEventAction = KeyEvent.ACTION_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);
-        sendKeyEvent(event);
-    }
-
-    private void sendKeyEvent(KeyEvent event) {
-        int direction = 0;
-        switch (event.getKeyCode()) {
-        case KeyEvent.KEYCODE_DPAD_DOWN:
-            direction = View.FOCUS_DOWN;
-            break;
-        case KeyEvent.KEYCODE_DPAD_UP:
-            direction = View.FOCUS_UP;
-            break;
-        case KeyEvent.KEYCODE_DPAD_LEFT:
-            direction = View.FOCUS_LEFT;
-            break;
-        case KeyEvent.KEYCODE_DPAD_RIGHT:
-            direction = View.FOCUS_RIGHT;
-            break;
-        case KeyEvent.KEYCODE_TAB:
-            direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
-            break;
-        }
-        if (direction != 0 && mWebView.focusSearch(direction) == null) {
-            // Can't take focus in that direction
-            direction = 0;
-        }
-        int eventHubAction = EventHub.KEY_UP;
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            eventHubAction = EventHub.KEY_DOWN;
-            int sound = keyCodeToSoundsEffect(event.getKeyCode());
-            if (sound != 0) {
-                mWebView.playSoundEffect(sound);
-            }
-        }
-        sendBatchableInputMessage(eventHubAction, direction, 0, event);
-    }
-
-    /**
-     * See {@link WebView#setBackgroundColor(int)}
-     */
-    @Override
-    public void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
-    }
-
-    /**
-     * Enable the communication b/t the webView and VideoViewProxy
-     *
-     * 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.
-     *
-     * 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);
-    }
-
-    /*package*/ void autoFillForm(int autoFillQueryId) {
-        mPrivateHandler.obtainMessage(AUTOFILL_FORM, autoFillQueryId, 0)
-            .sendToTarget();
-    }
-
-    /* package */ ViewManager getViewManager() {
-        return mViewManager;
-    }
-
-    /** send content invalidate */
-    protected void contentInvalidateAll() {
-        if (mWebViewCore != null && !mBlockWebkitViewMessages) {
-            mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
-        }
-    }
-
-    /** discard all textures from tiles. Used in Profiled WebView */
-    public void discardAllTextures() {
-        nativeDiscardAllTextures();
-    }
-
-    @Override
-    public void setLayerType(int layerType, Paint paint) {
-        updateHwAccelerated();
-    }
-
-    @Override
-    public void preDispatchDraw(Canvas canvas) {
-        // no-op for WebViewClassic.
-    }
-
-    private void updateHwAccelerated() {
-        if (mNativeClass == 0) {
-            return;
-        }
-        boolean hwAccelerated = false;
-        if (mWebView.isHardwareAccelerated()
-                && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
-            hwAccelerated = true;
-        }
-
-        // result is of type LayerAndroid::InvalidateFlags, non zero means invalidate/redraw
-        int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated);
-        if (mWebViewCore != null && !mBlockWebkitViewMessages && result != 0) {
-            mWebViewCore.contentDraw();
-        }
-    }
-
-    /**
-     * Begin collecting per-tile profiling data
-     *
-     * only used by profiling tests
-     */
-    public void tileProfilingStart() {
-        nativeTileProfilingStart();
-    }
-    /**
-     * Return per-tile profiling data
-     *
-     * only used by profiling tests
-     */
-    public float tileProfilingStop() {
-        return nativeTileProfilingStop();
-    }
-
-    /** only used by profiling tests */
-    public void tileProfilingClear() {
-        nativeTileProfilingClear();
-    }
-    /** only used by profiling tests */
-    public int tileProfilingNumFrames() {
-        return nativeTileProfilingNumFrames();
-    }
-    /** only used by profiling tests */
-    public int tileProfilingNumTilesInFrame(int frame) {
-        return nativeTileProfilingNumTilesInFrame(frame);
-    }
-    /** only used by profiling tests */
-    public int tileProfilingGetInt(int frame, int tile, String key) {
-        return nativeTileProfilingGetInt(frame, tile, key);
-    }
-    /** 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() {
-        if (mFocusedNode != null) {
-            return mFocusedNode.mEditable;
-        }
-        return false;
-    }
-
-    // Called via JNI
-    private void postInvalidate() {
-        mWebView.postInvalidate();
-    }
-
-    // Note: must be called before first WebViewClassic is created.
-    public static void setShouldMonitorWebCoreThread() {
-        WebViewCore.setShouldMonitorWebCoreThread();
-    }
-
-    @Override
-    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
-        int layer = getBaseLayer();
-        if (layer != 0) {
-            try {
-                ByteArrayOutputStream stream = new ByteArrayOutputStream();
-                ViewStateSerializer.dumpLayerHierarchy(layer, stream, level);
-                stream.close();
-                byte[] buf = stream.toByteArray();
-                out.write(new String(buf, "ascii"));
-            } catch (IOException e) {}
-        }
-    }
-
-    @Override
-    public View findHierarchyView(String className, int hashCode) {
-        if (mNativeClass == 0) return null;
-        Picture pic = new Picture();
-        if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) {
-            return null;
-        }
-        return new PictureWrapperView(getContext(), pic, mWebView);
-    }
-
-    private static class PictureWrapperView extends View {
-        Picture mPicture;
-        WebView mWebView;
-
-        public PictureWrapperView(Context context, Picture picture, WebView parent) {
-            super(context);
-            mPicture = picture;
-            mWebView = parent;
-            setWillNotDraw(false);
-            setRight(mPicture.getWidth());
-            setBottom(mPicture.getHeight());
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            canvas.drawPicture(mPicture);
-        }
-
-        @Override
-        public boolean post(Runnable action) {
-            return mWebView.post(action);
-        }
-    }
-
-    private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
-    private native void     nativeDebugDump();
-    private static native void nativeDestroy(int ptr);
-
-    private native void nativeDraw(Canvas canvas, RectF visibleRect,
-            int color, int extra);
-    private native void     nativeDumpDisplayTree(String urlOrNull);
-    private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
-    private native int      nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect,
-            Rect screenRect, RectF visibleContentRect, float scale, int extras);
-    private native int      nativeGetDrawGLFunction(int nativeInstance);
-    private native void     nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect,
-            Rect screenRect, RectF visibleContentRect, float scale);
-    private native String   nativeGetSelection();
-    private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native boolean  nativeSetBaseLayer(int nativeInstance,
-            int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout,
-            int scrollingLayer);
-    private native int      nativeGetBaseLayer(int nativeInstance);
-    private native void     nativeCopyBaseContentToPicture(Picture pict);
-    private native boolean     nativeDumpLayerContentToPicture(int nativeInstance,
-            String className, int layerId, Picture pict);
-    private native boolean  nativeHasContent();
-    private native void     nativeStopGL(int ptr);
-    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);
-
-    private native void     nativeUseHardwareAccelSkia(boolean enabled);
-
-    // Returns a pointer to the scrollable LayerAndroid at the given point.
-    private native int      nativeScrollableLayer(int nativeInstance, int x, int y, Rect scrollRect,
-            Rect scrollBounds);
-    /**
-     * Scroll the specified layer.
-     * @param nativeInstance Native WebView instance
-     * @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 nativeInstance, int layer, int newX, int newY);
-    private native void     nativeSetIsScrolling(boolean isScrolling);
-    private native int      nativeGetBackgroundColor(int nativeInstance);
-    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 void nativeSetTextSelection(int instance, int selection);
-    private static native int nativeGetHandleLayerId(int instance, int handle,
-            Point cursorLocation, QuadF textQuad);
-    private static native void nativeMapLayerRect(int instance, int layerId,
-            Rect rect);
-    // Returns 1 if a layer sync is needed, else 0
-    private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
-    private static native void nativeFindMaxVisibleRect(int instance, int layerId,
-            Rect visibleContentRect);
-    private static native boolean nativeIsHandleLeft(int instance, int handleId);
-    private static native boolean nativeIsPointVisible(int instance,
-            int layerId, int contentX, int contentY);
-}
diff --git a/core/java/android/webkit/WebViewClientClassicExt.java b/core/java/android/webkit/WebViewClientClassicExt.java
deleted file mode 100644
index a873585..0000000
--- a/core/java/android/webkit/WebViewClientClassicExt.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.net.http.SslError;
-
-/**
- * Adds WebViewClassic specific extension methods to the WebViewClient callback class.
- * These are not part of the public WebView API, so the class is hidden.
- * @hide
- */
-public class WebViewClientClassicExt extends WebViewClient {
-
-    /**
-     * Notify the host application that an SSL error occurred while loading a
-     * resource, but the WebView chose to proceed anyway based on a
-     * decision retained from a previous response to onReceivedSslError().
-     */
-    public void onProceededAfterSslError(WebView view, SslError error) {
-    }
-
-    /**
-     * Notify the host application to handle a SSL client certificate
-     * request (display the request to the user and ask whether to
-     * proceed with a client certificate or not). The host application
-     * has to call either handler.cancel() or handler.proceed() as the
-     * connection is suspended and waiting for the response. The
-     * default behavior is to cancel, returning no client certificate.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param handler A ClientCertRequestHandler object that will
-     *            handle the user's response.
-     * @param host_and_port The host and port of the requesting server.
-     */
-    public void onReceivedClientCertRequest(WebView view,
-            ClientCertRequestHandler handler, String host_and_port) {
-        handler.cancel();
-    }
-}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
deleted file mode 100644
index 4a09636..0000000
--- a/core/java/android/webkit/WebViewCore.java
+++ /dev/null
@@ -1,3145 +0,0 @@
-/*
- * Copyright (C) 2007 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.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.media.MediaFile;
-import android.net.ProxyProperties;
-import android.net.Uri;
-import android.net.http.CertificateChainValidator;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SurfaceView;
-import android.view.View;
-import android.webkit.WebViewClassic.FocusNodeHref;
-import android.webkit.WebViewInputDispatcher.WebKitCallbacks;
-
-import com.android.internal.os.SomeArgs;
-
-import junit.framework.Assert;
-
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @hide
- */
-public final class WebViewCore {
-
-    private static final String LOGTAG = "webcore";
-
-    static {
-        // Load libwebcore and libchromium_net during static initialization.
-        // This happens in the zygote process so they will be shared read-only
-        // across all app processes.
-        try {
-            System.loadLibrary("webcore");
-            System.loadLibrary("chromium_net");
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(LOGTAG, "Unable to load native support libraries.");
-        }
-    }
-
-    /*
-     * WebViewCore always executes in the same thread as the native webkit.
-     */
-
-    // The WebViewClassic that corresponds to this WebViewCore.
-    private WebViewClassic mWebViewClassic;
-    // Proxy for handling callbacks from native code
-    private final CallbackProxy mCallbackProxy;
-    // Settings object for maintaining all settings
-    private final WebSettingsClassic mSettings;
-    // Context for initializing the BrowserFrame with the proper assets.
-    private final Context mContext;
-    // The pointer to a native view object.
-    private int mNativeClass;
-    // The BrowserFrame is an interface to the native Frame component.
-    private BrowserFrame mBrowserFrame;
-    // Custom JS interfaces to add during the initialization.
-    private Map<String, Object> mJavascriptInterfaces;
-    /*
-     * range is from 200 to 10,000. 0 is a special value means device-width. -1
-     * means undefined.
-     */
-    private int mViewportWidth = -1;
-
-    /*
-     * range is from 200 to 10,000. 0 is a special value means device-height. -1
-     * means undefined.
-     */
-    private int mViewportHeight = -1;
-
-    /*
-     * scale in percent, range is from 1 to 1000. 0 means undefined.
-     */
-    private int mViewportInitialScale = 0;
-
-    /*
-     * scale in percent, range is from 1 to 1000. 0 means undefined.
-     */
-    private int mViewportMinimumScale = 0;
-
-    /*
-     * scale in percent, range is from 1 to 1000. 0 means undefined.
-     */
-    private int mViewportMaximumScale = 0;
-
-    private boolean mViewportUserScalable = true;
-
-    /*
-     * range is from 70 to 400.
-     * 0 is a special value means device-dpi. The default scale factor will be
-     * always 100.
-     * -1 means undefined. The default scale factor will be
-     * WebView.DEFAULT_SCALE_PERCENT.
-     */
-    private int mViewportDensityDpi = -1;
-
-    private boolean mIsRestored = false;
-    private float mRestoredScale = 0;
-    private float mRestoredTextWrapScale = 0;
-    private int mRestoredX = 0;
-    private int mRestoredY = 0;
-
-    private MockGeolocation mMockGeolocation = new MockGeolocation(this);
-
-    private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager =
-            new DeviceMotionAndOrientationManager(this);
-    private DeviceMotionService mDeviceMotionService;
-    private DeviceOrientationService mDeviceOrientationService;
-
-    private int mLowMemoryUsageThresholdMb;
-    private int mHighMemoryUsageThresholdMb;
-    private int mHighUsageDeltaMb;
-
-    private int mChromeCanFocusDirection;
-    private int mTextSelectionChangeReason = TextSelectionData.REASON_UNKNOWN;
-
-    // Used to determine if we should monitor the WebCore thread for responsiveness.
-    // If it "hangs", for example a web page enters a while(true) loop, we will
-    // prompt the user with a dialog allowing them to terminate the process.
-    private static boolean sShouldMonitorWebCoreThread;
-
-    // The thread name used to identify the WebCore thread and for use in
-    // debugging other classes that require operation within the WebCore thread.
-    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
-
-    public WebViewCore(Context context, WebViewClassic w, CallbackProxy proxy,
-            Map<String, Object> javascriptInterfaces) {
-        // No need to assign this in the WebCore thread.
-        mCallbackProxy = proxy;
-        mWebViewClassic = w;
-        mJavascriptInterfaces = javascriptInterfaces;
-        // This context object is used to initialize the WebViewCore during
-        // subwindow creation.
-        mContext = context;
-
-        // We need to wait for the initial thread creation before sending
-        // a message to the WebCore thread.
-        // XXX: This is the only time the UI thread will wait for the WebCore
-        // thread!
-        synchronized (WebViewCore.class) {
-            if (sWebCoreHandler == null) {
-                // Create a global thread and start it.
-                Thread t = new Thread(new WebCoreThread());
-                t.setName(THREAD_NAME);
-                t.start();
-                try {
-                    WebViewCore.class.wait();
-                } catch (InterruptedException e) {
-                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
-                           "creation.");
-                    Log.e(LOGTAG, Log.getStackTraceString(e));
-                }
-
-                if (sShouldMonitorWebCoreThread) {
-                    // Start the singleton watchdog which will monitor the WebCore thread
-                    // to verify it's still processing messages. Note that this is the only
-                    // time we need to check the value as all the other public methods on
-                    // the WebCoreThreadWatchdog are no-ops if start() is not called.
-                    WebCoreThreadWatchdog.start(sWebCoreHandler);
-                }
-            }
-            // Make sure the Watchdog is aware of this new WebView.
-            WebCoreThreadWatchdog.registerWebView(w);
-        }
-        // Create an EventHub to handle messages before and after the thread is
-        // ready.
-        mEventHub = new EventHub();
-        // Create a WebSettings object for maintaining all settings
-        mSettings = new WebSettingsClassic(mContext, mWebViewClassic);
-        // The WebIconDatabase needs to be initialized within the UI thread so
-        // just request the instance here.
-        WebIconDatabase.getInstance();
-        // Create the WebStorageClassic singleton and the UI handler
-        WebStorageClassic.getInstance().createUIHandler();
-        // Create the UI handler for GeolocationPermissions
-        GeolocationPermissionsClassic.getInstance().createUIHandler();
-
-        // Get the memory class of the current device. V8 will use these values
-        // to GC more effectively.
-        ActivityManager manager = (ActivityManager) mContext.getSystemService(
-                Context.ACTIVITY_SERVICE);
-        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
-        manager.getMemoryInfo(memInfo);
-
-        // Allow us to use up to our memory class value before V8's GC kicks in.
-        // These values have been determined by experimentation.
-        mLowMemoryUsageThresholdMb = manager.getLargeMemoryClass();
-        mHighMemoryUsageThresholdMb = (int) (mLowMemoryUsageThresholdMb * 1.5);
-        // Avoid constant V8 GC when memory usage equals to working set estimate.
-        mHighUsageDeltaMb = mLowMemoryUsageThresholdMb / 32;
-
-        // Send a message to initialize the WebViewCore.
-        Message init = sWebCoreHandler.obtainMessage(
-                WebCoreThread.INITIALIZE, this);
-        sWebCoreHandler.sendMessage(init);
-    }
-
-    /* Initialize private data within the WebCore thread.
-     */
-    private void initialize() {
-        /* Initialize our private BrowserFrame class to handle all
-         * frame-related functions. We need to create a new view which
-         * in turn creates a C level FrameView and attaches it to the frame.
-         */
-        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
-                mSettings, mJavascriptInterfaces);
-        mJavascriptInterfaces = null;
-        // Sync the native settings and also create the WebCore thread handler.
-        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
-        // Create the handler and transfer messages for the IconDatabase
-        WebIconDatabaseClassic.getInstance().createHandler();
-        // Create the handler for WebStorageClassic
-        WebStorageClassic.getInstance().createHandler();
-        // Create the handler for GeolocationPermissions.
-        GeolocationPermissionsClassic.getInstance().createHandler();
-        // The transferMessages call will transfer all pending messages to the
-        // WebCore thread handler.
-        mEventHub.transferMessages();
-
-        // Send a message back to WebView to tell it that we have set up the
-        // WebCore thread.
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.WEBCORE_INITIALIZED_MSG_ID,
-                    mNativeClass, 0).sendToTarget();
-        }
-
-    }
-
-    /* Handle the initialization of WebViewCore during subwindow creation. This
-     * method is called from the WebCore thread but it is called before the
-     * INITIALIZE message can be handled.
-     */
-    /* package */ void initializeSubwindow() {
-        // Go ahead and initialize the core components.
-        initialize();
-        // Remove the INITIALIZE method so we don't try to initialize twice.
-        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
-    }
-
-    /* Get the BrowserFrame component. This is used for subwindow creation and
-     * is called only from BrowserFrame in the WebCore thread. */
-    /* package */ synchronized BrowserFrame getBrowserFrame() {
-        return mBrowserFrame;
-    }
-
-    public WebKitCallbacks getInputDispatcherCallbacks() {
-        return mEventHub;
-    }
-
-    //-------------------------------------------------------------------------
-    // Common methods
-    //-------------------------------------------------------------------------
-
-    /**
-     * Causes all timers to pause. This applies to all WebViews in the current
-     * app process.
-     */
-    public static void pauseTimers() {
-        if (BrowserFrame.sJavaBridge == null) {
-            throw new IllegalStateException(
-                    "No WebView has been created in this process!");
-        }
-        BrowserFrame.sJavaBridge.pause();
-    }
-
-    /**
-     * Resume all timers. This applies to all WebViews in the current process.
-     */
-    public static void resumeTimers() {
-        if (BrowserFrame.sJavaBridge == null) {
-            throw new IllegalStateException(
-                    "No WebView has been created in this process!");
-        }
-        BrowserFrame.sJavaBridge.resume();
-    }
-
-    public WebSettingsClassic getSettings() {
-        return mSettings;
-    }
-
-    /*
-     * Given mimeType, check whether it's supported in Android media framework.
-     * mimeType could be such as "audio/ogg" and "video/mp4".
-     */
-    /* package */ static boolean isSupportedMediaMimeType(String mimeType) {
-        int fileType = MediaFile.getFileTypeForMimeType(mimeType);
-        return MediaFile.isAudioFileType(fileType)
-            || MediaFile.isVideoFileType(fileType)
-            || MediaFile.isPlayListFileType(fileType)
-            // The following is not in Media framework, but it's supported.
-            || (mimeType != null && mimeType.startsWith("video/m4v"));
-    }
-
-    /**
-     * Add an error message to the client's console.
-     * @param message The message to add
-     * @param lineNumber the line on which the error occurred
-     * @param sourceID the filename of the source that caused the error.
-     * @param msgLevel the log level of this message. This is a value casted to int
-     *     from WebCore::MessageLevel in WebCore/page/Console.h.
-     */
-    protected void addMessageToConsole(String message, int lineNumber, String sourceID,
-            int msgLevel) {
-        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
-    }
-
-    /**
-     * Invoke a javascript alert.
-     * @param message The message displayed in the alert.
-     */
-    protected void jsAlert(String url, String message) {
-        mCallbackProxy.onJsAlert(url, message);
-    }
-
-    /**
-     * Called by JNI when the focus node changed.
-     */
-    private void focusNodeChanged(int nodePointer, WebKitHitTest hitTest) {
-        if (mWebViewClassic == null) return;
-        mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.FOCUS_NODE_CHANGED,
-                nodePointer, 0, hitTest).sendToTarget();
-    }
-
-    /**
-     * Called by JNI to advance focus to the next view.
-     */
-    private void chromeTakeFocus(int webkitDirection) {
-        if (mWebViewClassic == null) return;
-        Message m = mWebViewClassic.mPrivateHandler.obtainMessage(
-                WebViewClassic.TAKE_FOCUS);
-        m.arg1 = mapDirection(webkitDirection);
-        m.sendToTarget();
-    }
-
-    /**
-     * Called by JNI to see if we can take focus in the given direction.
-     */
-    private boolean chromeCanTakeFocus(int webkitDirection) {
-        int direction = mapDirection(webkitDirection);
-        return direction == mChromeCanFocusDirection && direction != 0;
-    }
-
-    /**
-     * Maps a Webkit focus direction to a framework one
-     */
-    private int mapDirection(int webkitDirection) {
-        /*
-         * This is WebKit's FocusDirection enum (from FocusDirection.h)
-        enum FocusDirection {
-            FocusDirectionNone = 0,
-            FocusDirectionForward,
-            FocusDirectionBackward,
-            FocusDirectionUp,
-            FocusDirectionDown,
-            FocusDirectionLeft,
-            FocusDirectionRight
-        };
-         */
-        switch (webkitDirection) {
-        case 1:
-            return View.FOCUS_FORWARD;
-        case 2:
-            return View.FOCUS_BACKWARD;
-        case 3:
-            return View.FOCUS_UP;
-        case 4:
-            return View.FOCUS_DOWN;
-        case 5:
-            return View.FOCUS_LEFT;
-        case 6:
-            return View.FOCUS_RIGHT;
-        }
-        return 0;
-    }
-
-    /**
-     * Called by JNI.  Open a file chooser to upload a file.
-     * @param acceptType The value of the 'accept' attribute of the
-     *         input tag associated with this file picker.
-     * @param capture The value of the 'capture' attribute of the
-     *         input tag associated with this file picker.
-     * @return String version of the URI.
-     */
-    private String openFileChooser(String acceptType, String capture) {
-        Uri uri = mCallbackProxy.openFileChooser(acceptType, capture);
-        if (uri != null) {
-            String filePath = "";
-            // Note - querying for MediaStore.Images.Media.DATA
-            // seems to work for all content URIs, not just images
-            Cursor cursor = mContext.getContentResolver().query(
-                    uri,
-                    new String[] { MediaStore.Images.Media.DATA },
-                    null, null, null);
-            if (cursor != null) {
-                try {
-                    if (cursor.moveToNext()) {
-                        filePath = cursor.getString(0);
-                    }
-                } finally {
-                    cursor.close();
-                }
-            } else {
-                filePath = uri.getLastPathSegment();
-            }
-            String uriString = uri.toString();
-            BrowserFrame.sJavaBridge.storeFilePathForContentUri(filePath, uriString);
-            return uriString;
-        }
-        return "";
-    }
-
-    /**
-     * Notify the embedding application that the origin has exceeded it's database quota.
-     * @param url The URL that caused the overflow.
-     * @param databaseIdentifier The identifier of the database.
-     * @param quota The current quota for the origin.
-     * @param estimatedDatabaseSize The estimated size of the database.
-     */
-    protected void exceededDatabaseQuota(String url,
-                                         String databaseIdentifier,
-                                         long quota,
-                                         long estimatedDatabaseSize) {
-        // Inform the callback proxy of the quota overflow. Send an object
-        // that encapsulates a call to the nativeSetDatabaseQuota method to
-        // awaken the sleeping webcore thread when a decision from the
-        // client to allow or deny quota is available.
-        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
-                quota, estimatedDatabaseSize, getUsedQuota(),
-                new WebStorage.QuotaUpdater() {
-                        @Override
-                        public void updateQuota(long newQuota) {
-                            nativeSetNewStorageLimit(mNativeClass, newQuota);
-                        }
-                });
-    }
-
-    /**
-     * Notify the embedding application that the appcache has reached or exceeded its maximum
-     * allowed storage size.
-     *
-     * @param requiredStorage is the amount of storage, in bytes, that would be
-     * needed in order for the last appcache operation to succeed.
-     * @param maxSize maximum allowed Application Cache database size, in bytes.
-     */
-    protected void reachedMaxAppCacheSize(long requiredStorage, long maxSize) {
-        mCallbackProxy.onReachedMaxAppCacheSize(requiredStorage, maxSize,
-                new WebStorage.QuotaUpdater() {
-                    @Override
-                    public void updateQuota(long newQuota) {
-                        nativeSetNewStorageLimit(mNativeClass, newQuota);
-                    }
-                });
-    }
-
-    protected void populateVisitedLinks() {
-        ValueCallback callback = new ValueCallback<String[]>() {
-            @Override
-            public void onReceiveValue(String[] value) {
-                sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value);
-            }
-        };
-        mCallbackProxy.getVisitedHistory(callback);
-    }
-
-    /**
-     * Shows a prompt to ask the user to set the Geolocation permission state
-     * for the given origin.
-     * @param origin The origin for which Geolocation permissions are
-     *     requested.
-     */
-    protected void geolocationPermissionsShowPrompt(String origin) {
-        mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
-                new GeolocationPermissions.Callback() {
-            @Override
-            public void invoke(String origin, boolean allow, boolean remember) {
-                GeolocationPermissionsData data = new GeolocationPermissionsData();
-                data.mOrigin = origin;
-                data.mAllow = allow;
-                data.mRemember = remember;
-                // Marshall to WebCore thread.
-                sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
-            }
-        });
-    }
-
-    /**
-     * Hides the Geolocation permissions prompt.
-     */
-    protected void geolocationPermissionsHidePrompt() {
-        mCallbackProxy.onGeolocationPermissionsHidePrompt();
-    }
-
-    /**
-     * Invoke a javascript confirm dialog.
-     * @param message The message displayed in the dialog.
-     * @return True if the user confirmed or false if the user cancelled.
-     */
-    protected boolean jsConfirm(String url, String message) {
-        return mCallbackProxy.onJsConfirm(url, message);
-    }
-
-    /**
-     * Invoke a javascript prompt dialog.
-     * @param message The message to be displayed in the dialog.
-     * @param defaultValue The default value in the prompt input.
-     * @return The input from the user or null to indicate the user cancelled
-     *         the dialog.
-     */
-    protected String jsPrompt(String url, String message, String defaultValue) {
-        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
-    }
-
-    /**
-     * Invoke a javascript before unload dialog.
-     * @param url The url that is requesting the dialog.
-     * @param message The message displayed in the dialog.
-     * @return True if the user confirmed or false if the user cancelled. False
-     *         will cancel the navigation.
-     */
-    protected boolean jsUnload(String url, String message) {
-        return mCallbackProxy.onJsBeforeUnload(url, message);
-    }
-
-    /**
-     *
-     * Callback to notify that a JavaScript execution timeout has occured.
-     * @return True if the JavaScript execution should be interrupted. False
-     *         will continue the execution.
-     */
-    protected boolean jsInterrupt() {
-        return mCallbackProxy.onJsTimeout();
-    }
-
-    /**
-     * Notify the webview that we want to exit the video fullscreen.
-     * This is called through JNI by webcore.
-     */
-    protected void exitFullscreenVideo() {
-        if (mWebViewClassic == null) return;
-        Message message = Message.obtain(mWebViewClassic.mPrivateHandler,
-                       WebViewClassic.EXIT_FULLSCREEN_VIDEO);
-        message.sendToTarget();
-    }
-
-    /**
-     * Clear the picture set. To be called only on the WebCore thread.
-     */
-    /* package */ void clearContent() {
-        nativeClearContent(mNativeClass);
-    }
-
-    //-------------------------------------------------------------------------
-    // JNI methods
-    //-------------------------------------------------------------------------
-
-    static native String nativeFindAddress(String addr, boolean caseInsensitive);
-
-    /**
-     * Empty the picture set.
-     */
-    private native void nativeClearContent(int nativeClass);
-
-    private native void nativeContentInvalidateAll(int nativeClass);
-
-    /**
-     * Redraw a portion of the picture set. The Point wh returns the
-     * width and height of the overall picture.
-     */
-    private native int nativeRecordContent(int nativeClass, Point wh);
-
-    /**
-     * Notify webkit that animations have begun (on the hardware accelerated content)
-     */
-    private native void nativeNotifyAnimationStarted(int nativeClass);
-
-    private native boolean nativeKey(int nativeClass, int keyCode,
-            int unichar, int repeatCount, boolean isShift, boolean isAlt,
-            boolean isSym, boolean isDown);
-
-    private native void nativeSendListBoxChoices(int nativeClass,
-            boolean[] choices, int size);
-
-    private native void nativeSendListBoxChoice(int nativeClass, int choice);
-
-    private native void nativeCloseIdleConnections(int nativeClass);
-
-    /*  Tell webkit what its width and height are, for the purposes
-        of layout/line-breaking. These coordinates are in document space,
-        which is the same as View coords unless we have zoomed the document
-        (see nativeSetZoom).
-        textWrapWidth is used by layout to wrap column around. If viewport uses
-        fixed size, textWrapWidth can be different from width with zooming.
-        should this be called nativeSetViewPortSize?
-    */
-    private native void nativeSetSize(int nativeClass, int width, int height,
-            int textWrapWidth, float scale, int screenWidth, int screenHeight,
-            int anchorX, int anchorY, boolean ignoreHeight);
-
-    private native int nativeGetContentMinPrefWidth(int nativeClass);
-
-    // Start: functions that deal with text editing
-    private native void nativeReplaceTextfieldText(
-            int nativeClass, int oldStart, int oldEnd, String replace,
-            int newStart, int newEnd, int textGeneration);
-
-    private native void passToJs(int nativeClass,
-            int gen, String currentText, int keyCode, int keyValue,
-            boolean down, boolean cap, boolean fn, boolean sym);
-
-    private native void nativeSetFocusControllerActive(int nativeClass,
-            boolean active);
-
-    private native void nativeSaveDocumentState(int nativeClass);
-
-    private native void nativeMoveMouse(int nativeClass, int x, int y);
-
-    private native String nativeRetrieveHref(int nativeClass, int x, int y);
-    private native String nativeRetrieveAnchorText(int nativeClass,
-            int x, int y);
-    private native String nativeRetrieveImageSource(int nativeClass,
-            int x, int y);
-    private native boolean nativeMouseClick(int nativeClass);
-
-    private native int nativeHandleTouchEvent(int nativeClass, int action,
-            int[] idArray, int[] xArray, int[] yArray, int count,
-            int actionIndex, int metaState);
-
-    private native void nativeSetBackgroundColor(int nativeClass, int color);
-
-    private native void nativeDumpDomTree(int nativeClass, boolean useFile);
-
-    private native void nativeDumpRenderTree(int nativeClass, boolean useFile);
-
-    private native void nativeSetJsFlags(int nativeClass, String flags);
-
-    /**
-     *  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  nativeClass Pointer to the C++ WebViewCore object mNativeClass
-     * @param  start   Beginning of selection to delete.
-     * @param  end     End of selection to delete.
-     * @param  textGeneration Text generation number when delete was pressed.
-     */
-    private native void nativeDeleteSelection(int nativeClass, int start,
-            int end, int textGeneration);
-
-    /**
-     *  Set the selection to (start, end) in the focused textfield. If start and
-     *  end are out of order, swap them.
-     * @param  nativeClass Pointer to the C++ WebViewCore object mNativeClass
-     * @param  start   Beginning of selection.
-     * @param  end     End of selection.
-     */
-    private native void nativeSetSelection(int nativeClass, int start, int end);
-
-    // Register a scheme to be treated as local scheme so that it can access
-    // local asset files for resources
-    private native void nativeRegisterURLSchemeAsLocal(int nativeClass,
-            String scheme);
-
-    /*
-     * Inform webcore that the user has decided whether to allow or deny new
-     * quota for the current origin or more space for the app cache, and that
-     * the main thread should wake up now.
-     * @param limit Is the new quota for an origin or new app cache max size.
-     */
-    private native void nativeSetNewStorageLimit(int nativeClass, long limit);
-
-    /**
-     * Provide WebCore with a Geolocation permission state for the specified
-     * origin.
-     * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass
-     * @param origin The origin for which Geolocation permissions are provided.
-     * @param allow Whether Geolocation permissions are allowed.
-     * @param remember Whether this decision should be remembered beyond the
-     *     life of the current page.
-     */
-    private native void nativeGeolocationPermissionsProvide(int nativeClass,
-            String origin, boolean allow, boolean remember);
-
-    /**
-     * Provide WebCore with the previously visted links from the history database
-     * @param nativeClass TODO
-     */
-    private native void nativeProvideVisitedHistory(int nativeClass,
-            String[] history);
-
-    /**
-     * Modifies the current selection.
-     *
-     * Note: Accessibility support.
-     * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass
-     * @param direction The direction in which to alter the selection.
-     * @param granularity The granularity of the selection modification.
-     *
-     * @return The selection string.
-     */
-    private native String nativeModifySelection(int nativeClass, int direction,
-            int granularity);
-
-    // EventHub for processing messages
-    private final EventHub mEventHub;
-    // WebCore thread handler
-    private static Handler sWebCoreHandler;
-    // Class for providing Handler creation inside the WebCore thread.
-    private static class WebCoreThread implements Runnable {
-        // Message id for initializing a new WebViewCore.
-        private static final int INITIALIZE = 0;
-        private static final int REDUCE_PRIORITY = 1;
-        private static final int RESUME_PRIORITY = 2;
-
-        @Override
-        public void run() {
-            Looper.prepare();
-            Assert.assertNull(sWebCoreHandler);
-            synchronized (WebViewCore.class) {
-                sWebCoreHandler = new Handler() {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        switch (msg.what) {
-                            case INITIALIZE:
-                                WebViewCore core = (WebViewCore) msg.obj;
-                                core.initialize();
-                                break;
-
-                            case REDUCE_PRIORITY:
-                                // 3 is an adjustable number.
-                                Process.setThreadPriority(
-                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
-                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
-                                break;
-
-                            case RESUME_PRIORITY:
-                                Process.setThreadPriority(
-                                        Process.THREAD_PRIORITY_DEFAULT);
-                                break;
-
-                            case EventHub.ADD_PACKAGE_NAME:
-                                if (BrowserFrame.sJavaBridge == null) {
-                                    throw new IllegalStateException(
-                                            "No WebView has been created in this process!");
-                                }
-                                BrowserFrame.sJavaBridge.addPackageName((String) msg.obj);
-                                break;
-
-                            case EventHub.REMOVE_PACKAGE_NAME:
-                                if (BrowserFrame.sJavaBridge == null) {
-                                    throw new IllegalStateException(
-                                            "No WebView has been created in this process!");
-                                }
-                                BrowserFrame.sJavaBridge.removePackageName((String) msg.obj);
-                                break;
-
-                            case EventHub.PROXY_CHANGED:
-                                if (BrowserFrame.sJavaBridge == null) {
-                                    throw new IllegalStateException(
-                                            "No WebView has been created in this process!");
-                                }
-                                BrowserFrame.sJavaBridge.updateProxy((ProxyProperties)msg.obj);
-                                break;
-
-                            case EventHub.HEARTBEAT:
-                                // Ping back the watchdog to let it know we're still processing
-                                // messages.
-                                Message m = (Message)msg.obj;
-                                m.sendToTarget();
-                                break;
-                            case EventHub.TRUST_STORAGE_UPDATED:
-                                // post a task to network thread for updating trust manager
-                                nativeCertTrustChanged();
-                                CertificateChainValidator.handleTrustStorageUpdate();
-                                break;
-                        }
-                    }
-                };
-                WebViewCore.class.notify();
-            }
-            Looper.loop();
-        }
-    }
-
-    static class BaseUrlData {
-        String mBaseUrl;
-        String mData;
-        String mMimeType;
-        String mEncoding;
-        String mHistoryUrl;
-    }
-
-    static class JSInterfaceData {
-        Object mObject;
-        String mInterfaceName;
-        boolean mRequireAnnotation;
-    }
-
-    static class JSKeyData {
-        String mCurrentText;
-        KeyEvent mEvent;
-    }
-
-    static class MotionUpData {
-        int mFrame;
-        int mNode;
-        Rect mBounds;
-        int mX;
-        int mY;
-    }
-
-    static class GetUrlData {
-        String mUrl;
-        Map<String, String> mExtraHeaders;
-    }
-
-    static class PostUrlData {
-        String mUrl;
-        byte[] mPostData;
-    }
-
-    static class ReplaceTextData {
-        String mReplace;
-        int mNewStart;
-        int mNewEnd;
-        int mTextGeneration;
-    }
-
-    static class TextSelectionData {
-        static final int REASON_UNKNOWN = 0;
-        static final int REASON_ACCESSIBILITY_INJECTOR = 1;
-        static final int REASON_SELECT_WORD = 2;
-        public TextSelectionData(int start, int end, int selectTextPtr) {
-            mStart = start;
-            mEnd = end;
-            mSelectTextPtr = selectTextPtr;
-        }
-        int mStart;
-        int mEnd;
-        int mSelectTextPtr;
-        int mSelectionReason = TextSelectionData.REASON_UNKNOWN;
-    }
-
-    static class TouchUpData {
-        int mMoveGeneration;
-        int mFrame;
-        int mNode;
-        int mX;
-        int mY;
-        int mNativeLayer;
-        Rect mNativeLayerRect = new Rect();
-    }
-
-    static class TouchHighlightData {
-        int mX;
-        int mY;
-        int mSlop;
-        int mNativeLayer;
-        Rect mNativeLayerRect;
-    }
-
-    static class WebKitHitTest {
-        String mLinkUrl;
-        String mIntentUrl;
-        String mAnchorText;
-        String mImageUrl;
-        String mAltDisplayString;
-        String mTitle;
-        Rect[] mTouchRects;
-        boolean mEditable;
-        int mTapHighlightColor = WebViewClassic.HIGHLIGHT_COLOR;
-        Rect[] mEnclosingParentRects;
-        boolean mHasFocus;
-
-        // These are the input values that produced this hit test
-        int mHitTestX;
-        int mHitTestY;
-        int mHitTestSlop;
-        boolean mHitTestMovedMouse;
-    }
-
-    static class AutoFillData {
-        public AutoFillData() {
-            mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE;
-            mPreview = "";
-        }
-
-        public AutoFillData(int queryId, String preview) {
-            mQueryId = queryId;
-            mPreview = preview;
-        }
-
-        public int getQueryId() {
-            return mQueryId;
-        }
-
-        public String getPreviewString() {
-            return mPreview;
-        }
-
-        private int mQueryId;
-        private String mPreview;
-    }
-
-    static class TextFieldInitData {
-        public int mFieldPointer;
-        public String mText;
-        public int mType;
-        public boolean mIsSpellCheckEnabled;
-        public boolean mIsTextFieldNext;
-        public boolean mIsTextFieldPrev;
-        public boolean mIsAutoCompleteEnabled;
-        public String mName;
-        public String mLabel;
-        public int mMaxLength;
-        public Rect mContentBounds;
-        public int mNodeLayerId;
-        public Rect mClientRect;
-    }
-
-    // mAction of TouchEventData can be MotionEvent.getAction() which uses the
-    // last two bytes or one of the following values
-    static final int ACTION_LONGPRESS = 0x100;
-    static final int ACTION_DOUBLETAP = 0x200;
-
-    private static final int TOUCH_FLAG_HIT_HANDLER = 0x1;
-    private static final int TOUCH_FLAG_PREVENT_DEFAULT = 0x2;
-
-    static class TouchEventData {
-        int mAction;
-        int[] mIds;  // Ids of the touch points
-        Point[] mPoints;
-        Point[] mPointsInView;  // the point coordinates in view axis.
-        int mActionIndex;  // Associated pointer index for ACTION_POINTER_DOWN/UP
-        int mMetaState;
-        boolean mReprocess;
-        MotionEvent mMotionEvent;
-        int mNativeLayer;
-        Rect mNativeLayerRect = new Rect();
-        long mSequence;
-        boolean mNativeResult;
-    }
-
-    static class GeolocationPermissionsData {
-        String mOrigin;
-        boolean mAllow;
-        boolean mRemember;
-    }
-
-        static final String[] HandlerDebugString = {
-            "REVEAL_SELECTION", // 96
-            "", // 97
-            "", // = 98
-            "SCROLL_TEXT_INPUT", // = 99
-            "LOAD_URL", // = 100;
-            "STOP_LOADING", // = 101;
-            "RELOAD", // = 102;
-            "KEY_DOWN", // = 103;
-            "KEY_UP", // = 104;
-            "VIEW_SIZE_CHANGED", // = 105;
-            "GO_BACK_FORWARD", // = 106;
-            "SET_SCROLL_OFFSET", // = 107;
-            "RESTORE_STATE", // = 108;
-            "PAUSE_TIMERS", // = 109;
-            "RESUME_TIMERS", // = 110;
-            "CLEAR_CACHE", // = 111;
-            "CLEAR_HISTORY", // = 112;
-            "SET_SELECTION", // = 113;
-            "REPLACE_TEXT", // = 114;
-            "PASS_TO_JS", // = 115;
-            "SET_GLOBAL_BOUNDS", // = 116;
-            "", // = 117;
-            "CLICK", // = 118;
-            "SET_NETWORK_STATE", // = 119;
-            "DOC_HAS_IMAGES", // = 120;
-            "FAKE_CLICK", // = 121;
-            "DELETE_SELECTION", // = 122;
-            "LISTBOX_CHOICES", // = 123;
-            "SINGLE_LISTBOX_CHOICE", // = 124;
-            "MESSAGE_RELAY", // = 125;
-            "SET_BACKGROUND_COLOR", // = 126;
-            "SET_MOVE_FOCUS", // = 127
-            "SAVE_DOCUMENT_STATE", // = 128;
-            "129", // = 129;
-            "WEBKIT_DRAW", // = 130;
-            "131", // = 131;
-            "POST_URL", // = 132;
-            "", // = 133;
-            "CLEAR_CONTENT", // = 134;
-            "", // = 135;
-            "", // = 136;
-            "REQUEST_CURSOR_HREF", // = 137;
-            "ADD_JS_INTERFACE", // = 138;
-            "LOAD_DATA", // = 139;
-            "", // = 140;
-            "", // = 141;
-            "SET_ACTIVE", // = 142;
-            "ON_PAUSE",     // = 143
-            "ON_RESUME",    // = 144
-            "FREE_MEMORY",  // = 145
-            "VALID_NODE_BOUNDS", // = 146
-            "SAVE_WEBARCHIVE", // = 147
-            "WEBKIT_DRAW_LAYERS", // = 148;
-            "REMOVE_JS_INTERFACE", // = 149;
-        };
-
-    static class FindAllRequest {
-        public FindAllRequest(String text) {
-            mSearchText = text;
-            mMatchCount = -1;
-            mMatchIndex = -1;
-        }
-        public final String mSearchText;
-        public int mMatchCount;
-        public int mMatchIndex;
-    }
-
-    static class SaveViewStateRequest {
-        SaveViewStateRequest(OutputStream s, ValueCallback<Boolean> cb) {
-            mStream = s;
-            mCallback = cb;
-        }
-        public OutputStream mStream;
-        public ValueCallback<Boolean> mCallback;
-    }
-
-    /**
-     * @hide
-     */
-    public class EventHub implements WebViewInputDispatcher.WebKitCallbacks {
-        // Message Ids
-        static final int REVEAL_SELECTION = 96;
-        static final int SCROLL_TEXT_INPUT = 99;
-        static final int LOAD_URL = 100;
-        static final int STOP_LOADING = 101;
-        static final int RELOAD = 102;
-        static final int KEY_DOWN = 103;
-        static final int KEY_UP = 104;
-        static final int VIEW_SIZE_CHANGED = 105;
-        static final int GO_BACK_FORWARD = 106;
-        static final int SET_SCROLL_OFFSET = 107;
-        static final int RESTORE_STATE = 108;
-        static final int PAUSE_TIMERS = 109;
-        static final int RESUME_TIMERS = 110;
-        static final int CLEAR_CACHE = 111;
-        static final int CLEAR_HISTORY = 112;
-        static final int SET_SELECTION = 113;
-        static final int REPLACE_TEXT = 114;
-        static final int PASS_TO_JS = 115;
-        static final int SET_GLOBAL_BOUNDS = 116;
-        static final int SET_NETWORK_STATE = 119;
-        static final int DOC_HAS_IMAGES = 120;
-        static final int DELETE_SELECTION = 122;
-        static final int LISTBOX_CHOICES = 123;
-        static final int SINGLE_LISTBOX_CHOICE = 124;
-        public static final int MESSAGE_RELAY = 125;
-        static final int SET_BACKGROUND_COLOR = 126;
-        static final int SAVE_DOCUMENT_STATE = 128;
-        static final int DELETE_SURROUNDING_TEXT = 129;
-
-
-        static final int WEBKIT_DRAW = 130;
-        static final int POST_URL = 132;
-        static final int CLEAR_CONTENT = 134;
-
-        // UI nav messages
-        static final int SET_MOVE_MOUSE = 135;
-        static final int REQUEST_CURSOR_HREF = 137;
-        static final int ADD_JS_INTERFACE = 138;
-        static final int LOAD_DATA = 139;
-
-        // Used to tell the focus controller not to draw the blinking cursor,
-        // based on whether the WebView has focus and whether the WebView's
-        // cursor matches the webpage's focus.
-        static final int SET_ACTIVE = 142;
-
-        // lifecycle activities for just this DOM (unlike pauseTimers, which
-        // is global)
-        static final int ON_PAUSE = 143;
-        static final int ON_RESUME = 144;
-        static final int FREE_MEMORY = 145;
-
-        // Load and save web archives
-        static final int SAVE_WEBARCHIVE = 147;
-
-        static final int REMOVE_JS_INTERFACE = 149;
-
-        // Network-based messaging
-        static final int CLEAR_SSL_PREF_TABLE = 150;
-
-        // Test harness messages
-        static final int REQUEST_EXT_REPRESENTATION = 160;
-        static final int REQUEST_DOC_AS_TEXT = 161;
-
-        // debugging
-        static final int DUMP_DOMTREE = 170;
-        static final int DUMP_RENDERTREE = 171;
-
-        static final int SET_JS_FLAGS = 174;
-        static final int CONTENT_INVALIDATE_ALL = 175;
-        // Geolocation
-        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
-
-        static final int POPULATE_VISITED_LINKS = 181;
-
-        static final int HIDE_FULLSCREEN = 182;
-
-        static final int SET_NETWORK_TYPE = 183;
-
-        // navigator.isApplicationInstalled()
-        static final int ADD_PACKAGE_NAMES = 184;
-        static final int ADD_PACKAGE_NAME = 185;
-        static final int REMOVE_PACKAGE_NAME = 186;
-
-        // accessibility support
-        static final int MODIFY_SELECTION = 190;
-
-        static final int SET_USE_MOCK_DEVICE_ORIENTATION = 191;
-
-        static final int AUTOFILL_FORM = 192;
-
-        static final int PROXY_CHANGED = 193;
-
-        static final int EXECUTE_JS = 194;
-
-        static final int PLUGIN_SURFACE_READY = 195;
-
-        static final int NOTIFY_ANIMATION_STARTED = 196;
-
-        static final int HEARTBEAT = 197;
-
-        static final int SCROLL_LAYER = 198;
-
-        // private message ids
-        private static final int DESTROY =     200;
-
-        // for cut & paste
-        static final int COPY_TEXT = 210;
-        static final int DELETE_TEXT = 211;
-        static final int INSERT_TEXT = 212;
-        static final int SELECT_TEXT = 213;
-        static final int SELECT_WORD_AT = 214;
-        static final int SELECT_ALL = 215;
-
-        // for updating state on trust storage change
-        static final int TRUST_STORAGE_UPDATED = 220;
-
-        // find-on-page controls
-        static final int FIND_ALL = 221;
-        static final int FIND_NEXT = 222;
-
-        // key was pressed (down and up)
-        static final int KEY_PRESS = 223;
-        static final int SET_INITIAL_FOCUS = 224;
-
-        static final int SAVE_VIEW_STATE = 225;
-        static final int SET_USE_MOCK_GEOLOCATION = 226;
-
-        // Private handler for WebCore messages.
-        private Handler mHandler;
-        // Message queue for containing messages before the WebCore thread is
-        // ready.
-        private LinkedList<Message> mMessages = new LinkedList<Message>();
-        // Flag for blocking messages. This is used during DESTROY to avoid
-        // posting more messages to the EventHub or to WebView's event handler.
-        private boolean mBlockMessages;
-        private boolean mDestroying;
-
-        private int mTid;
-        private int mSavedPriority;
-
-        /**
-         * Prevent other classes from creating an EventHub.
-         */
-        private EventHub() {}
-
-        private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION;
-        private static final int LAST_PACKAGE_MSG_ID = REMOVE_JS_INTERFACE;
-
-        /**
-         * Transfer all messages to the newly created webcore thread handler.
-         */
-        private void transferMessages() {
-            mTid = Process.myTid();
-            mSavedPriority = Process.getThreadPriority(mTid);
-
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    if (DebugFlags.WEB_VIEW_CORE) {
-                        Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID
-                                || msg.what > LAST_PACKAGE_MSG_ID
-                                ? Integer.toString(msg.what)
-                                : HandlerDebugString[msg.what
-                                        - FIRST_PACKAGE_MSG_ID])
-                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
-                                + " obj=" + msg.obj);
-                    }
-                    switch (msg.what) {
-                    case PAUSE_TIMERS:
-                        mSavedPriority = Process.getThreadPriority(mTid);
-                        Process.setThreadPriority(mTid,
-                            Process.THREAD_PRIORITY_BACKGROUND);
-                        pauseTimers();
-                        if (mNativeClass != 0) {
-                            nativeCloseIdleConnections(mNativeClass);
-                        }
-                        return;
-
-                    case RESUME_TIMERS:
-                        Process.setThreadPriority(mTid, mSavedPriority);
-                        resumeTimers();
-                        return;
-                    }
-
-                    if (mWebViewClassic == null || mNativeClass == 0) {
-                        if (DebugFlags.WEB_VIEW_CORE) {
-                            Log.w(LOGTAG, "Rejecting message " + msg.what
-                                    + " because we are destroyed");
-                        }
-                        return;
-                    }
-                    if (mDestroying == true
-                            && msg.what != EventHub.DESTROY) {
-                        if (DebugFlags.WEB_VIEW_CORE) {
-                            Log.v(LOGTAG, "Rejecting message " + msg.what
-                                    + " because we are being destroyed");
-                        }
-                        return;
-                    }
-                    switch (msg.what) {
-                        case WEBKIT_DRAW:
-                            webkitDraw();
-                            break;
-
-                        case DESTROY:
-                            // Time to take down the world. Cancel all pending
-                            // loads and destroy the native view and frame.
-                            synchronized (WebViewCore.this) {
-                                mCallbackProxy.shutdown();
-                                // Wake up the WebCore thread just in case it is waiting for a
-                                // JavaScript dialog.
-                                synchronized (mCallbackProxy) {
-                                    mCallbackProxy.notify();
-                                }
-                                mBrowserFrame.destroy();
-                                mBrowserFrame = null;
-                                mSettings.onDestroyed();
-                                mNativeClass = 0;
-                                WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic);
-                                mWebViewClassic = null;
-                            }
-                            break;
-
-                        case REVEAL_SELECTION:
-                            nativeRevealSelection(mNativeClass);
-                            break;
-
-                        case SCROLL_TEXT_INPUT:
-                            float xPercent;
-                            if (msg.obj == null) {
-                                xPercent = 0f;
-                            } else {
-                                xPercent = ((Float) msg.obj).floatValue();
-                            }
-                            nativeScrollFocusedTextInput(mNativeClass, xPercent,
-                                    msg.arg2);
-                            break;
-
-                        case LOAD_URL: {
-                            CookieManagerClassic.getInstance().waitForCookieOperationsToComplete();
-                            GetUrlData param = (GetUrlData) msg.obj;
-                            loadUrl(param.mUrl, param.mExtraHeaders);
-                            break;
-                        }
-
-                        case POST_URL: {
-                            CookieManagerClassic.getInstance().waitForCookieOperationsToComplete();
-                            PostUrlData param = (PostUrlData) msg.obj;
-                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
-                            break;
-                        }
-                        case LOAD_DATA:
-                            CookieManagerClassic.getInstance().waitForCookieOperationsToComplete();
-                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
-                            String baseUrl = loadParams.mBaseUrl;
-                            if (baseUrl != null) {
-                                int i = baseUrl.indexOf(':');
-                                if (i > 0) {
-                                    // In 1.0, WebView.loadDataWithBaseURL() could access local
-                                    // asset files using 'file' scheme URLs as long as the data is
-                                    // valid. Later versions of WebKit have tightened the
-                                    // restriction around when pages can access such local URLs.
-                                    // To maintain compatibility with 1.0, we register the scheme of
-                                    // the baseUrl to be considered local, as long as it is not
-                                    // http(s)/ftp(s)/about/javascript.
-                                    String scheme = baseUrl.substring(0, i);
-                                    if (!scheme.startsWith("http") &&
-                                            !scheme.startsWith("ftp") &&
-                                            !scheme.startsWith("about") &&
-                                            !scheme.startsWith("javascript")) {
-                                        nativeRegisterURLSchemeAsLocal(mNativeClass,
-                                                scheme);
-                                    }
-                                }
-                            }
-                            mBrowserFrame.loadData(baseUrl,
-                                    loadParams.mData,
-                                    loadParams.mMimeType,
-                                    loadParams.mEncoding,
-                                    loadParams.mHistoryUrl);
-                            nativeContentInvalidateAll(mNativeClass);
-                            break;
-
-                        case STOP_LOADING:
-                            // If the WebCore has committed the load, but not
-                            // finished the first layout yet, we need to set
-                            // first layout done to trigger the interpreted side sync
-                            // up with native side
-                            if (mBrowserFrame.committed()
-                                    && !mBrowserFrame.firstLayoutDone()) {
-                                mBrowserFrame.didFirstLayout();
-                            }
-                            // Do this after syncing up the layout state.
-                            stopLoading();
-                            break;
-
-                        case RELOAD:
-                            mBrowserFrame.reload(false);
-                            break;
-
-                        case KEY_DOWN:
-                            key((KeyEvent) msg.obj, msg.arg1, true);
-                            break;
-
-                        case KEY_UP:
-                            key((KeyEvent) msg.obj, msg.arg1, false);
-                            break;
-
-                        case KEY_PRESS:
-                            keyPress(msg.arg1);
-                            break;
-
-                        case VIEW_SIZE_CHANGED: {
-                            viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj);
-                            break;
-                        }
-                        case SET_SCROLL_OFFSET:
-                            // note: these are in document coordinates
-                            // (inv-zoom)
-                            Point pt = (Point) msg.obj;
-                            nativeSetScrollOffset(mNativeClass,
-                                    msg.arg1 == 1, pt.x, pt.y);
-                            break;
-
-                        case SET_GLOBAL_BOUNDS:
-                            Rect r = (Rect) msg.obj;
-                            nativeSetGlobalBounds(mNativeClass, r.left, r.top,
-                                r.width(), r.height());
-                            break;
-
-                        case GO_BACK_FORWARD:
-                            // If it is a standard load and the load is not
-                            // committed yet, we interpret BACK as RELOAD
-                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
-                                    (mBrowserFrame.loadType() ==
-                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
-                                mBrowserFrame.reload(true);
-                            } else {
-                                mBrowserFrame.goBackOrForward(msg.arg1);
-                            }
-                            break;
-
-                        case RESTORE_STATE:
-                            stopLoading();
-                            restoreState(msg.arg1);
-                            break;
-
-
-                        case ON_PAUSE:
-                            nativePause(mNativeClass);
-                            break;
-
-                        case ON_RESUME:
-                            nativeResume(mNativeClass);
-                            break;
-
-                        case FREE_MEMORY:
-                            clearCache(false);
-                            nativeFreeMemory(mNativeClass);
-                            break;
-
-                        case SET_NETWORK_STATE:
-                            if (BrowserFrame.sJavaBridge == null) {
-                                throw new IllegalStateException("No WebView " +
-                                        "has been created in this process!");
-                            }
-                            BrowserFrame.sJavaBridge
-                                    .setNetworkOnLine(msg.arg1 == 1);
-                            break;
-
-                        case SET_NETWORK_TYPE:
-                            if (BrowserFrame.sJavaBridge == null) {
-                                throw new IllegalStateException("No WebView " +
-                                        "has been created in this process!");
-                            }
-                            Map<String, String> map = (Map<String, String>) msg.obj;
-                            BrowserFrame.sJavaBridge
-                                    .setNetworkType(map.get("type"), map.get("subtype"));
-                            break;
-
-                        case CLEAR_CACHE:
-                            clearCache(msg.arg1 == 1);
-                            break;
-
-                        case CLEAR_HISTORY:
-                            mCallbackProxy.getBackForwardList().
-                                    close(mBrowserFrame.mNativeFrame);
-                            break;
-
-                        case REPLACE_TEXT:
-                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
-                            nativeReplaceTextfieldText(mNativeClass, msg.arg1,
-                                    msg.arg2, rep.mReplace, rep.mNewStart,
-                                    rep.mNewEnd, rep.mTextGeneration);
-                            break;
-
-                        case PASS_TO_JS: {
-                            JSKeyData jsData = (JSKeyData) msg.obj;
-                            KeyEvent evt = jsData.mEvent;
-                            int keyCode = evt.getKeyCode();
-                            int keyValue = evt.getUnicodeChar();
-                            int generation = msg.arg1;
-                            passToJs(mNativeClass,
-                                    generation,
-                                    jsData.mCurrentText,
-                                    keyCode,
-                                    keyValue,
-                                    evt.isDown(), evt.isShiftPressed(),
-                                    evt.isAltPressed(), evt.isSymPressed());
-                            break;
-                        }
-
-                        case SAVE_DOCUMENT_STATE: {
-                            nativeSaveDocumentState(mNativeClass);
-                            break;
-                        }
-
-                        case CLEAR_SSL_PREF_TABLE:
-                            // FIXME: This will not work for connections currently in use, as
-                            // they cache the certificate responses. See http://b/5324235.
-                            SslCertLookupTable.getInstance().clear();
-                            nativeCloseIdleConnections(mNativeClass);
-                            break;
-
-                        case SET_ACTIVE:
-                            nativeSetFocusControllerActive(mNativeClass, msg.arg1 == 1);
-                            break;
-
-                        case ADD_JS_INTERFACE:
-                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
-                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
-                                    jsData.mInterfaceName, jsData.mRequireAnnotation);
-                            break;
-
-                        case REMOVE_JS_INTERFACE:
-                            jsData = (JSInterfaceData) msg.obj;
-                            mBrowserFrame.removeJavascriptInterface(
-                                    jsData.mInterfaceName);
-                            break;
-
-                        case REQUEST_EXT_REPRESENTATION:
-                            mBrowserFrame.externalRepresentation(
-                                    (Message) msg.obj);
-                            break;
-
-                        case REQUEST_DOC_AS_TEXT:
-                            mBrowserFrame.documentAsText((Message) msg.obj);
-                            break;
-
-                        case SET_MOVE_MOUSE:
-                            nativeMoveMouse(mNativeClass, msg.arg1, msg.arg2);
-                            break;
-
-                        case REQUEST_CURSOR_HREF: {
-                            WebKitHitTest hit = performHitTest(msg.arg1, msg.arg2, 1, false);
-                            Message hrefMsg = (Message) msg.obj;
-                            Bundle data = hrefMsg.getData();
-                            data.putString(FocusNodeHref.URL,hit.mLinkUrl);
-                            data.putString(FocusNodeHref.TITLE, hit.mAnchorText);
-                            data.putString(FocusNodeHref.SRC, hit.mImageUrl);
-                            hrefMsg.sendToTarget();
-                            break;
-                        }
-
-                        case DOC_HAS_IMAGES:
-                            Message imageResult = (Message) msg.obj;
-                            imageResult.arg1 =
-                                    mBrowserFrame.documentHasImages() ? 1 : 0;
-                            imageResult.sendToTarget();
-                            break;
-
-                        case DELETE_SELECTION:
-                            TextSelectionData deleteSelectionData
-                                    = (TextSelectionData) msg.obj;
-                            nativeDeleteSelection(mNativeClass,
-                                    deleteSelectionData.mStart, deleteSelectionData.mEnd, msg.arg1);
-                            break;
-
-                        case SET_SELECTION:
-                            nativeSetSelection(mNativeClass, msg.arg1, msg.arg2);
-                            break;
-
-                        case MODIFY_SELECTION:
-                            mTextSelectionChangeReason
-                                    = TextSelectionData.REASON_ACCESSIBILITY_INJECTOR;
-                            final SomeArgs args = (SomeArgs) msg.obj;
-                            final String modifiedSelectionString = nativeModifySelection(
-                                    mNativeClass, args.argi1, args.argi2);
-                            // If accessibility is on, the main thread may be
-                            // waiting for a response. Send on webcore thread.
-                            mWebViewClassic.handleSelectionChangedWebCoreThread(
-                                    modifiedSelectionString, args.argi3);
-                            args.recycle();
-                            mTextSelectionChangeReason
-                                    = TextSelectionData.REASON_UNKNOWN;
-                            break;
-
-                        case LISTBOX_CHOICES:
-                            SparseBooleanArray choices = (SparseBooleanArray)
-                                    msg.obj;
-                            int choicesSize = msg.arg1;
-                            boolean[] choicesArray = new boolean[choicesSize];
-                            for (int c = 0; c < choicesSize; c++) {
-                                choicesArray[c] = choices.get(c);
-                            }
-                            nativeSendListBoxChoices(mNativeClass,
-                                    choicesArray, choicesSize);
-                            break;
-
-                        case SINGLE_LISTBOX_CHOICE:
-                            nativeSendListBoxChoice(mNativeClass, msg.arg1);
-                            break;
-
-                        case SET_BACKGROUND_COLOR:
-                            nativeSetBackgroundColor(mNativeClass, msg.arg1);
-                            break;
-
-                        case DUMP_DOMTREE:
-                            nativeDumpDomTree(mNativeClass, msg.arg1 == 1);
-                            break;
-
-                        case DUMP_RENDERTREE:
-                            nativeDumpRenderTree(mNativeClass, msg.arg1 == 1);
-                            break;
-
-                        case SET_JS_FLAGS:
-                            nativeSetJsFlags(mNativeClass, (String)msg.obj);
-                            break;
-
-                        case CONTENT_INVALIDATE_ALL:
-                            nativeContentInvalidateAll(mNativeClass);
-                            break;
-
-                        case SAVE_WEBARCHIVE:
-                            WebViewClassic.SaveWebArchiveMessage saveMessage =
-                                (WebViewClassic.SaveWebArchiveMessage)msg.obj;
-                            saveMessage.mResultFile =
-                                saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
-                            mWebViewClassic.mPrivateHandler.obtainMessage(
-                                WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
-                            break;
-
-                        case GEOLOCATION_PERMISSIONS_PROVIDE:
-                            GeolocationPermissionsData data =
-                                    (GeolocationPermissionsData) msg.obj;
-                            nativeGeolocationPermissionsProvide(mNativeClass,
-                                    data.mOrigin, data.mAllow, data.mRemember);
-                            break;
-
-                        case CLEAR_CONTENT:
-                            // Clear the view so that onDraw() will draw nothing
-                            // but white background
-                            // (See public method WebView.clearView)
-                            clearContent();
-                            break;
-
-                        case MESSAGE_RELAY:
-                            ((Message) msg.obj).sendToTarget();
-                            break;
-
-                        case POPULATE_VISITED_LINKS:
-                            nativeProvideVisitedHistory(mNativeClass, (String[])msg.obj);
-                            break;
-
-                        case HIDE_FULLSCREEN:
-                            nativeFullScreenPluginHidden(mNativeClass, msg.arg1);
-                            break;
-
-                        case PLUGIN_SURFACE_READY:
-                            nativePluginSurfaceReady(mNativeClass);
-                            break;
-
-                        case NOTIFY_ANIMATION_STARTED:
-                            nativeNotifyAnimationStarted(mNativeClass);
-                            break;
-
-                        case ADD_PACKAGE_NAMES:
-                            if (BrowserFrame.sJavaBridge == null) {
-                                throw new IllegalStateException("No WebView " +
-                                        "has been created in this process!");
-                            }
-                            BrowserFrame.sJavaBridge.addPackageNames(
-                                    (Set<String>) msg.obj);
-                            break;
-
-                        case SET_USE_MOCK_GEOLOCATION:
-                            setUseMockGeolocation();
-                            break;
-
-                        case SET_USE_MOCK_DEVICE_ORIENTATION:
-                            setUseMockDeviceOrientation();
-                            break;
-
-                        case AUTOFILL_FORM:
-                            nativeAutoFillForm(mNativeClass, msg.arg1);
-                            mWebViewClassic.mPrivateHandler.obtainMessage(
-                                    WebViewClassic.AUTOFILL_COMPLETE, null).sendToTarget();
-                            break;
-
-                        case EXECUTE_JS:
-                            if (msg.obj instanceof String) {
-                                if (DebugFlags.WEB_VIEW_CORE) {
-                                    Log.d(LOGTAG, "Executing JS : " + msg.obj);
-                                }
-                                mBrowserFrame.stringByEvaluatingJavaScriptFromString(
-                                        (String) msg.obj);
-                            }
-                            break;
-                        case SCROLL_LAYER:
-                            int nativeLayer = msg.arg1;
-                            Rect rect = (Rect) msg.obj;
-                            nativeScrollLayer(mNativeClass, nativeLayer,
-                                    rect);
-                            break;
-
-                        case DELETE_TEXT: {
-                            int[] handles = (int[]) msg.obj;
-                            nativeDeleteText(mNativeClass, handles[0],
-                                    handles[1], handles[2], handles[3]);
-                            break;
-                        }
-                        case COPY_TEXT: {
-                            int[] handles = (int[]) msg.obj;
-                            String copiedText = nativeGetText(mNativeClass,
-                                    handles[0], handles[1], handles[2],
-                                    handles[3]);
-                            if (copiedText != null) {
-                                mWebViewClassic.mPrivateHandler.obtainMessage(
-                                        WebViewClassic.COPY_TO_CLIPBOARD, copiedText)
-                                        .sendToTarget();
-                            }
-                            break;
-                        }
-                        case INSERT_TEXT:
-                            nativeInsertText(mNativeClass, (String) msg.obj);
-                            break;
-                        case SELECT_TEXT: {
-                            int handleId = (Integer) msg.obj;
-                            nativeSelectText(mNativeClass, handleId,
-                                    msg.arg1, msg.arg2);
-                            break;
-                        }
-                        case SELECT_WORD_AT: {
-                            mTextSelectionChangeReason
-                                    = TextSelectionData.REASON_SELECT_WORD;
-                            int x = msg.arg1;
-                            int y = msg.arg2;
-                            if (!nativeSelectWordAt(mNativeClass, x, y)) {
-                                mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE)
-                                    .sendToTarget();
-                            }
-                            mTextSelectionChangeReason
-                                    = TextSelectionData.REASON_UNKNOWN;
-                            break;
-                        }
-                        case SELECT_ALL:
-                            nativeSelectAll(mNativeClass);
-                            break;
-                        case FIND_ALL: {
-                            FindAllRequest request = (FindAllRequest)msg.obj;
-                            if (request != null) {
-                                int matchCount = nativeFindAll(mNativeClass, request.mSearchText);
-                                int matchIndex = nativeFindNext(mNativeClass, true);
-                                synchronized (request) {
-                                    request.mMatchCount = matchCount;
-                                    request.mMatchIndex = matchIndex;
-                                    request.notify();
-                                }
-                            } else {
-                                nativeFindAll(mNativeClass, null);
-                            }
-                            Message.obtain(mWebViewClassic.mPrivateHandler,
-                                    WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget();
-                            break;
-                        }
-                        case FIND_NEXT: {
-                            FindAllRequest request = (FindAllRequest)msg.obj;
-                            int matchIndex = nativeFindNext(mNativeClass, msg.arg1 != 0);
-                            synchronized (request) {
-                                request.mMatchIndex = matchIndex;
-                            }
-                            Message.obtain(mWebViewClassic.mPrivateHandler,
-                                    WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget();
-                            break;
-                        }
-                        case SET_INITIAL_FOCUS:
-                            nativeSetInitialFocus(mNativeClass, msg.arg1);
-                            break;
-                        case SAVE_VIEW_STATE:
-                            SaveViewStateRequest request = (SaveViewStateRequest) msg.obj;
-                            saveViewState(request.mStream, request.mCallback);
-                            break;
-                    }
-                }
-
-            };
-            // Take all queued messages and resend them to the new handler.
-            synchronized (this) {
-                int size = mMessages.size();
-                for (int i = 0; i < size; i++) {
-                    mHandler.sendMessage(mMessages.get(i));
-                }
-                mMessages = null;
-            }
-        }
-
-        @Override
-        public Looper getWebKitLooper() {
-            return mHandler.getLooper();
-        }
-
-        @Override
-        public boolean dispatchWebKitEvent(WebViewInputDispatcher dispatcher,
-                MotionEvent event, int eventType, int flags) {
-            if (mNativeClass == 0) {
-                return false;
-            }
-            switch (eventType) {
-                case WebViewInputDispatcher.EVENT_TYPE_HIT_TEST:
-                    int x = Math.round(event.getX());
-                    int y = Math.round(event.getY());
-                    WebKitHitTest hit = performHitTest(x, y,
-                            mWebViewClassic.getScaledNavSlop(), true);
-                    mWebViewClassic.mPrivateHandler.obtainMessage(
-                            WebViewClassic.HIT_TEST_RESULT, hit).sendToTarget();
-                    return false;
-
-                case WebViewInputDispatcher.EVENT_TYPE_CLICK:
-                    return nativeMouseClick(mNativeClass);
-
-                case WebViewInputDispatcher.EVENT_TYPE_TOUCH: {
-                    int count = event.getPointerCount();
-                    int[] idArray = new int[count];
-                    int[] xArray = new int[count];
-                    int[] yArray = new int[count];
-                    for (int i = 0; i < count; i++) {
-                        idArray[i] = event.getPointerId(i);
-                        xArray[i] = (int) event.getX(i);
-                        yArray[i] = (int) event.getY(i);
-                    }
-                    int touchFlags = nativeHandleTouchEvent(mNativeClass,
-                            event.getActionMasked(),
-                            idArray, xArray, yArray, count,
-                            event.getActionIndex(), event.getMetaState());
-                    if (touchFlags == 0
-                            && event.getActionMasked() != MotionEvent.ACTION_CANCEL
-                            && (flags & WebViewInputDispatcher.FLAG_PRIVATE) == 0) {
-                        dispatcher.skipWebkitForRemainingTouchStream();
-                    }
-                    return (touchFlags & TOUCH_FLAG_PREVENT_DEFAULT) > 0;
-                }
-
-                default:
-                    return false;
-            }
-        }
-
-        /**
-         * Send a message internally to the queue or to the handler
-         */
-        private synchronized void sendMessage(Message msg) {
-            if (mBlockMessages) {
-                return;
-            }
-            if (mMessages != null) {
-                mMessages.add(msg);
-            } else {
-                mHandler.sendMessage(msg);
-            }
-        }
-
-        private synchronized void removeMessages(int what) {
-            if (mBlockMessages) {
-                return;
-            }
-            if (what == EventHub.WEBKIT_DRAW) {
-                mDrawIsScheduled = false;
-            }
-            if (mMessages != null) {
-                Iterator<Message> iter = mMessages.iterator();
-                while (iter.hasNext()) {
-                    Message m = iter.next();
-                    if (m.what == what) {
-                        iter.remove();
-                    }
-                }
-            } else {
-                mHandler.removeMessages(what);
-            }
-        }
-
-        private synchronized void sendMessageDelayed(Message msg, long delay) {
-            if (mBlockMessages) {
-                return;
-            }
-            mHandler.sendMessageDelayed(msg, delay);
-        }
-
-        /**
-         * Send a message internally to the front of the queue.
-         */
-        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
-            if (mBlockMessages) {
-                return;
-            }
-            if (mMessages != null) {
-                mMessages.add(0, msg);
-            } else {
-                mHandler.sendMessageAtFrontOfQueue(msg);
-            }
-        }
-
-        /**
-         * Remove all the messages.
-         */
-        private synchronized void removeMessages() {
-            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
-            mDrawIsScheduled = false;
-            if (mMessages != null) {
-                mMessages.clear();
-            } else {
-                mHandler.removeCallbacksAndMessages(null);
-            }
-        }
-
-        /**
-         * Block sending messages to the EventHub.
-         */
-        private synchronized void blockMessages() {
-            mBlockMessages = true;
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods called by host activity (in the same thread)
-    //-------------------------------------------------------------------------
-
-    void stopLoading() {
-        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
-        if (mBrowserFrame != null) {
-            mBrowserFrame.stopLoading();
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods called by WebView
-    // If it refers to local variable, it needs synchronized().
-    // If it needs WebCore, it has to send message.
-    //-------------------------------------------------------------------------
-
-    /**
-     * @hide
-     */
-    public void sendMessage(Message msg) {
-        mEventHub.sendMessage(msg);
-    }
-
-    void sendMessages(ArrayList<Message> messages) {
-        synchronized (mEventHub) {
-            for (int i = 0; i < messages.size(); i++) {
-                mEventHub.sendMessage(messages.get(i));
-            }
-        }
-    }
-
-    void sendMessage(int what) {
-        mEventHub.sendMessage(Message.obtain(null, what));
-    }
-
-    void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
-        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
-                null, what, arg1, arg2, obj));
-    }
-
-    void sendMessage(int what, Object obj) {
-        mEventHub.sendMessage(Message.obtain(null, what, obj));
-    }
-
-    void sendMessage(int what, int arg1) {
-        // just ignore the second argument (make it 0)
-        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
-    }
-
-    void sendMessage(int what, int arg1, int arg2) {
-        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
-    }
-
-    void sendMessage(int what, int arg1, Object obj) {
-        // just ignore the second argument (make it 0)
-        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
-    }
-
-    void sendMessage(int what, int arg1, int arg2, Object obj) {
-        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
-    }
-
-    void sendMessageAtFrontOfQueue(int what, Object obj) {
-        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
-                null, what, obj));
-    }
-
-    void sendMessageDelayed(int what, Object obj, long delay) {
-        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
-    }
-
-    void removeMessages(int what) {
-        mEventHub.removeMessages(what);
-    }
-
-    void removeMessages() {
-        mEventHub.removeMessages();
-    }
-
-    /**
-     * Sends a DESTROY message to WebCore.
-     * Called from UI thread.
-     */
-    void destroy() {
-        synchronized (mEventHub) {
-            // send DESTROY to front of queue
-            // PAUSE/RESUME timers will still be processed even if they get handled later
-            mEventHub.mDestroying = true;
-            mEventHub.sendMessageAtFrontOfQueue(
-                    Message.obtain(null, EventHub.DESTROY));
-            mEventHub.blockMessages();
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // WebViewCore private methods
-    //-------------------------------------------------------------------------
-
-    private WebKitHitTest performHitTest(int x, int y, int slop, boolean moveMouse) {
-        WebKitHitTest hit = nativeHitTest(mNativeClass, x, y, slop, moveMouse);
-        hit.mHitTestX = x;
-        hit.mHitTestY = y;
-        hit.mHitTestSlop = slop;
-        hit.mHitTestMovedMouse = moveMouse;
-        return hit;
-    }
-
-    private void clearCache(boolean includeDiskFiles) {
-        mBrowserFrame.clearCache();
-    }
-
-    private void loadUrl(String url, Map<String, String> extraHeaders) {
-        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
-        mBrowserFrame.loadUrl(url, extraHeaders);
-    }
-
-    private String saveWebArchive(String filename, boolean autoname) {
-        if (DebugFlags.WEB_VIEW_CORE) {
-            Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
-        }
-        return mBrowserFrame.saveWebArchive(filename, autoname);
-    }
-
-    private void key(KeyEvent evt, int canTakeFocusDirection, boolean isDown) {
-        if (DebugFlags.WEB_VIEW_CORE) {
-            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
-                    + evt);
-        }
-        mChromeCanFocusDirection = canTakeFocusDirection;
-        int keyCode = evt.getKeyCode();
-        int unicodeChar = evt.getUnicodeChar();
-
-        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null
-                && evt.getCharacters().length() > 0) {
-            // we should only receive individual complex characters
-            unicodeChar = evt.getCharacters().codePointAt(0);
-        }
-
-        boolean handled = nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(),
-                evt.isShiftPressed(), evt.isAltPressed(),
-                evt.isSymPressed(), isDown);
-        mChromeCanFocusDirection = 0;
-        if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
-            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
-                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-                if (canTakeFocusDirection != 0 && isDown) {
-                    Message m = mWebViewClassic.mPrivateHandler.obtainMessage(
-                            WebViewClassic.TAKE_FOCUS);
-                    m.arg1 = canTakeFocusDirection;
-                    m.sendToTarget();
-                }
-                return;
-            }
-            // bubble up the event handling
-            // but do not bubble up the ENTER key, which would open the search
-            // bar without any text.
-            mCallbackProxy.onUnhandledKeyEvent(evt);
-        }
-    }
-
-    private void keyPress(int unicodeChar) {
-        nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, true);
-        nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, false);
-    }
-
-    // These values are used to avoid requesting a layout based on old values
-    private int mCurrentViewWidth = 0;
-    private int mCurrentViewHeight = 0;
-    private float mCurrentViewScale = 1.0f;
-
-    // notify webkit that our virtual view size changed size (after inv-zoom)
-    private void viewSizeChanged(WebViewClassic.ViewSizeData data) {
-        int w = data.mWidth;
-        int h = data.mHeight;
-        int textwrapWidth = data.mTextWrapWidth;
-        float scale = data.mScale;
-        if (DebugFlags.WEB_VIEW_CORE) {
-            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
-                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
-        }
-        if (w == 0) {
-            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
-            return;
-        }
-        int width = calculateWindowWidth(w);
-        int height = h;
-        if (width != w) {
-            float heightWidthRatio = data.mHeightWidthRatio;
-            float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w;
-            height = Math.round(ratio * width);
-        }
-        int screenHeight = data.mActualViewHeight > 0 ? data.mActualViewHeight : h;
-        nativeSetSize(mNativeClass, width, height, textwrapWidth, scale,
-                w, screenHeight, data.mAnchorX, data.mAnchorY, data.mIgnoreHeight);
-        // Remember the current width and height
-        boolean needInvalidate = (mCurrentViewWidth == 0);
-        mCurrentViewWidth = w;
-        mCurrentViewHeight = h;
-        mCurrentViewScale = scale;
-        if (needInvalidate) {
-            // ensure {@link #webkitDraw} is called as we were blocking in
-            // {@link #contentDraw} when mCurrentViewWidth is 0
-            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
-            contentDraw();
-        }
-    }
-
-    // Calculate width to be used in webkit window.
-    private int calculateWindowWidth(int viewWidth) {
-        int width = viewWidth;
-        if (mSettings.getUseWideViewPort()) {
-            if (mViewportWidth == -1) {
-                // Fixed viewport width.
-                width = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
-            } else if (mViewportWidth > 0) {
-                // Use website specified or desired fixed viewport width.
-                width = mViewportWidth;
-            } else {
-                // For mobile web site.
-                width = Math.round(mWebViewClassic.getViewWidth() /
-                        mWebViewClassic.getDefaultZoomScale());
-            }
-        }
-        return width;
-    }
-
-    // Utility method for exceededDatabaseQuota callback. Computes the sum
-    // of WebSQL database quota for all origins.
-    private long getUsedQuota() {
-        WebStorageClassic webStorage = WebStorageClassic.getInstance();
-        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
-
-        if (origins == null) {
-            return 0;
-        }
-        long usedQuota = 0;
-        for (WebStorage.Origin website : origins) {
-            usedQuota += website.getQuota();
-        }
-        return usedQuota;
-    }
-
-    // Used to avoid posting more than one draw message.
-    private boolean mDrawIsScheduled;
-
-    // Used to suspend drawing.
-    private boolean mDrawIsPaused;
-
-    // mInitialViewState is set by didFirstLayout() and then reset in the
-    // next webkitDraw after passing the state to the UI thread.
-    private ViewState mInitialViewState = null;
-    private boolean mFirstLayoutForNonStandardLoad;
-
-    static class ViewState {
-        float mMinScale;
-        float mMaxScale;
-        float mViewScale;
-        float mTextWrapScale;
-        float mDefaultScale;
-        int mScrollX;
-        int mScrollY;
-        boolean mMobileSite;
-        boolean mIsRestored;
-        boolean mShouldStartScrolledRight;
-    }
-
-    static class DrawData {
-        DrawData() {
-            mBaseLayer = 0;
-            mContentSize = new Point();
-        }
-        int mBaseLayer;
-        // view size that was used by webkit during the most recent layout
-        Point mViewSize;
-        Point mContentSize;
-        int mMinPrefWidth;
-        // only non-null if it is for the first picture set after the first layout
-        ViewState mViewState;
-        boolean mFirstLayoutForNonStandardLoad;
-    }
-
-    DrawData mLastDrawData = null;
-
-    private Object m_skipDrawFlagLock = new Object();
-    private boolean m_skipDrawFlag = false;
-    private boolean m_drawWasSkipped = false;
-
-    void pauseWebKitDraw() {
-        synchronized (m_skipDrawFlagLock) {
-            if (!m_skipDrawFlag) {
-                m_skipDrawFlag = true;
-            }
-        }
-    }
-
-    void resumeWebKitDraw() {
-        synchronized (m_skipDrawFlagLock) {
-            if (m_skipDrawFlag && m_drawWasSkipped) {
-                // a draw was dropped, send a retry
-                m_drawWasSkipped = false;
-                mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
-            }
-            m_skipDrawFlag = false;
-        }
-    }
-
-    private void webkitDraw() {
-        synchronized (m_skipDrawFlagLock) {
-            if (m_skipDrawFlag) {
-                m_drawWasSkipped = true;
-                return;
-            }
-        }
-
-        mDrawIsScheduled = false;
-        DrawData draw = new DrawData();
-        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
-        draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mContentSize);
-        if (draw.mBaseLayer == 0) {
-            if (mWebViewClassic != null && !mWebViewClassic.isPaused()) {
-                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
-                mEventHub.sendMessageDelayed(Message.obtain(null, EventHub.WEBKIT_DRAW), 10);
-            } else {
-                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused");
-            }
-            return;
-        }
-        mLastDrawData = draw;
-        webkitDraw(draw);
-    }
-
-    private void webkitDraw(DrawData draw) {
-        if (mWebViewClassic != null) {
-            draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
-            if (mSettings.getUseWideViewPort()) {
-                draw.mMinPrefWidth = Math.max(
-                        mViewportWidth == -1 ? WebViewClassic.DEFAULT_VIEWPORT_WIDTH
-                                : (mViewportWidth == 0 ? mCurrentViewWidth
-                                        : mViewportWidth),
-                        nativeGetContentMinPrefWidth(mNativeClass));
-            }
-            if (mInitialViewState != null) {
-                draw.mViewState = mInitialViewState;
-                mInitialViewState = null;
-            }
-            if (mFirstLayoutForNonStandardLoad) {
-                draw.mFirstLayoutForNonStandardLoad = true;
-                mFirstLayoutForNonStandardLoad = false;
-            }
-            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
-            pauseWebKitDraw();
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
-        }
-    }
-
-    private void saveViewState(OutputStream stream,
-            ValueCallback<Boolean> callback) {
-        // TODO: Create a native method to do this better without overloading
-        // the draw path (and fix saving <canvas>)
-        DrawData draw = new DrawData();
-        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "saveViewState start");
-        draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mContentSize);
-        boolean result = false;
-        try {
-            result = ViewStateSerializer.serializeViewState(stream, draw);
-        } catch (Throwable t) {
-            Log.w(LOGTAG, "Failed to save view state", t);
-        }
-        callback.onReceiveValue(result);
-        if (draw.mBaseLayer != 0) {
-            if (mDrawIsScheduled) {
-                mDrawIsScheduled = false;
-                mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
-            }
-            mLastDrawData = draw;
-            webkitDraw(draw);
-        }
-    }
-
-    static void reducePriority() {
-        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
-        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
-        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
-        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
-                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
-    }
-
-    static void resumePriority() {
-        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
-        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
-        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
-        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
-                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
-    }
-
-    static void sendStaticMessage(int messageType, Object argument) {
-        if (sWebCoreHandler == null)
-            return;
-
-        sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
-    }
-
-    static void pauseUpdatePicture(WebViewCore core) {
-        // Note: there is one possible failure mode. If pauseUpdatePicture() is
-        // called from UI thread while WEBKIT_DRAW is just pulled out of the
-        // queue in WebCore thread to be executed. Then update won't be blocked.
-        if (core != null) {
-            if (!core.getSettings().enableSmoothTransition()) return;
-
-            synchronized (core) {
-                if (core.mNativeClass == 0) {
-                    Log.w(LOGTAG, "Cannot pauseUpdatePicture, core destroyed or not initialized!");
-                    return;
-                }
-                core.mDrawIsPaused = true;
-            }
-        }
-
-    }
-
-    static void resumeUpdatePicture(WebViewCore core) {
-        if (core != null) {
-            // if mDrawIsPaused is true, ignore the setting, continue to resume
-            if (!core.mDrawIsPaused)
-                return;
-
-            synchronized (core) {
-                if (core.mNativeClass == 0) {
-                    Log.w(LOGTAG, "Cannot resumeUpdatePicture, core destroyed!");
-                    return;
-                }
-                core.mDrawIsPaused = false;
-                // always redraw on resume to reenable gif animations
-                core.mDrawIsScheduled = false;
-            }
-        }
-    }
-
-    static boolean isUpdatePicturePaused(WebViewCore core) {
-        return core != null ? core.mDrawIsPaused : false;
-    }
-
-    //////////////////////////////////////////////////////////////////////////
-
-    private void restoreState(int index) {
-        WebBackForwardListClassic list = mCallbackProxy.getBackForwardList();
-        int size = list.getSize();
-        for (int i = 0; i < size; i++) {
-            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
-        }
-        mBrowserFrame.mLoadInitFromJava = true;
-        WebBackForwardListClassic.restoreIndex(mBrowserFrame.mNativeFrame, index);
-        mBrowserFrame.mLoadInitFromJava = false;
-    }
-
-    //-------------------------------------------------------------------------
-    // Implement abstract methods in WebViewCore, native WebKit callback part
-    //-------------------------------------------------------------------------
-
-    // called from JNI or WebView thread
-    /* package */ void contentDraw() {
-        synchronized (this) {
-            if (mWebViewClassic == null || mBrowserFrame == null) {
-                // We were destroyed
-                return;
-            }
-            // don't update the Picture until we have an initial width and finish
-            // the first layout
-            if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
-                return;
-            }
-            // only fire an event if this is our first request
-            if (mDrawIsScheduled) return;
-            mDrawIsScheduled = true;
-            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
-        }
-    }
-
-    // called by JNI
-    private void contentScrollTo(int x, int y, boolean animate,
-            boolean onlyIfImeIsShowing) {
-        if (!mBrowserFrame.firstLayoutDone()) {
-            /*
-             * WebKit restore state will be called before didFirstLayout(),
-             * remember the position as it has to be applied after restoring
-             * zoom factor which is controlled by screenWidth.
-             */
-            mRestoredX = x;
-            mRestoredY = y;
-            return;
-        }
-        if (mWebViewClassic != null) {
-            Message msg = Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0,
-                    onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
-            if (mDrawIsScheduled) {
-                mEventHub.sendMessage(Message.obtain(null,
-                        EventHub.MESSAGE_RELAY, msg));
-            } else {
-                msg.sendToTarget();
-            }
-        }
-    }
-
-    // called by JNI
-    private void sendNotifyProgressFinished() {
-        contentDraw();
-    }
-
-    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
-        be scaled before they can be used by the view system, which happens
-        in WebView since it (and its thread) know the current scale factor.
-     */
-    private void sendViewInvalidate(int left, int top, int right, int bottom) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                           WebViewClassic.INVAL_RECT_MSG_ID,
-                           new Rect(left, top, right, bottom)).sendToTarget();
-        }
-    }
-
-    private static boolean mRepaintScheduled = false;
-
-    /*
-     * Called by the WebView thread
-     */
-    /* package */ void signalRepaintDone() {
-        mRepaintScheduled = false;
-    }
-
-    // Gets the WebViewClassic corresponding to this WebViewCore. Note that the
-    // WebViewClassic object must only be used on the UI thread.
-    /* package */ WebViewClassic getWebViewClassic() {
-        return mWebViewClassic;
-    }
-
-    // Called by JNI
-    private WebView getWebView() {
-        return mWebViewClassic.getWebView();
-    }
-
-    // Called by JNI
-    private void sendPluginDrawMsg() {
-        sendMessage(EventHub.PLUGIN_SURFACE_READY);
-    }
-
-    private native void setViewportSettingsFromNative(int nativeClass);
-
-    // called by JNI
-    private void didFirstLayout(boolean standardLoad) {
-        if (DebugFlags.WEB_VIEW_CORE) {
-            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
-        }
-
-        mBrowserFrame.didFirstLayout();
-
-        if (mWebViewClassic == null) return;
-
-        boolean updateViewState = standardLoad || mIsRestored;
-        setupViewport(updateViewState);
-        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
-        // be called after the WebView updates its state. If updateRestoreState
-        // is false, start to draw now as it is ready.
-        if (!updateViewState) {
-            mWebViewClassic.mViewManager.postReadyToDrawAll();
-        }
-
-        // remove the touch highlight when moving to a new page
-        mWebViewClassic.mPrivateHandler.sendEmptyMessage(
-                WebViewClassic.HIT_TEST_RESULT);
-
-        // reset the scroll position, the restored offset and scales
-        mRestoredX = mRestoredY = 0;
-        mIsRestored = false;
-        mRestoredScale = mRestoredTextWrapScale = 0;
-    }
-
-    // called by JNI
-    private void updateViewport() {
-        // Update viewport asap to make sure we get correct one.
-        setupViewport(true);
-    }
-
-    static float getFixedDisplayDensity(Context context) {
-        // We make bad assumptions about multiplying and dividing density by 100,
-        // force them to be true with this hack
-        float density = context.getResources().getDisplayMetrics().density;
-        return ((int) (density * 100)) / 100.0f;
-    }
-
-    private void setupViewport(boolean updateViewState) {
-        if (mWebViewClassic == null || mSettings == null) {
-            // We've been destroyed or are being destroyed, return early
-            return;
-        }
-        // set the viewport settings from WebKit
-        setViewportSettingsFromNative(mNativeClass);
-
-        // clamp initial scale
-        if (mViewportInitialScale > 0) {
-            if (mViewportMinimumScale > 0) {
-                mViewportInitialScale = Math.max(mViewportInitialScale,
-                        mViewportMinimumScale);
-            }
-            if (mViewportMaximumScale > 0) {
-                mViewportInitialScale = Math.min(mViewportInitialScale,
-                        mViewportMaximumScale);
-            }
-        }
-
-        if (mSettings.forceUserScalable()) {
-            mViewportUserScalable = true;
-            if (mViewportInitialScale > 0) {
-                if (mViewportMinimumScale > 0) {
-                    mViewportMinimumScale = Math.min(mViewportMinimumScale,
-                            mViewportInitialScale / 2);
-                }
-                if (mViewportMaximumScale > 0) {
-                    mViewportMaximumScale = Math.max(mViewportMaximumScale,
-                            mViewportInitialScale * 2);
-                }
-            } else {
-                if (mViewportMinimumScale > 0) {
-                    mViewportMinimumScale = Math.min(mViewportMinimumScale, 50);
-                }
-                if (mViewportMaximumScale > 0) {
-                    mViewportMaximumScale = Math.max(mViewportMaximumScale, 200);
-                }
-            }
-        }
-
-        // adjust the default scale to match the densityDpi
-        float adjust = 1.0f;
-        if (mViewportDensityDpi == -1) {
-            adjust = getFixedDisplayDensity(mContext);
-        } else if (mViewportDensityDpi > 0) {
-            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
-                    / mViewportDensityDpi;
-            adjust = ((int) (adjust * 100)) / 100.0f;
-        }
-
-        // Remove any update density messages in flight.
-        // If the density is indeed different from WebView's default scale,
-        // a new message will be queued.
-        mWebViewClassic.mPrivateHandler.removeMessages(
-                WebViewClassic.UPDATE_ZOOM_DENSITY);
-        if (adjust != mWebViewClassic.getDefaultZoomScale()) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
-        }
-        int defaultScale = (int) (adjust * 100);
-
-        if (mViewportInitialScale > 0) {
-            mViewportInitialScale *= adjust;
-        }
-        if (mViewportMinimumScale > 0) {
-            mViewportMinimumScale *= adjust;
-        }
-        if (mViewportMaximumScale > 0) {
-            mViewportMaximumScale *= adjust;
-        }
-
-        // infer the values if they are not defined.
-        if (mViewportWidth == 0) {
-            if (mViewportInitialScale == 0) {
-                mViewportInitialScale = defaultScale;
-            }
-        }
-        if (mViewportUserScalable == false) {
-            mViewportInitialScale = defaultScale;
-            mViewportMinimumScale = defaultScale;
-            mViewportMaximumScale = defaultScale;
-        }
-        if (mViewportMinimumScale > mViewportInitialScale
-                && mViewportInitialScale != 0) {
-            mViewportMinimumScale = mViewportInitialScale;
-        }
-        if (mViewportMaximumScale > 0
-                && mViewportMaximumScale < mViewportInitialScale) {
-            mViewportMaximumScale = mViewportInitialScale;
-        }
-        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
-            mViewportWidth = 0;
-        }
-
-        // if mViewportWidth is 0, it means device-width, always update.
-        if (mViewportWidth != 0 && !updateViewState) {
-            // For non standard load, since updateViewState will be false.
-            mFirstLayoutForNonStandardLoad = true;
-            ViewState viewState = new ViewState();
-            viewState.mMinScale = mViewportMinimumScale / 100.0f;
-            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
-            viewState.mDefaultScale = adjust;
-            // as mViewportWidth is not 0, it is not mobile site.
-            viewState.mMobileSite = false;
-            // for non-mobile site, we don't need minPrefWidth, set it as 0
-            viewState.mScrollX = 0;
-            viewState.mShouldStartScrolledRight = false;
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
-            return;
-        }
-
-        // now notify webview
-        // webViewWidth refers to the width in the view system
-        int webViewWidth;
-        // viewportWidth refers to the width in the document system
-        int viewportWidth = mCurrentViewWidth;
-        if (viewportWidth == 0) {
-            // this may happen when WebView just starts. This is not perfect as
-            // we call WebView method from WebCore thread. But not perfect
-            // reference is better than no reference.
-            webViewWidth = mWebViewClassic.getViewWidth();
-            viewportWidth = (int) (webViewWidth / adjust);
-            if (viewportWidth == 0) {
-                if (DebugFlags.WEB_VIEW_CORE) {
-                    Log.v(LOGTAG, "Can't get the viewWidth yet");
-                }
-            }
-        } else {
-            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
-        }
-        mInitialViewState = new ViewState();
-        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
-        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
-        mInitialViewState.mDefaultScale = adjust;
-        mInitialViewState.mScrollX = mRestoredX;
-        mInitialViewState.mScrollY = mRestoredY;
-        mInitialViewState.mShouldStartScrolledRight = (mRestoredX == 0)
-                && (mRestoredY == 0)
-                && (mBrowserFrame != null)
-                && mBrowserFrame.getShouldStartScrolledRight();
-
-        mInitialViewState.mMobileSite = (0 == mViewportWidth);
-        if (mIsRestored) {
-            mInitialViewState.mIsRestored = true;
-            mInitialViewState.mViewScale = mRestoredScale;
-            if (mRestoredTextWrapScale > 0) {
-                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
-            } else {
-                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
-            }
-        } else {
-            if (mViewportInitialScale > 0) {
-                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
-                        mViewportInitialScale / 100.0f;
-            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth &&
-                !getSettings().getUseFixedViewport()) {
-                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
-                        (float) webViewWidth / mViewportWidth;
-            } else {
-                mInitialViewState.mTextWrapScale = adjust;
-                if (mSettings.getUseWideViewPort()) {
-                    // 0 will trigger WebView to turn on zoom overview mode
-                    mInitialViewState.mViewScale = 0;
-                } else {
-                    mInitialViewState.mViewScale = adjust;
-                }
-            }
-        }
-
-        if (mWebViewClassic.mHeightCanMeasure) {
-            // Trick to ensure that the Picture has the exact height for the
-            // content by forcing to layout with 0 height after the page is
-            // ready, which is indicated by didFirstLayout. This is essential to
-            // get rid of the white space in the GMail which uses WebView for
-            // message view.
-            mWebViewClassic.mLastHeightSent = 0;
-            // Send a negative scale to indicate that WebCore should reuse
-            // the current scale
-            WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData();
-            data.mWidth = mWebViewClassic.mLastWidthSent;
-            data.mHeight = 0;
-            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
-            // true. It is safe to use mWidth for mTextWrapWidth.
-            data.mTextWrapWidth = data.mWidth;
-            data.mScale = -1.0f;
-            data.mIgnoreHeight = false;
-            data.mAnchorX = data.mAnchorY = 0;
-            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
-            // avoid pushing the wrong picture to the WebView side. If there is
-            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
-            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
-            // in the queue, as mLastHeightSent has been updated here, we may
-            // miss the requestLayout in WebView side after the new picture.
-            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
-            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
-                    EventHub.VIEW_SIZE_CHANGED, data));
-        } else {
-            if (viewportWidth == 0) {
-                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
-                // to WebViewCore
-                mWebViewClassic.mLastWidthSent = 0;
-            } else {
-                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.
-                float tentativeScale = mInitialViewState.mViewScale;
-                if (tentativeScale == 0) {
-                    // The following tries to figure out more correct view scale
-                    // and text wrap scale to be sent to webkit, by using some
-                    // knowledge from web settings and zoom manager.
-
-                    // Calculated window width will be used to guess the scale
-                    // in zoom overview mode.
-                    tentativeScale = mInitialViewState.mTextWrapScale;
-                    int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
-                    int windowWidth = calculateWindowWidth(tentativeViewWidth);
-                    // In viewport setup time, since no content width is known, we assume
-                    // the windowWidth will be the content width, to get a more likely
-                    // zoom overview scale.
-                    data.mScale = (float) webViewWidth / windowWidth;
-                    if (!mSettings.getLoadWithOverviewMode()) {
-                        // If user choose non-overview mode.
-                        data.mScale = Math.max(data.mScale, tentativeScale);
-                    }
-                    if (mSettings.isNarrowColumnLayout()) {
-                        // In case of automatic text reflow in fixed view port mode.
-                        mInitialViewState.mTextWrapScale =
-                                mWebViewClassic.computeReadingLevelScale(data.mScale);
-                    }
-                } else {
-                    // Scale is given such as when page is restored, use it.
-                    data.mScale = tentativeScale;
-                }
-                if (DebugFlags.WEB_VIEW_CORE) {
-                    Log.v(LOGTAG, "setupViewport"
-                             + " mRestoredScale=" + mRestoredScale
-                             + " mViewScale=" + mInitialViewState.mViewScale
-                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
-                             + " data.mScale= " + data.mScale
-                             );
-                }
-                data.mWidth = Math.round(webViewWidth / data.mScale);
-                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
-                // first layout before we sync our webview dimensions to it. In that case, we
-                // request the real height of the webview. This is not a perfect solution as we
-                // are calling a WebView method from the WebCore thread. But this is preferable
-                // to syncing an incorrect height.
-                data.mHeight = mCurrentViewHeight == 0 ?
-                        Math.round(mWebViewClassic.getViewHeight() / data.mScale)
-                        : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth);
-                data.mTextWrapWidth = Math.round(webViewWidth
-                        / mInitialViewState.mTextWrapScale);
-                data.mIgnoreHeight = false;
-                data.mAnchorX = data.mAnchorY = 0;
-                // send VIEW_SIZE_CHANGED to the front of the queue so that we
-                // can avoid pushing the wrong picture to the WebView side.
-                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
-                // Let webkit know the scale and inner width/height immediately
-                // in viewport setup time to avoid wrong information.
-                viewSizeChanged(data);
-            }
-        }
-    }
-
-    // called by JNI
-    private void restoreScale(float scale, float textWrapScale) {
-        if (mBrowserFrame.firstLayoutDone() == false) {
-            mIsRestored = true;
-            mRestoredScale = scale;
-            if (mSettings.getUseWideViewPort()) {
-                mRestoredTextWrapScale = textWrapScale;
-            }
-        }
-    }
-
-    // called by JNI
-    private void needTouchEvents(boolean need) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
-                    .sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void updateTextfield(int ptr, String text, int textGeneration) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
-                    textGeneration, text).sendToTarget();
-        }
-    }
-
-    private TextSelectionData createTextSelection(int start, int end, int selPtr) {
-        TextSelectionData data = new TextSelectionData(start, end, selPtr);
-        data.mSelectionReason = mTextSelectionChangeReason;
-        return data;
-    }
-
-    // called by JNI
-    private void updateTextSelection(int pointer, int start, int end,
-            int textGeneration, int selectionPtr) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
-                createTextSelection(start, end, selectionPtr)).sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void updateTextSizeAndScroll(int pointer, int width, int height,
-            int scrollX, int scrollY) {
-        if (mWebViewClassic != null) {
-            Rect rect = new Rect(-scrollX, -scrollY, width - scrollX,
-                    height - scrollY);
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.EDIT_TEXT_SIZE_CHANGED, pointer, 0, rect)
-                    .sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void clearTextEntry() {
-        if (mWebViewClassic == null) return;
-        Message.obtain(mWebViewClassic.mPrivateHandler,
-                WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget();
-    }
-
-    // called by JNI
-    private void initEditField(int start, int end, int selectionPtr,
-            TextFieldInitData initData) {
-        if (mWebViewClassic == null) {
-            return;
-        }
-        Message.obtain(mWebViewClassic.mPrivateHandler,
-                WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget();
-        Message.obtain(mWebViewClassic.mPrivateHandler,
-                WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID,
-                initData.mFieldPointer, 0,
-                createTextSelection(start, end, selectionPtr))
-                .sendToTarget();
-    }
-
-    private native void nativeRevealSelection(int nativeClass);
-    private native String nativeRequestLabel(int nativeClass, int framePtr,
-            int nodePtr);
-    /**
-     * Scroll the focused textfield to (xPercent, y) in document space
-     */
-    private native void nativeScrollFocusedTextInput(int nativeClass,
-            float xPercent, int y);
-
-    // these must be in document space (i.e. not scaled/zoomed).
-    private native void nativeSetScrollOffset(int nativeClass,
-            boolean sendScrollEvent, int dx, int dy);
-
-    private native void nativeSetGlobalBounds(int nativeClass, int x, int y,
-            int w, int h);
-
-    // called by JNI
-    private void requestListBox(String[] array, int[] enabledArray,
-            int[] selectedArray) {
-        if (mWebViewClassic != null) {
-            mWebViewClassic.requestListBox(array, enabledArray, selectedArray);
-        }
-    }
-
-    // called by JNI
-    private void requestListBox(String[] array, int[] enabledArray,
-            int selection) {
-        if (mWebViewClassic != null) {
-            mWebViewClassic.requestListBox(array, enabledArray, selection);
-        }
-
-    }
-
-    // called by JNI
-    private void requestKeyboard(boolean showKeyboard) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler,
-                    WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
-                    .sendToTarget();
-        }
-    }
-
-    private void setWebTextViewAutoFillable(int queryId, String preview) {
-        if (mWebViewClassic != null) {
-            Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE,
-                    new AutoFillData(queryId, preview))
-                    .sendToTarget();
-        }
-    }
-
-    Context getContext() {
-        return mContext;
-    }
-
-    // called by JNI
-    private void keepScreenOn(boolean screenOn) {
-        if (mWebViewClassic != null) {
-            Message message = mWebViewClassic.mPrivateHandler.obtainMessage(
-                    WebViewClassic.SCREEN_ON);
-            message.arg1 = screenOn ? 1 : 0;
-            message.sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private Class<?> getPluginClass(String libName, String clsName) {
-
-        if (mWebViewClassic == null) {
-            return null;
-        }
-
-        PluginManager pluginManager = PluginManager.getInstance(null);
-
-        String pkgName = pluginManager.getPluginsAPKName(libName);
-        if (pkgName == null) {
-            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
-            return null;
-        }
-
-        try {
-            return pluginManager.getPluginClass(pkgName, clsName);
-        } catch (NameNotFoundException e) {
-            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
-        } catch (ClassNotFoundException e) {
-            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
-                    ") in the apk (" + pkgName + ")");
-        }
-
-        return null;
-    }
-
-    // called by JNI. PluginWidget function to launch a full-screen view using a
-    // View object provided by the plugin class.
-    private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) {
-        if (mWebViewClassic == null) {
-            return;
-        }
-
-        Message message = mWebViewClassic.mPrivateHandler.obtainMessage(
-                WebViewClassic.SHOW_FULLSCREEN);
-        message.obj = childView.mView;
-        message.arg1 = orientation;
-        message.arg2 = npp;
-        message.sendToTarget();
-    }
-
-    // called by JNI
-    private void hideFullScreenPlugin() {
-        if (mWebViewClassic == null) {
-            return;
-        }
-        mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN)
-                .sendToTarget();
-    }
-
-    private ViewManager.ChildView createSurface(View pluginView) {
-        if (mWebViewClassic == null) {
-            return null;
-        }
-
-        if (pluginView == null) {
-            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
-            return null;
-        }
-
-        // ensures the view system knows the view can redraw itself
-        pluginView.setWillNotDraw(false);
-
-        if(pluginView instanceof SurfaceView)
-            ((SurfaceView)pluginView).setZOrderOnTop(true);
-
-        ViewManager.ChildView view = mWebViewClassic.mViewManager.createView();
-        view.mView = pluginView;
-        return view;
-    }
-
-    // called by JNI.  PluginWidget functions for creating an embedded View for
-    // the surface drawing model.
-    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
-                                             int width, int height) {
-        ViewManager.ChildView view = createSurface(pluginView);
-        view.attachView(x, y, width, height);
-        return view;
-    }
-
-    private void updateSurface(ViewManager.ChildView childView, int x, int y,
-            int width, int height) {
-        childView.attachView(x, y, width, height);
-    }
-
-    private void destroySurface(ViewManager.ChildView childView) {
-        childView.removeView();
-    }
-
-    // called by JNI
-    static class ShowRectData {
-        int mLeft;
-        int mTop;
-        int mWidth;
-        int mHeight;
-        int mContentWidth;
-        int mContentHeight;
-        float mXPercentInDoc;
-        float mXPercentInView;
-        float mYPercentInDoc;
-        float mYPercentInView;
-    }
-
-    private void showRect(int left, int top, int width, int height,
-            int contentWidth, int contentHeight, float xPercentInDoc,
-            float xPercentInView, float yPercentInDoc, float yPercentInView) {
-        if (mWebViewClassic != null) {
-            ShowRectData data = new ShowRectData();
-            data.mLeft = left;
-            data.mTop = top;
-            data.mWidth = width;
-            data.mHeight = height;
-            data.mContentWidth = contentWidth;
-            data.mContentHeight = contentHeight;
-            data.mXPercentInDoc = xPercentInDoc;
-            data.mXPercentInView = xPercentInView;
-            data.mYPercentInDoc = yPercentInDoc;
-            data.mYPercentInView = yPercentInView;
-            Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID,
-                    data).sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void centerFitRect(int x, int y, int width, int height) {
-        if (mWebViewClassic == null) {
-            return;
-        }
-        mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT,
-                new Rect(x, y, x + width, y + height)).sendToTarget();
-    }
-
-    // called by JNI
-    private void setScrollbarModes(int hMode, int vMode) {
-        if (mWebViewClassic == null) {
-            return;
-        }
-        mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES,
-                hMode, vMode).sendToTarget();
-    }
-
-    // called by JNI
-    private void selectAt(int x, int y) {
-        // TODO: Figure out what to do with this (b/6111818)
-    }
-
-    private void setUseMockDeviceOrientation() {
-        mDeviceMotionAndOrientationManager.setUseMock();
-    }
-
-    private void setUseMockGeolocation() {
-        mMockGeolocation.setUseMock();
-    }
-
-    public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
-        mMockGeolocation.setPosition(latitude, longitude, accuracy);
-    }
-
-    public void setMockGeolocationError(int code, String message) {
-        mMockGeolocation.setError(code, message);
-    }
-
-    public void setMockGeolocationPermission(boolean allow) {
-        mMockGeolocation.setPermission(allow);
-    }
-
-    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
-            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
-        mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha,
-                canProvideBeta, beta, canProvideGamma, gamma);
-    }
-
-    protected DeviceMotionService getDeviceMotionService() {
-        if (mDeviceMotionService == null) {
-            mDeviceMotionService =
-                    new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext);
-        }
-        return mDeviceMotionService;
-    }
-
-    protected DeviceOrientationService getDeviceOrientationService() {
-        if (mDeviceOrientationService == null) {
-            mDeviceOrientationService =
-                    new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext);
-        }
-        return mDeviceOrientationService;
-    }
-
-    static void setShouldMonitorWebCoreThread() {
-        sShouldMonitorWebCoreThread = true;
-    }
-
-    private native void nativePause(int nativeClass);
-    private native void nativeResume(int nativeClass);
-    private native void nativeFreeMemory(int nativeClass);
-    private native void nativeFullScreenPluginHidden(int nativeClass, int npp);
-    private native void nativePluginSurfaceReady(int nativeClass);
-
-    private native WebKitHitTest nativeHitTest(int nativeClass, int x, int y,
-            int slop, boolean moveMouse);
-
-    private native void nativeAutoFillForm(int nativeClass, int queryId);
-    private native void nativeScrollLayer(int nativeClass, int layer, Rect rect);
-    private native int nativeFindAll(int nativeClass, String text);
-    private native int nativeFindNext(int nativeClass, boolean forward);
-
-    /**
-     * Deletes editable text between two points. Note that the selection may
-     * differ from the WebView's selection because the algorithms for selecting
-     * text differs for non-LTR text. Any text that isn't editable will be
-     * left unchanged.
-     * @param nativeClass The pointer to the native class (mNativeClass)
-     * @param startX The X position of the top-left selection point.
-     * @param startY The Y position of the top-left selection point.
-     * @param endX The X position of the bottom-right selection point.
-     * @param endY The Y position of the bottom-right selection point.
-     */
-    private native void nativeDeleteText(int nativeClass,
-            int startX, int startY, int endX, int endY);
-    /**
-     * Inserts text at the current cursor position. If the currently-focused
-     * node does not have a cursor position then this function does nothing.
-     */
-    private native void nativeInsertText(int nativeClass, String text);
-    /**
-     * Gets the text between two selection points. Note that the selection
-     * may differ from the WebView's selection because the algorithms for
-     * selecting text differs for non-LTR text.
-     * @param nativeClass The pointer to the native class (mNativeClass)
-     * @param startX The X position of the top-left selection point.
-     * @param startY The Y position of the top-left selection point.
-     * @param endX The X position of the bottom-right selection point.
-     * @param endY The Y position of the bottom-right selection point.
-     */
-    private native String nativeGetText(int nativeClass,
-            int startX, int startY, int endX, int endY);
-    private native void nativeSelectText(int nativeClass,
-            int handleId, int x, int y);
-    private native void nativeClearTextSelection(int nativeClass);
-    private native boolean nativeSelectWordAt(int nativeClass, int x, int y);
-    private native void nativeSelectAll(int nativeClass);
-    private native void nativeSetInitialFocus(int nativeClass, int keyDirection);
-
-    private static native void nativeCertTrustChanged();
-}
diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java
deleted file mode 100644
index be01028..0000000
--- a/core/java/android/webkit/WebViewDatabaseClassic.java
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * 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 java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.Map.Entry;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
-import android.util.Log;
-
-final class WebViewDatabaseClassic extends WebViewDatabase {
-    private static final String LOGTAG = "WebViewDatabaseClassic";
-    private static final String DATABASE_FILE = "webview.db";
-    private static final String CACHE_DATABASE_FILE = "webviewCache.db";
-
-    private static final int DATABASE_VERSION = 11;
-    // 2 -> 3 Modified Cache table to allow cache of redirects
-    // 3 -> 4 Added Oma-Downloads table
-    // 4 -> 5 Modified Cache table to support persistent contentLength
-    // 5 -> 4 Removed Oma-Downoads table
-    // 5 -> 6 Add INDEX for cache table
-    // 6 -> 7 Change cache localPath from int to String
-    // 7 -> 8 Move cache to its own db
-    // 8 -> 9 Store both scheme and host when storing passwords
-    // 9 -> 10 Update httpauth table UNIQUE
-    // 10 -> 11 Drop cookies and cache now managed by the chromium stack,
-    //          and update the form data table to use the new format
-    //          implemented for b/5265606.
-
-    private static WebViewDatabaseClassic sInstance = null;
-    private static final Object sInstanceLock = new Object();
-
-    private static SQLiteDatabase sDatabase = null;
-
-    // synchronize locks
-    private final Object mPasswordLock = new Object();
-    private final Object mFormLock = new Object();
-    private final Object mHttpAuthLock = new Object();
-
-    private static final String mTableNames[] = {
-        "password", "formurl", "formdata", "httpauth"
-    };
-
-    // Table ids (they are index to mTableNames)
-    private static final int TABLE_PASSWORD_ID = 0;
-    private static final int TABLE_FORMURL_ID = 1;
-    private static final int TABLE_FORMDATA_ID = 2;
-    private static final int TABLE_HTTPAUTH_ID = 3;
-
-    // column id strings for "_id" which can be used by any table
-    private static final String ID_COL = "_id";
-
-    private static final String[] ID_PROJECTION = new String[] {
-        "_id"
-    };
-
-    // column id strings for "password" table
-    private static final String PASSWORD_HOST_COL = "host";
-    private static final String PASSWORD_USERNAME_COL = "username";
-    private static final String PASSWORD_PASSWORD_COL = "password";
-
-    // column id strings for "formurl" table
-    private static final String FORMURL_URL_COL = "url";
-
-    // column id strings for "formdata" table
-    private static final String FORMDATA_URLID_COL = "urlid";
-    private static final String FORMDATA_NAME_COL = "name";
-    private static final String FORMDATA_VALUE_COL = "value";
-
-    // column id strings for "httpauth" table
-    private static final String HTTPAUTH_HOST_COL = "host";
-    private static final String HTTPAUTH_REALM_COL = "realm";
-    private static final String HTTPAUTH_USERNAME_COL = "username";
-    private static final String HTTPAUTH_PASSWORD_COL = "password";
-
-    // Initially true until the background thread completes.
-    private boolean mInitialized = false;
-
-    private WebViewDatabaseClassic(final Context context) {
-        JniUtil.setContext(context);
-        new Thread() {
-            @Override
-            public void run() {
-                init(context);
-            }
-        }.start();
-
-        // Singleton only, use getInstance()
-    }
-
-    public static WebViewDatabaseClassic getInstance(Context context) {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                sInstance = new WebViewDatabaseClassic(context);
-            }
-            return sInstance;
-        }
-    }
-
-    private synchronized void init(Context context) {
-        if (mInitialized) {
-            return;
-        }
-
-        initDatabase(context);
-        // Before using the Chromium HTTP stack, we stored the WebKit cache in
-        // our own DB. Clean up the DB file if it's still around.
-        context.deleteDatabase(CACHE_DATABASE_FILE);
-
-        // Thread done, notify.
-        mInitialized = true;
-        notify();
-    }
-
-    private void initDatabase(Context context) {
-        try {
-            sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null);
-        } catch (SQLiteException e) {
-            // try again by deleting the old db and create a new one
-            if (context.deleteDatabase(DATABASE_FILE)) {
-                sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
-                        null);
-            }
-        }
-
-        // sDatabase should not be null,
-        // the only case is RequestAPI test has problem to create db
-        if (sDatabase == null) {
-            mInitialized = true;
-            notify();
-            return;
-        }
-
-        if (sDatabase.getVersion() != DATABASE_VERSION) {
-            sDatabase.beginTransactionNonExclusive();
-            try {
-                upgradeDatabase();
-                sDatabase.setTransactionSuccessful();
-            } finally {
-                sDatabase.endTransaction();
-            }
-        }
-    }
-
-    private static void upgradeDatabase() {
-        upgradeDatabaseToV10();
-        upgradeDatabaseFromV10ToV11();
-        // Add future database upgrade functions here, one version at a
-        // time.
-        sDatabase.setVersion(DATABASE_VERSION);
-    }
-
-    private static void upgradeDatabaseFromV10ToV11() {
-        int oldVersion = sDatabase.getVersion();
-
-        if (oldVersion >= 11) {
-            // Nothing to do.
-            return;
-        }
-
-        // Clear out old java stack cookies - this data is now stored in
-        // a separate database managed by the Chrome stack.
-        sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
-
-        // Likewise for the old cache table.
-        sDatabase.execSQL("DROP TABLE IF EXISTS cache");
-
-        // Update form autocomplete  URLs to match new ICS formatting.
-        Cursor c = sDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null,
-                null, null, null, null);
-        while (c.moveToNext()) {
-            String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL)));
-            String url = c.getString(c.getColumnIndex(FORMURL_URL_COL));
-            ContentValues cv = new ContentValues(1);
-            cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url));
-            sDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?",
-                    new String[] { urlId });
-        }
-        c.close();
-    }
-
-    private static void upgradeDatabaseToV10() {
-        int oldVersion = sDatabase.getVersion();
-
-        if (oldVersion >= 10) {
-            // Nothing to do.
-            return;
-        }
-
-        if (oldVersion != 0) {
-            Log.i(LOGTAG, "Upgrading database from version "
-                    + oldVersion + " to "
-                    + DATABASE_VERSION + ", which will destroy old data");
-        }
-
-        if (9 == oldVersion) {
-            sDatabase.execSQL("DROP TABLE IF EXISTS "
-                    + mTableNames[TABLE_HTTPAUTH_ID]);
-            sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
-                    + " (" + ID_COL + " INTEGER PRIMARY KEY, "
-                    + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
-                    + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
-                    + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
-                    + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
-                    + ") ON CONFLICT REPLACE);");
-            return;
-        }
-
-        sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
-        sDatabase.execSQL("DROP TABLE IF EXISTS cache");
-        sDatabase.execSQL("DROP TABLE IF EXISTS "
-                + mTableNames[TABLE_FORMURL_ID]);
-        sDatabase.execSQL("DROP TABLE IF EXISTS "
-                + mTableNames[TABLE_FORMDATA_ID]);
-        sDatabase.execSQL("DROP TABLE IF EXISTS "
-                + mTableNames[TABLE_HTTPAUTH_ID]);
-        sDatabase.execSQL("DROP TABLE IF EXISTS "
-                + mTableNames[TABLE_PASSWORD_ID]);
-
-        // formurl
-        sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID]
-                + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL
-                + " TEXT" + ");");
-
-        // formdata
-        sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID]
-                + " (" + ID_COL + " INTEGER PRIMARY KEY, "
-                + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL
-                + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE ("
-                + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", "
-                + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);");
-
-        // httpauth
-        sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
-                + " (" + ID_COL + " INTEGER PRIMARY KEY, "
-                + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
-                + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
-                + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
-                + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
-                + ") ON CONFLICT REPLACE);");
-        // passwords
-        sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID]
-                + " (" + ID_COL + " INTEGER PRIMARY KEY, "
-                + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL
-                + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE ("
-                + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL
-                + ") ON CONFLICT REPLACE);");
-    }
-
-    // Wait for the background initialization thread to complete and check the
-    // database creation status.
-    private boolean checkInitialized() {
-        synchronized (this) {
-            while (!mInitialized) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    Log.e(LOGTAG, "Caught exception while checking " +
-                                  "initialization");
-                    Log.e(LOGTAG, Log.getStackTraceString(e));
-                }
-            }
-        }
-        return sDatabase != null;
-    }
-
-    private boolean hasEntries(int tableId) {
-        if (!checkInitialized()) {
-            return false;
-        }
-
-        Cursor cursor = null;
-        boolean ret = false;
-        try {
-            cursor = sDatabase.query(mTableNames[tableId], ID_PROJECTION,
-                    null, null, null, null, null);
-            ret = cursor.moveToFirst() == true;
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "hasEntries", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return ret;
-    }
-
-    //
-    // password functions
-    //
-
-    /**
-     * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique.
-     *
-     * @param schemePlusHost The scheme and host for the password
-     * @param username The username for the password. If it is null, it means
-     *            password can't be saved.
-     * @param password The password
-     */
-    void setUsernamePassword(String schemePlusHost, String username,
-                String password) {
-        if (schemePlusHost == null || !checkInitialized()) {
-            return;
-        }
-
-        synchronized (mPasswordLock) {
-            final ContentValues c = new ContentValues();
-            c.put(PASSWORD_HOST_COL, schemePlusHost);
-            c.put(PASSWORD_USERNAME_COL, username);
-            c.put(PASSWORD_PASSWORD_COL, password);
-            sDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL,
-                    c);
-        }
-    }
-
-    /**
-     * Retrieve the username and password for a given host
-     *
-     * @param schemePlusHost The scheme and host which passwords applies to
-     * @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.
-     */
-    String[] getUsernamePassword(String schemePlusHost) {
-        if (schemePlusHost == null || !checkInitialized()) {
-            return null;
-        }
-
-        final String[] columns = new String[] {
-                PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL
-        };
-        final String selection = "(" + PASSWORD_HOST_COL + " == ?)";
-        synchronized (mPasswordLock) {
-            String[] ret = null;
-            Cursor cursor = null;
-            try {
-                cursor = sDatabase.query(mTableNames[TABLE_PASSWORD_ID],
-                        columns, selection, new String[] { schemePlusHost }, null,
-                        null, null);
-                if (cursor.moveToFirst()) {
-                    ret = new String[2];
-                    ret[0] = cursor.getString(
-                            cursor.getColumnIndex(PASSWORD_USERNAME_COL));
-                    ret[1] = cursor.getString(
-                            cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "getUsernamePassword", e);
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            return ret;
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#hasUsernamePassword
-     */
-    @Override
-    public boolean hasUsernamePassword() {
-        synchronized (mPasswordLock) {
-            return hasEntries(TABLE_PASSWORD_ID);
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#clearUsernamePassword
-     */
-    @Override
-    public void clearUsernamePassword() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        synchronized (mPasswordLock) {
-            sDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null);
-        }
-    }
-
-    //
-    // http authentication password functions
-    //
-
-    /**
-     * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL,
-     * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique.
-     *
-     * @param host The host for the password
-     * @param realm The realm for the password
-     * @param username The username for the password. If it is null, it means
-     *            password can't be saved.
-     * @param password The password
-     */
-    void setHttpAuthUsernamePassword(String host, String realm, String username,
-            String password) {
-        if (host == null || realm == null || !checkInitialized()) {
-            return;
-        }
-
-        synchronized (mHttpAuthLock) {
-            final ContentValues c = new ContentValues();
-            c.put(HTTPAUTH_HOST_COL, host);
-            c.put(HTTPAUTH_REALM_COL, realm);
-            c.put(HTTPAUTH_USERNAME_COL, username);
-            c.put(HTTPAUTH_PASSWORD_COL, password);
-            sDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL,
-                    c);
-        }
-    }
-
-    /**
-     * Retrieve the HTTP authentication username and password for a given
-     * host+realm pair
-     *
-     * @param host The host the password applies to
-     * @param realm The realm the password applies to
-     * @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.
-     */
-    String[] getHttpAuthUsernamePassword(String host, String realm) {
-        if (host == null || realm == null || !checkInitialized()){
-            return null;
-        }
-
-        final String[] columns = new String[] {
-                HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
-        };
-        final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND ("
-                + HTTPAUTH_REALM_COL + " == ?)";
-        synchronized (mHttpAuthLock) {
-            String[] ret = null;
-            Cursor cursor = null;
-            try {
-                cursor = sDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
-                        columns, selection, new String[] { host, realm }, null,
-                        null, null);
-                if (cursor.moveToFirst()) {
-                    ret = new String[2];
-                    ret[0] = cursor.getString(
-                            cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
-                    ret[1] = cursor.getString(
-                            cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            return ret;
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#hasHttpAuthUsernamePassword
-     */
-    @Override
-    public boolean hasHttpAuthUsernamePassword() {
-        synchronized (mHttpAuthLock) {
-            return hasEntries(TABLE_HTTPAUTH_ID);
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#clearHttpAuthUsernamePassword
-     */
-    @Override
-    public void clearHttpAuthUsernamePassword() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        synchronized (mHttpAuthLock) {
-            sDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null);
-        }
-    }
-
-    //
-    // form data functions
-    //
-
-    /**
-     * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL,
-     * FORMDATA_VALUE_COL) is unique
-     *
-     * @param url The url of the site
-     * @param formdata The form data in HashMap
-     */
-    void setFormData(String url, HashMap<String, String> formdata) {
-        if (url == null || formdata == null || !checkInitialized()) {
-            return;
-        }
-
-        final String selection = "(" + FORMURL_URL_COL + " == ?)";
-        synchronized (mFormLock) {
-            long urlid = -1;
-            Cursor cursor = null;
-            try {
-                cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID],
-                        ID_PROJECTION, selection, new String[] { url }, null, null,
-                        null);
-                if (cursor.moveToFirst()) {
-                    urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
-                } else {
-                    ContentValues c = new ContentValues();
-                    c.put(FORMURL_URL_COL, url);
-                    urlid = sDatabase.insert(
-                            mTableNames[TABLE_FORMURL_ID], null, c);
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "setFormData", e);
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            if (urlid >= 0) {
-                Set<Entry<String, String>> set = formdata.entrySet();
-                Iterator<Entry<String, String>> iter = set.iterator();
-                ContentValues map = new ContentValues();
-                map.put(FORMDATA_URLID_COL, urlid);
-                while (iter.hasNext()) {
-                    Entry<String, String> entry = iter.next();
-                    map.put(FORMDATA_NAME_COL, entry.getKey());
-                    map.put(FORMDATA_VALUE_COL, entry.getValue());
-                    sDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map);
-                }
-            }
-        }
-    }
-
-    /**
-     * Get all the values for a form entry with "name" in a given site
-     *
-     * @param url The url of the site
-     * @param name The name of the form entry
-     * @return A list of values. Return empty list if nothing is found.
-     */
-    ArrayList<String> getFormData(String url, String name) {
-        ArrayList<String> values = new ArrayList<String>();
-        if (url == null || name == null || !checkInitialized()) {
-            return values;
-        }
-
-        final String urlSelection = "(" + FORMURL_URL_COL + " == ?)";
-        final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND ("
-                + FORMDATA_NAME_COL + " == ?)";
-        synchronized (mFormLock) {
-            Cursor cursor = null;
-            try {
-                cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID],
-                        ID_PROJECTION, urlSelection, new String[] { url }, null,
-                        null, null);
-                while (cursor.moveToNext()) {
-                    long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
-                    Cursor dataCursor = null;
-                    try {
-                        dataCursor = sDatabase.query(
-                                mTableNames[TABLE_FORMDATA_ID],
-                                new String[] { ID_COL, FORMDATA_VALUE_COL },
-                                dataSelection,
-                                new String[] { Long.toString(urlid), name },
-                                null, null, null);
-                        if (dataCursor.moveToFirst()) {
-                            int valueCol = dataCursor.getColumnIndex(
-                                    FORMDATA_VALUE_COL);
-                            do {
-                                values.add(dataCursor.getString(valueCol));
-                            } while (dataCursor.moveToNext());
-                        }
-                    } catch (IllegalStateException e) {
-                        Log.e(LOGTAG, "getFormData dataCursor", e);
-                    } finally {
-                        if (dataCursor != null) dataCursor.close();
-                    }
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "getFormData cursor", e);
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            return values;
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#hasFormData
-     */
-    @Override
-    public boolean hasFormData() {
-        synchronized (mFormLock) {
-            return hasEntries(TABLE_FORMURL_ID);
-        }
-    }
-
-    /**
-     * @see WebViewDatabase#clearFormData
-     */
-    @Override
-    public void clearFormData() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        synchronized (mFormLock) {
-            sDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null);
-            sDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null);
-        }
-    }
-}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 16daba0..b9131bf 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -29,13 +29,6 @@
  */
 public final class WebViewFactory {
 
-    private static final String FORCE_PROVIDER_PROPERTY = "webview.force_provider";
-    private static final String FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM = "chromium";
-    private static final String FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC = "classic";
-
-    // Default Provider factory class name.
-    // TODO: When the Chromium powered WebView is ready, it should be the default factory class.
-    private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
     private static final String CHROMIUM_WEBVIEW_FACTORY =
             "com.android.webview.chromium.WebViewChromiumFactoryProvider";
 
@@ -72,7 +65,7 @@
     /** @hide */
     public static boolean useExperimentalWebView() {
         // TODO: Remove callers of this method then remove it.
-        return isChromiumWebViewEnabled();
+        return true;
     }
 
     /** @hide */
@@ -84,7 +77,7 @@
     static WebViewFactoryProvider getProvider() {
         synchronized (sProviderLock) {
             // For now the main purpose of this function (and the factory abstraction) is to keep
-            // us honest and minimize usage of WebViewClassic internals when binding the proxy.
+            // us honest and minimize usage of WebView internals when binding the proxy.
             if (sProviderInstance != null) return sProviderInstance;
 
             Class<WebViewFactoryProvider> providerClass;
@@ -118,25 +111,7 @@
         }
     }
 
-    // We allow a system property to specify that we should use the experimental Chromium powered
-    // WebView. This enables us to switch between implementations at runtime.
-    private static boolean isChromiumWebViewEnabled() {
-        String forceProviderName = SystemProperties.get(FORCE_PROVIDER_PROPERTY);
-        if (forceProviderName.isEmpty()) return true;
-
-        Log.i(LOGTAG, String.format("Provider overridden by property: %s=%s",
-                FORCE_PROVIDER_PROPERTY, forceProviderName));
-        if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM)) return true;
-        if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC)) return false;
-        Log.e(LOGTAG, String.format("Unrecognized provider: %s", forceProviderName));
-        return true;
-    }
-
     private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
-        if (isChromiumWebViewEnabled()) {
-            return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
-        } else  {
-            return (Class<WebViewFactoryProvider>) Class.forName(DEFAULT_WEBVIEW_FACTORY);
-        }
+        return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
     }
 }
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
deleted file mode 100644
index f64547f..0000000
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ /dev/null
@@ -1,1293 +0,0 @@
-/*
- * 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.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-/**
- * Perform asynchronous dispatch of input events in a {@link WebView}.
- *
- * This dispatcher is shared by the UI thread ({@link WebViewClassic}) and web kit
- * thread ({@link WebViewCore}).  The UI thread enqueues events for
- * processing, waits for the web kit thread to handle them, and then performs
- * additional processing depending on the outcome.
- *
- * How it works:
- *
- * 1. The web view thread receives an input event from the input system on the UI
- * thread in its {@link WebViewClassic#onTouchEvent} handler.  It sends the input event
- * to the dispatcher, then immediately returns true to the input system to indicate that
- * it will handle the event.
- *
- * 2. The web kit thread is notified that an event has been enqueued.  Meanwhile additional
- * events may be enqueued from the UI thread.  In some cases, the dispatcher may decide to
- * coalesce motion events into larger batches or to cancel events that have been
- * sitting in the queue for too long.
- *
- * 3. The web kit thread wakes up and handles all input events that are waiting for it.
- * After processing each input event, it informs the dispatcher whether the web application
- * has decided to handle the event itself and to prevent default event handling.
- *
- * 4. If web kit indicates that it wants to prevent default event handling, then web kit
- * consumes the remainder of the gesture and web view receives a cancel event if
- * needed.  Otherwise, the web view handles the gesture on the UI thread normally.
- *
- * 5. If the web kit thread takes too long to handle an input event, then it loses the
- * right to handle it.  The dispatcher synthesizes a cancellation event for web kit and
- * then tells the web view on the UI thread to handle the event that timed out along
- * with the rest of the gesture.
- *
- * One thing to keep in mind about the dispatcher is that what goes into the dispatcher
- * is not necessarily what the web kit or UI thread will see.  As mentioned above, the
- * dispatcher may tweak the input event stream to improve responsiveness.  Both web view and
- * web kit are guaranteed to perceive a consistent stream of input events but
- * they might not always see the same events (especially if one decides
- * to prevent the other from handling a particular gesture).
- *
- * This implementation very deliberately does not refer to the {@link WebViewClassic}
- * or {@link WebViewCore} classes, preferring to communicate with them only via
- * interfaces to avoid unintentional coupling to their implementation details.
- *
- * Currently, the input dispatcher only handles pointer events (includes touch,
- * hover and scroll events).  In principle, it could be extended to handle trackball
- * and key events if needed.
- *
- * @hide
- */
-final class WebViewInputDispatcher {
-    private static final String TAG = "WebViewInputDispatcher";
-    private static final boolean DEBUG = false;
-    // This enables batching of MotionEvents. It will combine multiple MotionEvents
-    // together into a single MotionEvent if more events come in while we are
-    // still waiting on the processing of a previous event.
-    // If this is set to false, we will instead opt to drop ACTION_MOVE
-    // events we cannot keep up with.
-    // TODO: If batching proves to be working well, remove this
-    private static final boolean ENABLE_EVENT_BATCHING = true;
-
-    private final Object mLock = new Object();
-
-    // Pool of queued input events.  (guarded by mLock)
-    private static final int MAX_DISPATCH_EVENT_POOL_SIZE = 10;
-    private DispatchEvent mDispatchEventPool;
-    private int mDispatchEventPoolSize;
-
-    // Posted state, tracks events posted to the dispatcher.  (guarded by mLock)
-    private final TouchStream mPostTouchStream = new TouchStream();
-    private boolean mPostSendTouchEventsToWebKit;
-    private boolean mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
-    private boolean mPostLongPressScheduled;
-    private boolean mPostClickScheduled;
-    private boolean mPostShowTapHighlightScheduled;
-    private boolean mPostHideTapHighlightScheduled;
-    private int mPostLastWebKitXOffset;
-    private int mPostLastWebKitYOffset;
-    private float mPostLastWebKitScale;
-
-    // State for event tracking (click, longpress, double tap, etc..)
-    private boolean mIsDoubleTapCandidate;
-    private boolean mIsTapCandidate;
-    private float mInitialDownX;
-    private float mInitialDownY;
-    private float mTouchSlopSquared;
-    private float mDoubleTapSlopSquared;
-
-    // Web kit state, tracks events observed by web kit.  (guarded by mLock)
-    private final DispatchEventQueue mWebKitDispatchEventQueue = new DispatchEventQueue();
-    private final TouchStream mWebKitTouchStream = new TouchStream();
-    private final WebKitCallbacks mWebKitCallbacks;
-    private final WebKitHandler mWebKitHandler;
-    private boolean mWebKitDispatchScheduled;
-    private boolean mWebKitTimeoutScheduled;
-    private long mWebKitTimeoutTime;
-
-    // UI state, tracks events observed by the UI.  (guarded by mLock)
-    private final DispatchEventQueue mUiDispatchEventQueue = new DispatchEventQueue();
-    private final TouchStream mUiTouchStream = new TouchStream();
-    private final UiCallbacks mUiCallbacks;
-    private final UiHandler mUiHandler;
-    private boolean mUiDispatchScheduled;
-
-    // Give up on web kit handling of input events when this timeout expires.
-    private static final long WEBKIT_TIMEOUT_MILLIS = 200;
-    private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
-    private static final int LONG_PRESS_TIMEOUT =
-            ViewConfiguration.getLongPressTimeout() + TAP_TIMEOUT;
-    private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
-    private static final int PRESSED_STATE_DURATION = ViewConfiguration.getPressedStateDuration();
-
-    /**
-     * Event type: Indicates a touch event type.
-     *
-     * This event is delivered together with a {@link MotionEvent} with one of the
-     * following actions: {@link MotionEvent#ACTION_DOWN}, {@link MotionEvent#ACTION_MOVE},
-     * {@link MotionEvent#ACTION_UP}, {@link MotionEvent#ACTION_POINTER_DOWN},
-     * {@link MotionEvent#ACTION_POINTER_UP}, {@link MotionEvent#ACTION_CANCEL}.
-     */
-    public static final int EVENT_TYPE_TOUCH = 0;
-
-    /**
-     * Event type: Indicates a hover event type.
-     *
-     * This event is delivered together with a {@link MotionEvent} with one of the
-     * following actions: {@link MotionEvent#ACTION_HOVER_ENTER},
-     * {@link MotionEvent#ACTION_HOVER_MOVE}, {@link MotionEvent#ACTION_HOVER_MOVE}.
-     */
-    public static final int EVENT_TYPE_HOVER = 1;
-
-    /**
-     * Event type: Indicates a scroll event type.
-     *
-     * This event is delivered together with a {@link MotionEvent} with action
-     * {@link MotionEvent#ACTION_SCROLL}.
-     */
-    public static final int EVENT_TYPE_SCROLL = 2;
-
-    /**
-     * Event type: Indicates a long-press event type.
-     *
-     * This event is delivered in the middle of a sequence of {@link #EVENT_TYPE_TOUCH} events.
-     * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_MOVE}
-     * that indicates the current touch coordinates of the long-press.
-     *
-     * This event is sent when the current touch gesture has been held longer than
-     * the long-press interval.
-     */
-    public static final int EVENT_TYPE_LONG_PRESS = 3;
-
-    /**
-     * Event type: Indicates a click event type.
-     *
-     * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that
-     * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}.
-     * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP}
-     * that indicates the location of the click.
-     *
-     * This event is sent shortly after the end of a touch after the double-tap
-     * interval has expired to indicate a click.
-     */
-    public static final int EVENT_TYPE_CLICK = 4;
-
-    /**
-     * Event type: Indicates a double-tap event type.
-     *
-     * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that
-     * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}.
-     * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP}
-     * that indicates the location of the double-tap.
-     *
-     * This event is sent immediately after a sequence of two touches separated
-     * in time by no more than the double-tap interval and separated in space
-     * by no more than the double-tap slop.
-     */
-    public static final int EVENT_TYPE_DOUBLE_TAP = 5;
-
-    /**
-     * Event type: Indicates that a hit test should be performed
-     */
-    public static final int EVENT_TYPE_HIT_TEST = 6;
-
-    /**
-     * Flag: This event is private to this queue.  Do not forward it.
-     */
-    public static final int FLAG_PRIVATE = 1 << 0;
-
-    /**
-     * Flag: This event is currently being processed by web kit.
-     * If a timeout occurs, make a copy of it before forwarding the event to another queue.
-     */
-    public static final int FLAG_WEBKIT_IN_PROGRESS = 1 << 1;
-
-    /**
-     * Flag: A timeout occurred while waiting for web kit to process this input event.
-     */
-    public static final int FLAG_WEBKIT_TIMEOUT = 1 << 2;
-
-    /**
-     * Flag: Indicates that the event was transformed for delivery to web kit.
-     * The event must be transformed back before being delivered to the UI.
-     */
-    public static final int FLAG_WEBKIT_TRANSFORMED_EVENT = 1 << 3;
-
-    public WebViewInputDispatcher(UiCallbacks uiCallbacks, WebKitCallbacks webKitCallbacks) {
-        this.mUiCallbacks = uiCallbacks;
-        mUiHandler = new UiHandler(uiCallbacks.getUiLooper());
-
-        this.mWebKitCallbacks = webKitCallbacks;
-        mWebKitHandler = new WebKitHandler(webKitCallbacks.getWebKitLooper());
-
-        ViewConfiguration config = ViewConfiguration.get(mUiCallbacks.getContext());
-        mDoubleTapSlopSquared = config.getScaledDoubleTapSlop();
-        mDoubleTapSlopSquared = (mDoubleTapSlopSquared * mDoubleTapSlopSquared);
-        mTouchSlopSquared = config.getScaledTouchSlop();
-        mTouchSlopSquared = (mTouchSlopSquared * mTouchSlopSquared);
-    }
-
-    /**
-     * Sets whether web kit wants to receive touch events.
-     *
-     * @param enable True to enable dispatching of touch events to web kit, otherwise
-     * web kit will be skipped.
-     */
-    public void setWebKitWantsTouchEvents(boolean enable) {
-        if (DEBUG) {
-            Log.d(TAG, "webkitWantsTouchEvents: " + enable);
-        }
-        synchronized (mLock) {
-            if (mPostSendTouchEventsToWebKit != enable) {
-                if (!enable) {
-                    enqueueWebKitCancelTouchEventIfNeededLocked();
-                }
-                mPostSendTouchEventsToWebKit = enable;
-            }
-        }
-    }
-
-    /**
-     * Posts a pointer event to the dispatch queue.
-     *
-     * @param event The event to post.
-     * @param webKitXOffset X offset to apply to events before dispatching them to web kit.
-     * @param webKitYOffset Y offset to apply to events before dispatching them to web kit.
-     * @param webKitScale The scale factor to apply to translated events before dispatching
-     * them to web kit.
-     * @return True if the dispatcher will handle the event, false if the event is unsupported.
-     */
-    public boolean postPointerEvent(MotionEvent event,
-            int webKitXOffset, int webKitYOffset, float webKitScale) {
-        if (event == null) {
-            throw new IllegalArgumentException("event cannot be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "postPointerEvent: " + event);
-        }
-
-        final int action = event.getActionMasked();
-        final int eventType;
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_MOVE:
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_POINTER_DOWN:
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_CANCEL:
-                eventType = EVENT_TYPE_TOUCH;
-                break;
-            case MotionEvent.ACTION_SCROLL:
-                eventType = EVENT_TYPE_SCROLL;
-                break;
-            case MotionEvent.ACTION_HOVER_ENTER:
-            case MotionEvent.ACTION_HOVER_MOVE:
-            case MotionEvent.ACTION_HOVER_EXIT:
-                eventType = EVENT_TYPE_HOVER;
-                break;
-            default:
-                return false; // currently unsupported event type
-        }
-
-        synchronized (mLock) {
-            // Ensure that the event is consistent and should be delivered.
-            MotionEvent eventToEnqueue = event;
-            if (eventType == EVENT_TYPE_TOUCH) {
-                eventToEnqueue = mPostTouchStream.update(event);
-                if (eventToEnqueue == null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "postPointerEvent: dropped event " + event);
-                    }
-                    unscheduleLongPressLocked();
-                    unscheduleClickLocked();
-                    hideTapCandidateLocked();
-                    return false;
-                }
-
-                if (action == MotionEvent.ACTION_DOWN && mPostSendTouchEventsToWebKit) {
-                    if (mUiCallbacks.shouldInterceptTouchEvent(eventToEnqueue)) {
-                        mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
-                    } else if (mPostDoNotSendTouchEventsToWebKitUntilNextGesture) {
-                        // Recover from a previous web kit timeout.
-                        mPostDoNotSendTouchEventsToWebKitUntilNextGesture = false;
-                    }
-                }
-            }
-
-            // Copy the event because we need to retain ownership.
-            if (eventToEnqueue == event) {
-                eventToEnqueue = event.copy();
-            }
-
-            DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, eventType, 0,
-                    webKitXOffset, webKitYOffset, webKitScale);
-            updateStateTrackersLocked(d, event);
-            enqueueEventLocked(d);
-        }
-        return true;
-    }
-
-    private void scheduleLongPressLocked() {
-        unscheduleLongPressLocked();
-        mPostLongPressScheduled = true;
-        mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_LONG_PRESS,
-                LONG_PRESS_TIMEOUT);
-    }
-
-    private void unscheduleLongPressLocked() {
-        if (mPostLongPressScheduled) {
-            mPostLongPressScheduled = false;
-            mUiHandler.removeMessages(UiHandler.MSG_LONG_PRESS);
-        }
-    }
-
-    private void postLongPress() {
-        synchronized (mLock) {
-            if (!mPostLongPressScheduled) {
-                return;
-            }
-            mPostLongPressScheduled = false;
-
-            MotionEvent event = mPostTouchStream.getLastEvent();
-            if (event == null) {
-                return;
-            }
-
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                case MotionEvent.ACTION_MOVE:
-                case MotionEvent.ACTION_POINTER_DOWN:
-                case MotionEvent.ACTION_POINTER_UP:
-                    break;
-                default:
-                    return;
-            }
-
-            MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
-            eventToEnqueue.setAction(MotionEvent.ACTION_MOVE);
-            DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_LONG_PRESS, 0,
-                    mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
-            enqueueEventLocked(d);
-        }
-    }
-
-    private void hideTapCandidateLocked() {
-        unscheduleHideTapHighlightLocked();
-        unscheduleShowTapHighlightLocked();
-        mUiCallbacks.showTapHighlight(false);
-    }
-
-    private void showTapCandidateLocked() {
-        unscheduleHideTapHighlightLocked();
-        unscheduleShowTapHighlightLocked();
-        mUiCallbacks.showTapHighlight(true);
-    }
-
-    private void scheduleShowTapHighlightLocked() {
-        unscheduleShowTapHighlightLocked();
-        mPostShowTapHighlightScheduled = true;
-        mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_SHOW_TAP_HIGHLIGHT,
-                TAP_TIMEOUT);
-    }
-
-    private void unscheduleShowTapHighlightLocked() {
-        if (mPostShowTapHighlightScheduled) {
-            mPostShowTapHighlightScheduled = false;
-            mUiHandler.removeMessages(UiHandler.MSG_SHOW_TAP_HIGHLIGHT);
-        }
-    }
-
-    private void scheduleHideTapHighlightLocked() {
-        unscheduleHideTapHighlightLocked();
-        mPostHideTapHighlightScheduled = true;
-        mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_HIDE_TAP_HIGHLIGHT,
-                PRESSED_STATE_DURATION);
-    }
-
-    private void unscheduleHideTapHighlightLocked() {
-        if (mPostHideTapHighlightScheduled) {
-            mPostHideTapHighlightScheduled = false;
-            mUiHandler.removeMessages(UiHandler.MSG_HIDE_TAP_HIGHLIGHT);
-        }
-    }
-
-    private void postShowTapHighlight(boolean show) {
-        synchronized (mLock) {
-            if (show) {
-                if (!mPostShowTapHighlightScheduled) {
-                    return;
-                }
-                mPostShowTapHighlightScheduled = false;
-            } else {
-                if (!mPostHideTapHighlightScheduled) {
-                    return;
-                }
-                mPostHideTapHighlightScheduled = false;
-            }
-            mUiCallbacks.showTapHighlight(show);
-        }
-    }
-
-    private void scheduleClickLocked() {
-        unscheduleClickLocked();
-        mPostClickScheduled = true;
-        mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_CLICK, DOUBLE_TAP_TIMEOUT);
-    }
-
-    private void unscheduleClickLocked() {
-        if (mPostClickScheduled) {
-            mPostClickScheduled = false;
-            mUiHandler.removeMessages(UiHandler.MSG_CLICK);
-        }
-    }
-
-    private void postClick() {
-        synchronized (mLock) {
-            if (!mPostClickScheduled) {
-                return;
-            }
-            mPostClickScheduled = false;
-
-            MotionEvent event = mPostTouchStream.getLastEvent();
-            if (event == null || event.getAction() != MotionEvent.ACTION_UP) {
-                return;
-            }
-
-            showTapCandidateLocked();
-            MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
-            DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_CLICK, 0,
-                    mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
-            enqueueEventLocked(d);
-        }
-    }
-
-    private void checkForDoubleTapOnDownLocked(MotionEvent event) {
-        mIsDoubleTapCandidate = false;
-        if (!mPostClickScheduled) {
-            return;
-        }
-        int deltaX = (int) mInitialDownX - (int) event.getX();
-        int deltaY = (int) mInitialDownY - (int) event.getY();
-        if ((deltaX * deltaX + deltaY * deltaY) < mDoubleTapSlopSquared) {
-            unscheduleClickLocked();
-            mIsDoubleTapCandidate = true;
-        }
-    }
-
-    private boolean isClickCandidateLocked(MotionEvent event) {
-        if (event == null
-                || event.getActionMasked() != MotionEvent.ACTION_UP
-                || !mIsTapCandidate) {
-            return false;
-        }
-        long downDuration = event.getEventTime() - event.getDownTime();
-        return downDuration < LONG_PRESS_TIMEOUT;
-    }
-
-    private void enqueueDoubleTapLocked(MotionEvent event) {
-        MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
-        DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_DOUBLE_TAP, 0,
-                mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
-        enqueueEventLocked(d);
-    }
-
-    private void enqueueHitTestLocked(MotionEvent event) {
-        mUiCallbacks.clearPreviousHitTest();
-        MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
-        DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0,
-                mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
-        enqueueEventLocked(d);
-    }
-
-    private void checkForSlopLocked(MotionEvent event) {
-        if (!mIsTapCandidate) {
-            return;
-        }
-        int deltaX = (int) mInitialDownX - (int) event.getX();
-        int deltaY = (int) mInitialDownY - (int) event.getY();
-        if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquared) {
-            unscheduleLongPressLocked();
-            mIsTapCandidate = false;
-            hideTapCandidateLocked();
-        }
-    }
-
-    private void updateStateTrackersLocked(DispatchEvent d, MotionEvent event) {
-        mPostLastWebKitXOffset = d.mWebKitXOffset;
-        mPostLastWebKitYOffset = d.mWebKitYOffset;
-        mPostLastWebKitScale = d.mWebKitScale;
-        int action = event != null ? event.getAction() : MotionEvent.ACTION_CANCEL;
-        if (d.mEventType != EVENT_TYPE_TOUCH) {
-            return;
-        }
-
-        if (action == MotionEvent.ACTION_CANCEL
-                || event.getPointerCount() > 1) {
-            unscheduleLongPressLocked();
-            unscheduleClickLocked();
-            hideTapCandidateLocked();
-            mIsDoubleTapCandidate = false;
-            mIsTapCandidate = false;
-            hideTapCandidateLocked();
-        } else if (action == MotionEvent.ACTION_DOWN) {
-            checkForDoubleTapOnDownLocked(event);
-            scheduleLongPressLocked();
-            mIsTapCandidate = true;
-            mInitialDownX = event.getX();
-            mInitialDownY = event.getY();
-            enqueueHitTestLocked(event);
-            if (mIsDoubleTapCandidate) {
-                hideTapCandidateLocked();
-            } else {
-                scheduleShowTapHighlightLocked();
-            }
-        } else if (action == MotionEvent.ACTION_UP) {
-            unscheduleLongPressLocked();
-            if (isClickCandidateLocked(event)) {
-                if (mIsDoubleTapCandidate) {
-                    hideTapCandidateLocked();
-                    enqueueDoubleTapLocked(event);
-                } else {
-                    scheduleClickLocked();
-                }
-            } else {
-                hideTapCandidateLocked();
-            }
-        } else if (action == MotionEvent.ACTION_MOVE) {
-            checkForSlopLocked(event);
-        }
-    }
-
-    /**
-     * Dispatches pending web kit events.
-     * Must only be called from the web kit thread.
-     *
-     * This method may be used to flush the queue of pending input events
-     * immediately.  This method may help to reduce input dispatch latency
-     * if called before certain expensive operations such as drawing.
-     */
-    public void dispatchWebKitEvents() {
-        dispatchWebKitEvents(false);
-    }
-
-    private void dispatchWebKitEvents(boolean calledFromHandler) {
-        for (;;) {
-            // Get the next event, but leave it in the queue so we can move it to the UI
-            // queue if a timeout occurs.
-            DispatchEvent d;
-            MotionEvent event;
-            final int eventType;
-            int flags;
-            synchronized (mLock) {
-                if (!ENABLE_EVENT_BATCHING) {
-                    drainStaleWebKitEventsLocked();
-                }
-                d = mWebKitDispatchEventQueue.mHead;
-                if (d == null) {
-                    if (mWebKitDispatchScheduled) {
-                        mWebKitDispatchScheduled = false;
-                        if (!calledFromHandler) {
-                            mWebKitHandler.removeMessages(
-                                    WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS);
-                        }
-                    }
-                    return;
-                }
-
-                event = d.mEvent;
-                if (event != null) {
-                    event.offsetLocation(d.mWebKitXOffset, d.mWebKitYOffset);
-                    event.scale(d.mWebKitScale);
-                    d.mFlags |= FLAG_WEBKIT_TRANSFORMED_EVENT;
-                }
-
-                eventType = d.mEventType;
-                if (eventType == EVENT_TYPE_TOUCH) {
-                    event = mWebKitTouchStream.update(event);
-                    if (DEBUG && event == null && d.mEvent != null) {
-                        Log.d(TAG, "dispatchWebKitEvents: dropped event " + d.mEvent);
-                    }
-                }
-
-                d.mFlags |= FLAG_WEBKIT_IN_PROGRESS;
-                flags = d.mFlags;
-            }
-
-            // Handle the event.
-            final boolean preventDefault;
-            if (event == null) {
-                preventDefault = false;
-            } else {
-                preventDefault = dispatchWebKitEvent(event, eventType, flags);
-            }
-
-            synchronized (mLock) {
-                flags = d.mFlags;
-                d.mFlags = flags & ~FLAG_WEBKIT_IN_PROGRESS;
-                boolean recycleEvent = event != d.mEvent;
-
-                if ((flags & FLAG_WEBKIT_TIMEOUT) != 0) {
-                    // A timeout occurred!
-                    recycleDispatchEventLocked(d);
-                } else {
-                    // Web kit finished in a timely manner.  Dequeue the event.
-                    assert mWebKitDispatchEventQueue.mHead == d;
-                    mWebKitDispatchEventQueue.dequeue();
-
-                    updateWebKitTimeoutLocked();
-
-                    if ((flags & FLAG_PRIVATE) != 0) {
-                        // Event was intended for web kit only.  All done.
-                        recycleDispatchEventLocked(d);
-                    } else if (preventDefault) {
-                        // Web kit has decided to consume the event!
-                        if (d.mEventType == EVENT_TYPE_TOUCH) {
-                            enqueueUiCancelTouchEventIfNeededLocked();
-                            unscheduleLongPressLocked();
-                        }
-                    } else {
-                        // Web kit is being friendly.  Pass the event to the UI.
-                        enqueueUiEventUnbatchedLocked(d);
-                    }
-                }
-
-                if (event != null && recycleEvent) {
-                    event.recycle();
-                }
-
-                if (eventType == EVENT_TYPE_CLICK) {
-                    scheduleHideTapHighlightLocked();
-                }
-            }
-        }
-    }
-
-    // Runs on web kit thread.
-    private boolean dispatchWebKitEvent(MotionEvent event, int eventType, int flags) {
-        if (DEBUG) {
-            Log.d(TAG, "dispatchWebKitEvent: event=" + event
-                    + ", eventType=" + eventType + ", flags=" + flags);
-        }
-        boolean preventDefault = mWebKitCallbacks.dispatchWebKitEvent(
-                this, event, eventType, flags);
-        if (DEBUG) {
-            Log.d(TAG, "dispatchWebKitEvent: preventDefault=" + preventDefault);
-        }
-        return preventDefault;
-    }
-
-    private boolean isMoveEventLocked(DispatchEvent d) {
-        return d.mEvent != null
-                && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE;
-    }
-
-    private void drainStaleWebKitEventsLocked() {
-        DispatchEvent d = mWebKitDispatchEventQueue.mHead;
-        while (d != null && d.mNext != null
-                && isMoveEventLocked(d)
-                && isMoveEventLocked(d.mNext)) {
-            DispatchEvent next = d.mNext;
-            skipWebKitEventLocked(d);
-            d = next;
-        }
-        mWebKitDispatchEventQueue.mHead = d;
-    }
-
-    // Called by WebKit when it doesn't care about the rest of the touch stream
-    public void skipWebkitForRemainingTouchStream() {
-        // Just treat this like a timeout
-        handleWebKitTimeout();
-    }
-
-    // Runs on UI thread in response to the web kit thread appearing to be unresponsive.
-    private void handleWebKitTimeout() {
-        synchronized (mLock) {
-            if (!mWebKitTimeoutScheduled) {
-                return;
-            }
-            mWebKitTimeoutScheduled = false;
-
-            if (DEBUG) {
-                Log.d(TAG, "handleWebKitTimeout: timeout occurred!");
-            }
-
-            // Drain the web kit event queue.
-            DispatchEvent d = mWebKitDispatchEventQueue.dequeueList();
-
-            // If web kit was processing an event (must be at the head of the list because
-            // it can only do one at a time), then clone it or ignore it.
-            if ((d.mFlags & FLAG_WEBKIT_IN_PROGRESS) != 0) {
-                d.mFlags |= FLAG_WEBKIT_TIMEOUT;
-                if ((d.mFlags & FLAG_PRIVATE) != 0) {
-                    d = d.mNext; // the event is private to web kit, ignore it
-                } else {
-                    d = copyDispatchEventLocked(d);
-                    d.mFlags &= ~FLAG_WEBKIT_IN_PROGRESS;
-                }
-            }
-
-            // Enqueue all non-private events for handling by the UI thread.
-            while (d != null) {
-                DispatchEvent next = d.mNext;
-                skipWebKitEventLocked(d);
-                d = next;
-            }
-
-            // Tell web kit to cancel all pending touches.
-            // This also prevents us from sending web kit any more touches until the
-            // next gesture begins.  (As required to ensure touch event stream consistency.)
-            enqueueWebKitCancelTouchEventIfNeededLocked();
-        }
-    }
-
-    private void skipWebKitEventLocked(DispatchEvent d) {
-        d.mNext = null;
-        if ((d.mFlags & FLAG_PRIVATE) != 0) {
-            recycleDispatchEventLocked(d);
-        } else {
-            d.mFlags |= FLAG_WEBKIT_TIMEOUT;
-            enqueueUiEventUnbatchedLocked(d);
-        }
-    }
-
-    /**
-     * Dispatches pending UI events.
-     * Must only be called from the UI thread.
-     *
-     * This method may be used to flush the queue of pending input events
-     * immediately.  This method may help to reduce input dispatch latency
-     * if called before certain expensive operations such as drawing.
-     */
-    public void dispatchUiEvents() {
-        dispatchUiEvents(false);
-    }
-
-    private void dispatchUiEvents(boolean calledFromHandler) {
-        for (;;) {
-            MotionEvent event;
-            final int eventType;
-            final int flags;
-            synchronized (mLock) {
-                DispatchEvent d = mUiDispatchEventQueue.dequeue();
-                if (d == null) {
-                    if (mUiDispatchScheduled) {
-                        mUiDispatchScheduled = false;
-                        if (!calledFromHandler) {
-                            mUiHandler.removeMessages(UiHandler.MSG_DISPATCH_UI_EVENTS);
-                        }
-                    }
-                    return;
-                }
-
-                event = d.mEvent;
-                if (event != null && (d.mFlags & FLAG_WEBKIT_TRANSFORMED_EVENT) != 0) {
-                    event.scale(1.0f / d.mWebKitScale);
-                    event.offsetLocation(-d.mWebKitXOffset, -d.mWebKitYOffset);
-                    d.mFlags &= ~FLAG_WEBKIT_TRANSFORMED_EVENT;
-                }
-
-                eventType = d.mEventType;
-                if (eventType == EVENT_TYPE_TOUCH) {
-                    event = mUiTouchStream.update(event);
-                    if (DEBUG && event == null && d.mEvent != null) {
-                        Log.d(TAG, "dispatchUiEvents: dropped event " + d.mEvent);
-                    }
-                }
-
-                flags = d.mFlags;
-
-                if (event == d.mEvent) {
-                    d.mEvent = null; // retain ownership of event, don't recycle it yet
-                }
-                recycleDispatchEventLocked(d);
-
-                if (eventType == EVENT_TYPE_CLICK) {
-                    scheduleHideTapHighlightLocked();
-                }
-            }
-
-            // Handle the event.
-            if (event != null) {
-                dispatchUiEvent(event, eventType, flags);
-                event.recycle();
-            }
-        }
-    }
-
-    // Runs on UI thread.
-    private void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
-        if (DEBUG) {
-            Log.d(TAG, "dispatchUiEvent: event=" + event
-                    + ", eventType=" + eventType + ", flags=" + flags);
-        }
-        mUiCallbacks.dispatchUiEvent(event, eventType, flags);
-    }
-
-    private void enqueueEventLocked(DispatchEvent d) {
-        if (!shouldSkipWebKit(d)) {
-            enqueueWebKitEventLocked(d);
-        } else {
-            enqueueUiEventLocked(d);
-        }
-    }
-
-    private boolean shouldSkipWebKit(DispatchEvent d) {
-        switch (d.mEventType) {
-            case EVENT_TYPE_CLICK:
-            case EVENT_TYPE_HOVER:
-            case EVENT_TYPE_SCROLL:
-            case EVENT_TYPE_HIT_TEST:
-                return false;
-            case EVENT_TYPE_TOUCH:
-                // TODO: This should be cleaned up. We now have WebViewInputDispatcher
-                // and WebViewClassic both checking for slop and doing their own
-                // thing - they should be consolidated. And by consolidated, I mean
-                // WebViewClassic's version should just be deleted.
-                // The reason this is done is because webpages seem to expect
-                // that they only get an ontouchmove if the slop has been exceeded.
-                if (mIsTapCandidate && d.mEvent != null
-                        && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
-                    return true;
-                }
-                return !mPostSendTouchEventsToWebKit
-                        || mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
-        }
-        return true;
-    }
-
-    private void enqueueWebKitCancelTouchEventIfNeededLocked() {
-        // We want to cancel touch events that were delivered to web kit.
-        // Enqueue a null event at the end of the queue if needed.
-        if (mWebKitTouchStream.isCancelNeeded() || !mWebKitDispatchEventQueue.isEmpty()) {
-            DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE,
-                    0, 0, 1.0f);
-            enqueueWebKitEventUnbatchedLocked(d);
-            mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
-        }
-    }
-
-    private void enqueueWebKitEventLocked(DispatchEvent d) {
-        if (batchEventLocked(d, mWebKitDispatchEventQueue.mTail)) {
-            if (DEBUG) {
-                Log.d(TAG, "enqueueWebKitEventLocked: batched event " + d.mEvent);
-            }
-            recycleDispatchEventLocked(d);
-        } else {
-            enqueueWebKitEventUnbatchedLocked(d);
-        }
-    }
-
-    private void enqueueWebKitEventUnbatchedLocked(DispatchEvent d) {
-        if (DEBUG) {
-            Log.d(TAG, "enqueueWebKitEventUnbatchedLocked: enqueued event " + d.mEvent);
-        }
-        mWebKitDispatchEventQueue.enqueue(d);
-        scheduleWebKitDispatchLocked();
-        updateWebKitTimeoutLocked();
-    }
-
-    private void scheduleWebKitDispatchLocked() {
-        if (!mWebKitDispatchScheduled) {
-            mWebKitHandler.sendEmptyMessage(WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS);
-            mWebKitDispatchScheduled = true;
-        }
-    }
-
-    private void updateWebKitTimeoutLocked() {
-        DispatchEvent d = mWebKitDispatchEventQueue.mHead;
-        if (d != null && mWebKitTimeoutScheduled && mWebKitTimeoutTime == d.mTimeoutTime) {
-            return;
-        }
-        if (mWebKitTimeoutScheduled) {
-            mUiHandler.removeMessages(UiHandler.MSG_WEBKIT_TIMEOUT);
-            mWebKitTimeoutScheduled = false;
-        }
-        if (d != null) {
-            mUiHandler.sendEmptyMessageAtTime(UiHandler.MSG_WEBKIT_TIMEOUT, d.mTimeoutTime);
-            mWebKitTimeoutScheduled = true;
-            mWebKitTimeoutTime = d.mTimeoutTime;
-        }
-    }
-
-    private void enqueueUiCancelTouchEventIfNeededLocked() {
-        // We want to cancel touch events that were delivered to the UI.
-        // Enqueue a null event at the end of the queue if needed.
-        if (mUiTouchStream.isCancelNeeded() || !mUiDispatchEventQueue.isEmpty()) {
-            DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE,
-                    0, 0, 1.0f);
-            enqueueUiEventUnbatchedLocked(d);
-        }
-    }
-
-    private void enqueueUiEventLocked(DispatchEvent d) {
-        if (batchEventLocked(d, mUiDispatchEventQueue.mTail)) {
-            if (DEBUG) {
-                Log.d(TAG, "enqueueUiEventLocked: batched event " + d.mEvent);
-            }
-            recycleDispatchEventLocked(d);
-        } else {
-            enqueueUiEventUnbatchedLocked(d);
-        }
-    }
-
-    private void enqueueUiEventUnbatchedLocked(DispatchEvent d) {
-        if (DEBUG) {
-            Log.d(TAG, "enqueueUiEventUnbatchedLocked: enqueued event " + d.mEvent);
-        }
-        mUiDispatchEventQueue.enqueue(d);
-        scheduleUiDispatchLocked();
-    }
-
-    private void scheduleUiDispatchLocked() {
-        if (!mUiDispatchScheduled) {
-            mUiHandler.sendEmptyMessage(UiHandler.MSG_DISPATCH_UI_EVENTS);
-            mUiDispatchScheduled = true;
-        }
-    }
-
-    private boolean batchEventLocked(DispatchEvent in, DispatchEvent tail) {
-        if (!ENABLE_EVENT_BATCHING) {
-            return false;
-        }
-        if (tail != null && tail.mEvent != null && in.mEvent != null
-                && in.mEventType == tail.mEventType
-                && in.mFlags == tail.mFlags
-                && in.mWebKitXOffset == tail.mWebKitXOffset
-                && in.mWebKitYOffset == tail.mWebKitYOffset
-                && in.mWebKitScale == tail.mWebKitScale) {
-            return tail.mEvent.addBatch(in.mEvent);
-        }
-        return false;
-    }
-
-    private DispatchEvent obtainDispatchEventLocked(MotionEvent event,
-            int eventType, int flags, int webKitXOffset, int webKitYOffset, float webKitScale) {
-        DispatchEvent d = obtainUninitializedDispatchEventLocked();
-        d.mEvent = event;
-        d.mEventType = eventType;
-        d.mFlags = flags;
-        d.mTimeoutTime = SystemClock.uptimeMillis() + WEBKIT_TIMEOUT_MILLIS;
-        d.mWebKitXOffset = webKitXOffset;
-        d.mWebKitYOffset = webKitYOffset;
-        d.mWebKitScale = webKitScale;
-        if (DEBUG) {
-            Log.d(TAG, "Timeout time: " + (d.mTimeoutTime - SystemClock.uptimeMillis()));
-        }
-        return d;
-    }
-
-    private DispatchEvent copyDispatchEventLocked(DispatchEvent d) {
-        DispatchEvent copy = obtainUninitializedDispatchEventLocked();
-        if (d.mEvent != null) {
-            copy.mEvent = d.mEvent.copy();
-        }
-        copy.mEventType = d.mEventType;
-        copy.mFlags = d.mFlags;
-        copy.mTimeoutTime = d.mTimeoutTime;
-        copy.mWebKitXOffset = d.mWebKitXOffset;
-        copy.mWebKitYOffset = d.mWebKitYOffset;
-        copy.mWebKitScale = d.mWebKitScale;
-        copy.mNext = d.mNext;
-        return copy;
-    }
-
-    private DispatchEvent obtainUninitializedDispatchEventLocked() {
-        DispatchEvent d = mDispatchEventPool;
-        if (d != null) {
-            mDispatchEventPoolSize -= 1;
-            mDispatchEventPool = d.mNext;
-            d.mNext = null;
-        } else {
-            d = new DispatchEvent();
-        }
-        return d;
-    }
-
-    private void recycleDispatchEventLocked(DispatchEvent d) {
-        if (d.mEvent != null) {
-            d.mEvent.recycle();
-            d.mEvent = null;
-        }
-
-        if (mDispatchEventPoolSize < MAX_DISPATCH_EVENT_POOL_SIZE) {
-            mDispatchEventPoolSize += 1;
-            d.mNext = mDispatchEventPool;
-            mDispatchEventPool = d;
-        }
-    }
-
-    /* Implemented by {@link WebViewClassic} to perform operations on the UI thread. */
-    public static interface UiCallbacks {
-        /**
-         * Gets the UI thread's looper.
-         * @return The looper.
-         */
-        public Looper getUiLooper();
-
-        /**
-         * Gets the UI's context
-         * @return The context
-         */
-        public Context getContext();
-
-        /**
-         * Dispatches an event to the UI.
-         * @param event The event.
-         * @param eventType The event type.
-         * @param flags The event's dispatch flags.
-         */
-        public void dispatchUiEvent(MotionEvent event, int eventType, int flags);
-
-        /**
-         * Asks the UI thread whether this touch event stream should be
-         * intercepted based on the touch down event.
-         * @param event The touch down event.
-         * @return true if the UI stream wants the touch stream without going
-         * through webkit or false otherwise.
-         */
-        public boolean shouldInterceptTouchEvent(MotionEvent event);
-
-        /**
-         * Inform's the UI that it should show the tap highlight
-         * @param show True if it should show the highlight, false if it should hide it
-         */
-        public void showTapHighlight(boolean show);
-
-        /**
-         * Called when we are sending a new EVENT_TYPE_HIT_TEST to WebKit, so
-         * previous hit tests should be cleared as they are obsolete.
-         */
-        public void clearPreviousHitTest();
-    }
-
-    /* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */
-    public static interface WebKitCallbacks {
-        /**
-         * Gets the web kit thread's looper.
-         * @return The looper.
-         */
-        public Looper getWebKitLooper();
-
-        /**
-         * Dispatches an event to web kit.
-         * @param dispatcher The WebViewInputDispatcher sending the event
-         * @param event The event.
-         * @param eventType The event type.
-         * @param flags The event's dispatch flags.
-         * @return True if web kit wants to prevent default event handling.
-         */
-        public boolean dispatchWebKitEvent(WebViewInputDispatcher dispatcher,
-                MotionEvent event, int eventType, int flags);
-    }
-
-    // Runs on UI thread.
-    private final class UiHandler extends Handler {
-        public static final int MSG_DISPATCH_UI_EVENTS = 1;
-        public static final int MSG_WEBKIT_TIMEOUT = 2;
-        public static final int MSG_LONG_PRESS = 3;
-        public static final int MSG_CLICK = 4;
-        public static final int MSG_SHOW_TAP_HIGHLIGHT = 5;
-        public static final int MSG_HIDE_TAP_HIGHLIGHT = 6;
-
-        public UiHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DISPATCH_UI_EVENTS:
-                    dispatchUiEvents(true);
-                    break;
-                case MSG_WEBKIT_TIMEOUT:
-                    handleWebKitTimeout();
-                    break;
-                case MSG_LONG_PRESS:
-                    postLongPress();
-                    break;
-                case MSG_CLICK:
-                    postClick();
-                    break;
-                case MSG_SHOW_TAP_HIGHLIGHT:
-                    postShowTapHighlight(true);
-                    break;
-                case MSG_HIDE_TAP_HIGHLIGHT:
-                    postShowTapHighlight(false);
-                    break;
-                default:
-                    throw new IllegalStateException("Unknown message type: " + msg.what);
-            }
-        }
-    }
-
-    // Runs on web kit thread.
-    private final class WebKitHandler extends Handler {
-        public static final int MSG_DISPATCH_WEBKIT_EVENTS = 1;
-
-        public WebKitHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DISPATCH_WEBKIT_EVENTS:
-                    dispatchWebKitEvents(true);
-                    break;
-                default:
-                    throw new IllegalStateException("Unknown message type: " + msg.what);
-            }
-        }
-    }
-
-    private static final class DispatchEvent {
-        public DispatchEvent mNext;
-
-        public MotionEvent mEvent;
-        public int mEventType;
-        public int mFlags;
-        public long mTimeoutTime;
-        public int mWebKitXOffset;
-        public int mWebKitYOffset;
-        public float mWebKitScale;
-    }
-
-    private static final class DispatchEventQueue {
-        public DispatchEvent mHead;
-        public DispatchEvent mTail;
-
-        public boolean isEmpty() {
-            return mHead != null;
-        }
-
-        public void enqueue(DispatchEvent d) {
-            if (mHead == null) {
-                mHead = d;
-                mTail = d;
-            } else {
-                mTail.mNext = d;
-                mTail = d;
-            }
-        }
-
-        public DispatchEvent dequeue() {
-            DispatchEvent d = mHead;
-            if (d != null) {
-                DispatchEvent next = d.mNext;
-                if (next == null) {
-                    mHead = null;
-                    mTail = null;
-                } else {
-                    mHead = next;
-                    d.mNext = null;
-                }
-            }
-            return d;
-        }
-
-        public DispatchEvent dequeueList() {
-            DispatchEvent d = mHead;
-            if (d != null) {
-                mHead = null;
-                mTail = null;
-            }
-            return d;
-        }
-    }
-
-    /**
-     * Keeps track of a stream of touch events so that we can discard touch
-     * events that would make the stream inconsistent.
-     */
-    private static final class TouchStream {
-        private MotionEvent mLastEvent;
-
-        /**
-         * Gets the last touch event that was delivered.
-         * @return The last touch event, or null if none.
-         */
-        public MotionEvent getLastEvent() {
-            return mLastEvent;
-        }
-
-        /**
-         * Updates the touch event stream.
-         * @param event The event that we intend to send, or null to cancel the
-         * touch event stream.
-         * @return The event that we should actually send, or null if no event should
-         * be sent because the proposed event would make the stream inconsistent.
-         */
-        public MotionEvent update(MotionEvent event) {
-            if (event == null) {
-                if (isCancelNeeded()) {
-                    event = mLastEvent;
-                    if (event != null) {
-                        event.setAction(MotionEvent.ACTION_CANCEL);
-                        mLastEvent = null;
-                    }
-                }
-                return event;
-            }
-
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_MOVE:
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_POINTER_DOWN:
-                case MotionEvent.ACTION_POINTER_UP:
-                    if (mLastEvent == null
-                            || mLastEvent.getAction() == MotionEvent.ACTION_UP) {
-                        return null;
-                    }
-                    updateLastEvent(event);
-                    return event;
-
-                case MotionEvent.ACTION_DOWN:
-                    updateLastEvent(event);
-                    return event;
-
-                case MotionEvent.ACTION_CANCEL:
-                    if (mLastEvent == null) {
-                        return null;
-                    }
-                    updateLastEvent(null);
-                    return event;
-
-                default:
-                    return null;
-            }
-        }
-
-        /**
-         * Returns true if there is a gesture in progress that may need to be canceled.
-         * @return True if cancel is needed.
-         */
-        public boolean isCancelNeeded() {
-            return mLastEvent != null && mLastEvent.getAction() != MotionEvent.ACTION_UP;
-        }
-
-        private void updateLastEvent(MotionEvent event) {
-            if (mLastEvent != null) {
-                mLastEvent.recycle();
-            }
-            mLastEvent = event != null ? MotionEvent.obtainNoHistory(event) : null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/webkit/ZoomControlBase.java b/core/java/android/webkit/ZoomControlBase.java
deleted file mode 100644
index be9e8f3..0000000
--- a/core/java/android/webkit/ZoomControlBase.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.webkit;
-
-interface ZoomControlBase {
-
-    /**
-     * Causes the on-screen zoom control to be made visible
-     */
-    public void show();
-
-    /**
-     * Causes the on-screen zoom control to disappear
-     */
-    public void hide();
-
-    /**
-     * Enables the control to update its state if necessary in response to a
-     * change in the pages zoom level. For example, if the max zoom level is
-     * reached then the control can disable the button for zooming in.
-     */
-    public void update();
-
-    /**
-     * Checks to see if the control is currently visible to the user.
-     */
-    public boolean isVisible();
-}
diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java
deleted file mode 100644
index ae19832..0000000
--- a/core/java/android/webkit/ZoomControlEmbedded.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.webkit;
-
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.Toast;
-import android.widget.ZoomButtonsController;
-
-class ZoomControlEmbedded implements ZoomControlBase {
-
-    private final ZoomManager mZoomManager;
-    private final WebViewClassic mWebView;
-
-    // The controller is lazily initialized in getControls() for performance.
-    private ZoomButtonsController mZoomButtonsController;
-
-    public ZoomControlEmbedded(ZoomManager zoomManager, WebViewClassic webView) {
-        mZoomManager = zoomManager;
-        mWebView = webView;
-    }
-
-    public void show() {
-        if (!getControls().isVisible() && !mZoomManager.isZoomScaleFixed()) {
-
-            mZoomButtonsController.setVisible(true);
-
-            if (mZoomManager.isDoubleTapEnabled()) {
-                WebSettingsClassic settings = mWebView.getSettings();
-                int count = settings.getDoubleTapToastCount();
-                if (mZoomManager.isInZoomOverview() && count > 0) {
-                    settings.setDoubleTapToastCount(--count);
-                    Toast.makeText(mWebView.getContext(),
-                            com.android.internal.R.string.double_tap_toast,
-                            Toast.LENGTH_LONG).show();
-                }
-            }
-        }
-    }
-
-    public void hide() {
-        if (mZoomButtonsController != null) {
-            mZoomButtonsController.setVisible(false);
-        }
-    }
-
-    public boolean isVisible() {
-        return mZoomButtonsController != null && mZoomButtonsController.isVisible();
-    }
-
-    public void update() {
-        if (mZoomButtonsController == null) {
-            return;
-        }
-
-        boolean canZoomIn = mZoomManager.canZoomIn();
-        boolean canZoomOut = mZoomManager.canZoomOut() && !mZoomManager.isInZoomOverview();
-        if (!canZoomIn && !canZoomOut) {
-            // Hide the zoom in and out buttons if the page cannot zoom
-            mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
-        } else {
-            // Set each one individually, as a page may be able to zoom in or out
-            mZoomButtonsController.setZoomInEnabled(canZoomIn);
-            mZoomButtonsController.setZoomOutEnabled(canZoomOut);
-        }
-    }
-
-    private ZoomButtonsController getControls() {
-        if (mZoomButtonsController == null) {
-            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
-            // right.
-            View controls = mZoomButtonsController.getZoomControls();
-            ViewGroup.LayoutParams params = controls.getLayoutParams();
-            if (params instanceof FrameLayout.LayoutParams) {
-                ((FrameLayout.LayoutParams) params).gravity = Gravity.END;
-            }
-        }
-        return mZoomButtonsController;
-    }
-
-    private class ZoomListener implements ZoomButtonsController.OnZoomListener {
-
-        public void onVisibilityChanged(boolean visible) {
-            if (visible) {
-                mWebView.switchOutDrawHistory();
-                // Bring back the hidden zoom controls.
-                mZoomButtonsController.getZoomControls().setVisibility(View.VISIBLE);
-                update();
-            }
-        }
-
-        public void onZoom(boolean zoomIn) {
-            if (zoomIn) {
-                mWebView.zoomIn();
-            } else {
-                mWebView.zoomOut();
-            }
-            update();
-        }
-    }
-}
diff --git a/core/java/android/webkit/ZoomControlExternal.java b/core/java/android/webkit/ZoomControlExternal.java
deleted file mode 100644
index f5bfc05..0000000
--- a/core/java/android/webkit/ZoomControlExternal.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.webkit;
-
-import android.content.Context;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.View.OnClickListener;
-import android.view.animation.AlphaAnimation;
-import android.widget.FrameLayout;
-
-@Deprecated
-class ZoomControlExternal implements ZoomControlBase {
-
-    // The time that the external controls are visible before fading away
-    private static final long ZOOM_CONTROLS_TIMEOUT =
-            ViewConfiguration.getZoomControlsTimeout();
-    // The view containing the external zoom controls
-    private ExtendedZoomControls mZoomControls;
-    private Runnable mZoomControlRunnable;
-    private final Handler mPrivateHandler = new Handler();
-
-    private final WebViewClassic mWebView;
-
-    public ZoomControlExternal(WebViewClassic webView) {
-        mWebView = webView;
-    }
-
-    public void show() {
-        if(mZoomControlRunnable != null) {
-            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-        }
-        getControls().show(true);
-        mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
-    }
-
-    public void hide() {
-        if (mZoomControlRunnable != null) {
-            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-        }
-        if (mZoomControls != null) {
-            mZoomControls.hide();
-        }
-    }
-
-    public boolean isVisible() {
-        return mZoomControls != null && mZoomControls.isShown();
-    }
-
-    public void update() { }
-
-    public ExtendedZoomControls getControls() {
-        if (mZoomControls == null) {
-            mZoomControls = createZoomControls();
-
-            /*
-             * need to be set to VISIBLE first so that getMeasuredHeight() in
-             * {@link #onSizeChanged()} can return the measured value for proper
-             * layout.
-             */
-            mZoomControls.setVisibility(View.VISIBLE);
-            mZoomControlRunnable = new Runnable() {
-                public void run() {
-                    /* Don't dismiss the controls if the user has
-                     * focus on them. Wait and check again later.
-                     */
-                    if (!mZoomControls.hasFocus()) {
-                        mZoomControls.hide();
-                    } else {
-                        mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                        mPrivateHandler.postDelayed(mZoomControlRunnable,
-                                ZOOM_CONTROLS_TIMEOUT);
-                    }
-                }
-            };
-        }
-        return mZoomControls;
-    }
-
-    private ExtendedZoomControls createZoomControls() {
-        ExtendedZoomControls zoomControls = new ExtendedZoomControls(mWebView.getContext());
-        zoomControls.setOnZoomInClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                // reset time out
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
-                mWebView.zoomIn();
-            }
-        });
-        zoomControls.setOnZoomOutClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                // reset time out
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
-                mWebView.zoomOut();
-            }
-        });
-        return zoomControls;
-    }
-
-    private static class ExtendedZoomControls extends FrameLayout {
-
-        private android.widget.ZoomControls mPlusMinusZoomControls;
-
-        public ExtendedZoomControls(Context context) {
-            super(context, null);
-            LayoutInflater inflater = (LayoutInflater)
-                    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
-            mPlusMinusZoomControls = (android.widget.ZoomControls) findViewById(
-                    com.android.internal.R.id.zoomControls);
-            findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
-                    View.GONE);
-        }
-
-        public void show(boolean showZoom) {
-            mPlusMinusZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE);
-            fade(View.VISIBLE, 0.0f, 1.0f);
-        }
-
-        public void hide() {
-            fade(View.GONE, 1.0f, 0.0f);
-        }
-
-        private void fade(int visibility, float startAlpha, float endAlpha) {
-            AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
-            anim.setDuration(500);
-            startAnimation(anim);
-            setVisibility(visibility);
-        }
-
-        public boolean hasFocus() {
-            return mPlusMinusZoomControls.hasFocus();
-        }
-
-        public void setOnZoomInClickListener(OnClickListener listener) {
-            mPlusMinusZoomControls.setOnZoomInClickListener(listener);
-        }
-
-        public void setOnZoomOutClickListener(OnClickListener listener) {
-            mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
-        }
-    }
-}
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
deleted file mode 100644
index 1d864e5..0000000
--- a/core/java/android/webkit/ZoomManager.java
+++ /dev/null
@@ -1,1263 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.Canvas;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.FloatMath;
-import android.util.Log;
-import android.view.ScaleGestureDetector;
-import android.view.View;
-
-/**
- * The ZoomManager is responsible for maintaining the WebView's current zoom
- * level state.  It is also responsible for managing the on-screen zoom controls
- * as well as any animation of the WebView due to zooming.
- *
- * Currently, there are two methods for animating the zoom of a WebView.
- *
- * (1) The first method is triggered by startZoomAnimation(...) and is a fixed
- * length animation where the final zoom scale is known at startup.  This type of
- * animation notifies webkit of the final scale BEFORE it animates. The animation
- * is then done by scaling the CANVAS incrementally based on a stepping function.
- *
- * (2) The second method is triggered by a multi-touch pinch and the new scale
- * is determined dynamically based on the user's gesture. This type of animation
- * only notifies webkit of new scale AFTER the gesture is complete. The animation
- * effect is achieved by scaling the VIEWS (both WebView and ViewManager.ChildView)
- * to the new scale in response to events related to the user's gesture.
- */
-class ZoomManager {
-
-    static final String LOGTAG = "webviewZoom";
-
-    private final WebViewClassic mWebView;
-    private final CallbackProxy mCallbackProxy;
-
-    // Widgets responsible for the on-screen zoom functions of the WebView.
-    private ZoomControlEmbedded mEmbeddedZoomControl;
-    private ZoomControlExternal mExternalZoomControl;
-
-    /*
-     * The scale factors that determine the upper and lower bounds for the
-     * default zoom scale.
-     */
-    protected static final float DEFAULT_MAX_ZOOM_SCALE_FACTOR = 4.00f;
-    protected static final float DEFAULT_MIN_ZOOM_SCALE_FACTOR = 0.25f;
-
-    // The default scale limits, which are dependent on the display density.
-    private float mDefaultMaxZoomScale;
-    private float mDefaultMinZoomScale;
-
-    // The actual scale limits, which can be set through a webpage's viewport
-    // meta-tag.
-    private float mMaxZoomScale;
-    private float mMinZoomScale;
-
-    // Locks the minimum ZoomScale to the value currently set in mMinZoomScale.
-    private boolean mMinZoomScaleFixed = true;
-
-    /*
-     * When loading a new page the WebView does not initially know the final
-     * width of the page. Therefore, when a new page is loaded in overview mode
-     * the overview scale is initialized to a default value. This flag is then
-     * set and used to notify the ZoomManager to take the width of the next
-     * picture from webkit and use that width to enter into zoom overview mode.
-     */
-    private boolean mInitialZoomOverview = false;
-
-    /*
-     * When in the zoom overview mode, the page's width is fully fit to the
-     * current window. Additionally while the page is in this state it is
-     * active, in other words, you can click to follow the links. We cache a
-     * boolean to enable us to quickly check whether or not we are in overview
-     * mode, but this value should only be modified by changes to the zoom
-     * scale.
-     */
-    private boolean mInZoomOverview = false;
-    private int mZoomOverviewWidth;
-    private float mInvZoomOverviewWidth;
-
-    /*
-     * These variables track the center point of the zoom and they are used to
-     * determine the point around which we should zoom. They are stored in view
-     * coordinates.
-     */
-    private float mZoomCenterX;
-    private float mZoomCenterY;
-
-    /*
-     * Similar to mZoomCenterX(Y), these track the focus point of the scale
-     * gesture. The difference is these get updated every time when onScale is
-     * invoked no matter if a zooming really happens.
-     */
-    private float mFocusX;
-    private float mFocusY;
-
-    /*
-     * mFocusMovementQueue keeps track of the previous focus point movement
-     * has been through. Comparing to the difference of the gesture's previous
-     * span and current span, it determines if the gesture is for panning or
-     * zooming or both.
-     */
-    private FocusMovementQueue mFocusMovementQueue;
-
-    /*
-     * These values represent the point around which the screen should be
-     * centered after zooming. In other words it is used to determine the center
-     * point of the visible document after the page has finished zooming. This
-     * is important because the zoom may have potentially reflowed the text and
-     * we need to ensure the proper portion of the document remains on the
-     * screen.
-     */
-    private int mAnchorX;
-    private int mAnchorY;
-
-    // The scale factor that is used to determine the column width for text
-    private float mTextWrapScale;
-
-    /*
-     * The default zoom scale is the scale factor used when the user triggers a
-     * zoom in by double tapping on the WebView. The value is initially set
-     * based on the display density, but can be changed at any time via the
-     * WebSettings.
-     */
-    private float mDefaultScale;
-    private float mInvDefaultScale;
-
-    /*
-     * The logical density of the display. This is a scaling factor for the
-     * Density Independent Pixel unit, where one DIP is one pixel on an
-     * approximately 160 dpi screen (see android.util.DisplayMetrics.density)
-     */
-    private float mDisplayDensity;
-
-    /*
-     * The factor that is used to tweak the zoom scale on a double-tap,
-     * and can be changed via WebSettings. Range is from 0.75f to 1.25f.
-     */
-    private float mDoubleTapZoomFactor = 1.0f;
-
-    /*
-     * The scale factor that is used as the minimum increment when going from
-     * overview to reading level on a double tap.
-     */
-    private static float MIN_DOUBLE_TAP_SCALE_INCREMENT = 0.5f;
-
-    // the current computed zoom scale and its inverse.
-    private float mActualScale;
-    private float mInvActualScale;
-    
-    /*
-     * The initial scale for the WebView. 0 means default. If initial scale is
-     * greater than 0, the WebView starts with this value as its initial scale.
-     */
-    private float mInitialScale;
-
-    private static float MINIMUM_SCALE_INCREMENT = 0.007f;
-
-    /*
-     *  The touch points could be changed even the fingers stop moving.
-     *  We use the following to filter out the zooming jitters.
-     */
-    private static float MINIMUM_SCALE_WITHOUT_JITTER = 0.007f;
-
-    /*
-     * The following member variables are only to be used for animating zoom. If
-     * mZoomScale is non-zero then we are in the middle of a zoom animation. The
-     * other variables are used as a cache (e.g. inverse) or as a way to store
-     * the state of the view prior to animating (e.g. initial scroll coords).
-     */
-    private float mZoomScale;
-    private float mInvInitialZoomScale;
-    private float mInvFinalZoomScale;
-    private int mInitialScrollX;
-    private int mInitialScrollY;
-    private long mZoomStart;
-
-    private static final int ZOOM_ANIMATION_LENGTH = 175;
-
-    // whether support multi-touch
-    private boolean mSupportMultiTouch;
-    
-    /**
-     * True if we have a touch panel capable of detecting smooth pan/scale at the same time
-     */
-    private boolean mAllowPanAndScale;
-
-    // use the framework's ScaleGestureDetector to handle scaling gestures
-    private ScaleGestureDetector mScaleDetector;
-    private boolean mPinchToZoomAnimating = false;
-
-    private boolean mHardwareAccelerated = false;
-    private boolean mInHWAcceleratedZoom = false;
-
-    public ZoomManager(WebViewClassic webView, CallbackProxy callbackProxy) {
-        mWebView = webView;
-        mCallbackProxy = callbackProxy;
-
-        /*
-         * Ideally mZoomOverviewWidth should be mContentWidth. But sites like
-         * ESPN and Engadget always have wider mContentWidth no matter what the
-         * viewport size is.
-         */
-        setZoomOverviewWidth(WebViewClassic.DEFAULT_VIEWPORT_WIDTH);
-
-        mFocusMovementQueue = new FocusMovementQueue();
-    }
-
-    /**
-     * Initialize both the default and actual zoom scale to the given density.
-     *
-     * @param density The logical density of the display. This is a scaling factor
-     * for the Density Independent Pixel unit, where one DIP is one pixel on an
-     * approximately 160 dpi screen (see android.util.DisplayMetrics.density).
-     */
-    public void init(float density) {
-        assert density > 0;
-
-        mDisplayDensity = density;
-        setDefaultZoomScale(density);
-        mActualScale = density;
-        mInvActualScale = 1 / density;
-        mTextWrapScale = getReadingLevelScale();
-    }
-
-    /**
-     * Update the default zoom scale using the given density. It will also reset
-     * the current min and max zoom scales to the default boundaries as well as
-     * ensure that the actual scale falls within those boundaries.
-     *
-     * @param density The logical density of the display. This is a scaling factor
-     * for the Density Independent Pixel unit, where one DIP is one pixel on an
-     * approximately 160 dpi screen (see android.util.DisplayMetrics.density).
-     */
-    public void updateDefaultZoomDensity(float density) {
-        assert density > 0;
-
-        if (Math.abs(density - mDefaultScale) > MINIMUM_SCALE_INCREMENT) {
-            // Remember the current zoom density before it gets changed.
-            final float originalDefault = mDefaultScale;
-            // set the new default density
-            mDisplayDensity = density;
-            setDefaultZoomScale(density);
-            float scaleChange = (originalDefault > 0.0) ? density / originalDefault: 1.0f;
-            // adjust the scale if it falls outside the new zoom bounds
-            setZoomScale(mActualScale * scaleChange, true);
-        }
-    }
-
-    private void setDefaultZoomScale(float defaultScale) {
-        final float originalDefault = mDefaultScale;
-        mDefaultScale = defaultScale;
-        mInvDefaultScale = 1 / defaultScale;
-        mDefaultMaxZoomScale = defaultScale * DEFAULT_MAX_ZOOM_SCALE_FACTOR;
-        mDefaultMinZoomScale = defaultScale * DEFAULT_MIN_ZOOM_SCALE_FACTOR;
-        if (originalDefault > 0.0 && mMaxZoomScale > 0.0) {
-            // Keeps max zoom scale when zoom density changes.
-            mMaxZoomScale = defaultScale / originalDefault * mMaxZoomScale;
-        } else {
-            mMaxZoomScale = mDefaultMaxZoomScale;
-        }
-        if (originalDefault > 0.0 && mMinZoomScale > 0.0) {
-            // Keeps min zoom scale when zoom density changes.
-            mMinZoomScale = defaultScale / originalDefault * mMinZoomScale;
-        } else {
-            mMinZoomScale = mDefaultMinZoomScale;
-        }
-        if (!exceedsMinScaleIncrement(mMinZoomScale, mMaxZoomScale)) {
-            mMaxZoomScale = mMinZoomScale;
-        }
-        sanitizeMinMaxScales();
-    }
-
-    public final float getScale() {
-        return mActualScale;
-    }
-
-    public final float getInvScale() {
-        return mInvActualScale;
-    }
-
-    public final float getTextWrapScale() {
-        return mTextWrapScale;
-    }
-
-    public final float getMaxZoomScale() {
-        return mMaxZoomScale;
-    }
-
-    public final float getMinZoomScale() {
-        return mMinZoomScale;
-    }
-
-    public final float getDefaultScale() {
-        return mDefaultScale;
-    }
-
-    /**
-     * Returns the zoom scale used for reading text on a double-tap.
-     */
-    public final float getReadingLevelScale() {
-        return computeScaleWithLimits(computeReadingLevelScale(getZoomOverviewScale()));
-    }
-
-    /* package */ final float computeReadingLevelScale(float scale) {
-        return Math.max(mDisplayDensity * mDoubleTapZoomFactor,
-                scale + MIN_DOUBLE_TAP_SCALE_INCREMENT);
-    }
-
-    public final float getInvDefaultScale() {
-        return mInvDefaultScale;
-    }
-
-    public final float getDefaultMaxZoomScale() {
-        return mDefaultMaxZoomScale;
-    }
-
-    public final float getDefaultMinZoomScale() {
-        return mDefaultMinZoomScale;
-    }
-
-    public final int getDocumentAnchorX() {
-        return mAnchorX;
-    }
-
-    public final int getDocumentAnchorY() {
-        return mAnchorY;
-    }
-
-    public final void clearDocumentAnchor() {
-        mAnchorX = mAnchorY = 0;
-    }
-
-    public final void setZoomCenter(float x, float y) {
-        mZoomCenterX = x;
-        mZoomCenterY = y;
-    }
-
-    public final void setInitialScaleInPercent(int scaleInPercent) {
-        mInitialScale = scaleInPercent * 0.01f;
-    }
-
-    public final float computeScaleWithLimits(float scale) {
-        if (scale < mMinZoomScale) {
-            scale = mMinZoomScale;
-        } else if (scale > mMaxZoomScale) {
-            scale = mMaxZoomScale;
-        }
-        return scale;
-    }
-
-    public final boolean isScaleOverLimits(float scale) {
-        return scale <= mMinZoomScale || scale >= mMaxZoomScale;
-    }
-
-    public final boolean isZoomScaleFixed() {
-        return mMinZoomScale >= mMaxZoomScale;
-    }
-
-    public static final boolean exceedsMinScaleIncrement(float scaleA, float scaleB) {
-        return Math.abs(scaleA - scaleB) >= MINIMUM_SCALE_INCREMENT;
-    }
-
-    public boolean willScaleTriggerZoom(float scale) {
-        return exceedsMinScaleIncrement(scale, mActualScale);
-    }
-
-    public final boolean canZoomIn() {
-        return mMaxZoomScale - mActualScale > MINIMUM_SCALE_INCREMENT;
-    }
-
-    public final boolean canZoomOut() {
-        return mActualScale - mMinZoomScale > MINIMUM_SCALE_INCREMENT;
-    }
-
-    public boolean zoomIn() {
-        return zoom(1.25f);
-    }
-
-    public boolean zoomOut() {
-        return zoom(0.8f);
-    }
-
-    // returns TRUE if zoom out succeeds and FALSE if no zoom changes.
-    private boolean zoom(float zoomMultiplier) {
-        mInitialZoomOverview = false;
-        // TODO: alternatively we can disallow this during draw history mode
-        mWebView.switchOutDrawHistory();
-        // Center zooming to the center of the screen.
-        mZoomCenterX = mWebView.getViewWidth() * .5f;
-        mZoomCenterY = mWebView.getViewHeight() * .5f;
-        mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX());
-        mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY());
-        return startZoomAnimation(mActualScale * zoomMultiplier, 
-            !mWebView.getSettings().getUseFixedViewport());
-    }
-
-    /**
-     * Initiates an animated zoom of the WebView.
-     *
-     * @return true if the new scale triggered an animation and false otherwise.
-     */
-    public boolean startZoomAnimation(float scale, boolean reflowText) {
-        mInitialZoomOverview = false;
-        float oldScale = mActualScale;
-        mInitialScrollX = mWebView.getScrollX();
-        mInitialScrollY = mWebView.getScrollY();
-
-        // snap to reading level scale if it is close
-        if (!exceedsMinScaleIncrement(scale, getReadingLevelScale())) {
-            scale = getReadingLevelScale();
-        }
-
-        setZoomScale(scale, reflowText);
-
-        if (oldScale != mActualScale) {
-            if (mHardwareAccelerated) {
-                mInHWAcceleratedZoom = true;
-            }
-            // use mZoomPickerScale to see zoom preview first
-            mZoomStart = SystemClock.uptimeMillis();
-            mInvInitialZoomScale = 1.0f / oldScale;
-            mInvFinalZoomScale = 1.0f / mActualScale;
-            mZoomScale = mActualScale;
-            mWebView.onFixedLengthZoomAnimationStart();
-            mWebView.invalidate();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * This method is called by the WebView's drawing code when a fixed length zoom
-     * animation is occurring. Its purpose is to animate the zooming of the canvas
-     * to the desired scale which was specified in startZoomAnimation(...).
-     *
-     * A fixed length animation begins when startZoomAnimation(...) is called and
-     * continues until the ZOOM_ANIMATION_LENGTH time has elapsed. During that
-     * interval each time the WebView draws it calls this function which is 
-     * responsible for generating the animation.
-     *
-     * Additionally, the WebView can check to see if such an animation is currently
-     * in progress by calling isFixedLengthAnimationInProgress().
-     */
-    public void animateZoom(Canvas canvas) {
-        mInitialZoomOverview = false;
-        if (mZoomScale == 0) {
-            Log.w(LOGTAG, "A WebView is attempting to perform a fixed length "
-                    + "zoom animation when no zoom is in progress");
-            // Now that we've logged about it, go ahead and just recover
-            mInHWAcceleratedZoom = false;
-            return;
-        }
-
-        float zoomScale;
-        int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
-        if (interval < ZOOM_ANIMATION_LENGTH) {
-            float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
-            zoomScale = 1.0f / (mInvInitialZoomScale
-                    + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
-            mWebView.invalidate();
-        } else {
-            zoomScale = mZoomScale;
-            // set mZoomScale to be 0 as we have finished animating
-            mZoomScale = 0;
-            mWebView.onFixedLengthZoomAnimationEnd();
-        }
-        // calculate the intermediate scroll position. Since we need to use
-        // 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 = -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) : WebViewClassic.pinLoc(ty
-                - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight()
-                * zoomScale)) + titleHeight) + mWebView.getScrollY();
-
-        if (mHardwareAccelerated) {
-            mWebView.updateScrollCoordinates(mWebView.getScrollX() - tx, mWebView.getScrollY() - ty);
-            // By adding webView matrix, we need to offset the canvas a bit
-            // to make the animation smooth.
-            canvas.translate(tx, ty);
-            setZoomScale(zoomScale, false);
-
-            if (mZoomScale == 0) {
-                // We've reached the end of the zoom animation.
-                mInHWAcceleratedZoom = false;
-
-                // Ensure that the zoom level is pushed to WebCore. This has not
-                // yet occurred because we prevent it from happening while
-                // mInHWAcceleratedZoom is true.
-                mWebView.sendViewSizeZoom(false);
-            }
-        } else {
-            canvas.translate(tx, ty);
-            canvas.scale(zoomScale, zoomScale);
-        }
-    }
-
-    public boolean isZoomAnimating() {
-        return isFixedLengthAnimationInProgress() || mPinchToZoomAnimating;
-    }
-
-    public boolean isFixedLengthAnimationInProgress() {
-        return mZoomScale != 0 || mInHWAcceleratedZoom;
-    }
-
-    public void updateDoubleTapZoom(int doubleTapZoom) {
-        boolean zoomIn = (mTextWrapScale - mActualScale) < .1f;
-        mDoubleTapZoomFactor = doubleTapZoom / 100.0f;
-        mTextWrapScale = getReadingLevelScale();
-        float newScale = zoomIn ? mTextWrapScale
-                : Math.min(mTextWrapScale, mActualScale);
-        setZoomScale(newScale, true, true);
-    }
-
-    public void refreshZoomScale(boolean reflowText) {
-        setZoomScale(mActualScale, reflowText, true);
-    }
-
-    public void setZoomScale(float scale, boolean reflowText) {
-        setZoomScale(scale, reflowText, false);
-    }
-
-    private void setZoomScale(float scale, boolean reflowText, boolean force) {
-        final boolean isScaleLessThanMinZoom = scale < mMinZoomScale;
-        scale = computeScaleWithLimits(scale);
-
-        // determine whether or not we are in the zoom overview mode
-        if (isScaleLessThanMinZoom && mMinZoomScale < mDefaultScale) {
-            mInZoomOverview = true;
-        } else {
-            mInZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale());
-        }
-
-        if (reflowText && !mWebView.getSettings().getUseFixedViewport()) {
-            mTextWrapScale = scale;
-        }
-
-        if (scale != mActualScale || force) {
-            float oldScale = mActualScale;
-            float oldInvScale = mInvActualScale;
-
-            if (scale != mActualScale && !mPinchToZoomAnimating) {
-                mCallbackProxy.onScaleChanged(mActualScale, scale);
-            }
-
-            mActualScale = scale;
-            mInvActualScale = 1 / scale;
-
-            if (!mWebView.drawHistory() && !mInHWAcceleratedZoom) {
-
-                // If history Picture is drawn, don't update scroll. They will
-                // be updated when we get out of that mode.
-                // update our scroll so we don't appear to jump
-                // i.e. keep the center of the doc in the center of the view
-                // If this is part of a zoom on a HW accelerated canvas, we
-                // have already updated the scroll so don't do it again.
-                int oldX = mWebView.getScrollX();
-                int oldY = mWebView.getScrollY();
-                float ratio = scale * oldInvScale;
-                float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
-                float sy = ratio * oldY + (ratio - 1)
-                        * (mZoomCenterY - mWebView.getTitleHeight());
-
-                // Scale all the child views
-                mWebView.mViewManager.scaleAll();
-
-                // as we don't have animation for scaling, don't do animation
-                // for scrolling, as it causes weird intermediate state
-                int scrollX = mWebView.pinLocX(Math.round(sx));
-                int scrollY = mWebView.pinLocY(Math.round(sy));
-                if(!mWebView.updateScrollCoordinates(scrollX, scrollY)) {
-                    // the scroll position is adjusted at the beginning of the
-                    // zoom animation. But we want to update the WebKit at the
-                    // end of the zoom animation. See comments in onScaleEnd().
-                    mWebView.sendOurVisibleRect();
-                }
-            }
-
-            // if the we need to reflow the text then force the VIEW_SIZE_CHANGED
-            // event to be sent to WebKit
-            mWebView.sendViewSizeZoom(reflowText);
-        }
-    }
-
-    public boolean isDoubleTapEnabled() {
-        WebSettings settings = mWebView.getSettings();
-        return settings != null && settings.getUseWideViewPort();
-    }
-
-    /**
-     * The double tap gesture can result in different behaviors depending on the
-     * content that is tapped.
-     *
-     * (1) PLUGINS: If the taps occur on a plugin then we maximize the plugin on
-     * the screen. If the plugin is already maximized then zoom the user into
-     * overview mode.
-     *
-     * (2) HTML/OTHER: If the taps occur outside a plugin then the following
-     * heuristic is used.
-     *   A. If the current text wrap scale differs from newly calculated and the
-     *      layout algorithm specifies the use of NARROW_COLUMNS, then fit to
-     *      column by reflowing the text.
-     *   B. If the page is not in overview mode then change to overview mode.
-     *   C. If the page is in overmode then change to the default scale.
-     */
-    public void handleDoubleTap(float lastTouchX, float lastTouchY) {
-        // User takes action, set initial zoom overview to false.
-        mInitialZoomOverview = false;
-        WebSettingsClassic settings = mWebView.getSettings();
-        if (!isDoubleTapEnabled()) {
-            return;
-        }
-
-        setZoomCenter(lastTouchX, lastTouchY);
-        mAnchorX = mWebView.viewToContentX((int) lastTouchX + mWebView.getScrollX());
-        mAnchorY = mWebView.viewToContentY((int) lastTouchY + mWebView.getScrollY());
-        settings.setDoubleTapToastCount(0);
-
-        // remove the zoom control after double tap
-        dismissZoomPicker();
-
-        final float newTextWrapScale;
-        if (settings.getUseFixedViewport()) {
-            newTextWrapScale = Math.max(mActualScale, getReadingLevelScale());
-        } else {
-            newTextWrapScale = mActualScale;
-        }
-        final boolean firstTimeReflow = !exceedsMinScaleIncrement(mActualScale, mTextWrapScale);
-        if (firstTimeReflow || mInZoomOverview) {
-            // In case first time reflow or in zoom overview mode, let reflow and zoom
-            // happen at the same time.
-            mTextWrapScale = newTextWrapScale;
-        }
-        if (settings.isNarrowColumnLayout()
-                && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale)
-                && !firstTimeReflow
-                && !mInZoomOverview) {
-            // Reflow only.
-            mTextWrapScale = newTextWrapScale;
-            refreshZoomScale(true);
-        } else if (!mInZoomOverview && willScaleTriggerZoom(getZoomOverviewScale())) {
-            // Reflow, if necessary.
-            if (mTextWrapScale > getReadingLevelScale()) {
-                mTextWrapScale = getReadingLevelScale();
-                refreshZoomScale(true);
-            }
-            zoomToOverview();
-        } else {
-            zoomToReadingLevel();
-        }
-    }
-
-    private void setZoomOverviewWidth(int width) {
-        if (width == 0) {
-            mZoomOverviewWidth = WebViewClassic.DEFAULT_VIEWPORT_WIDTH;
-        } else {
-            mZoomOverviewWidth = width;
-        }
-        mInvZoomOverviewWidth = 1.0f / width;
-    }
-
-    /* package */ float getZoomOverviewScale() {
-        return mWebView.getViewWidth() * mInvZoomOverviewWidth;
-    }
-
-    public boolean isInZoomOverview() {
-        return mInZoomOverview;
-    }
-
-    private void zoomToOverview() {
-        // Force the titlebar fully reveal in overview mode
-        int scrollY = mWebView.getScrollY();
-        if (scrollY < mWebView.getTitleHeight()) {
-            mWebView.updateScrollCoordinates(mWebView.getScrollX(), 0);
-        }
-        startZoomAnimation(getZoomOverviewScale(), 
-            !mWebView.getSettings().getUseFixedViewport());
-    }
-
-    private void zoomToReadingLevel() {
-        final float readingScale = getReadingLevelScale();
-
-        int left = mWebView.getBlockLeftEdge(mAnchorX, mAnchorY, readingScale);
-        if (left != WebViewClassic.NO_LEFTEDGE) {
-            // add a 5pt padding to the left edge.
-            int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5))
-                    - mWebView.getScrollX();
-            // Re-calculate the zoom center so that the new scroll x will be
-            // on the left edge.
-            if (viewLeft > 0) {
-                mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
-            } else {
-                mWebView.getWebView().scrollBy(viewLeft, 0);
-                mZoomCenterX = 0;
-            }
-        }
-        startZoomAnimation(readingScale,
-            !mWebView.getSettings().getUseFixedViewport());
-    }
-
-    public void updateMultiTouchSupport(Context context) {
-        // check the preconditions
-        assert mWebView.getSettings() != null;
-
-        final WebSettings settings = mWebView.getSettings();
-        final PackageManager pm = context.getPackageManager();
-        mSupportMultiTouch = 
-                (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
-                 || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT))
-                && settings.supportZoom() && settings.getBuiltInZoomControls();
-        mAllowPanAndScale =
-                pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
-                || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT);
-
-        if (mSupportMultiTouch && (mScaleDetector == null)) {
-            mScaleDetector = new ScaleGestureDetector(context, new ScaleDetectorListener());
-        } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
-            mScaleDetector = null;
-        }
-    }
-
-    public boolean supportsMultiTouchZoom() {
-        return mSupportMultiTouch;
-    }
-
-    public boolean supportsPanDuringZoom() {
-        return mAllowPanAndScale;
-    }
-
-    /**
-     * Notifies the caller that the ZoomManager is requesting that scale related
-     * updates should not be sent to webkit. This can occur in cases where the
-     * ZoomManager is performing an animation and does not want webkit to update
-     * until the animation is complete.
-     *
-     * @return true if scale related updates should not be sent to webkit and
-     *         false otherwise.
-     */
-    public boolean isPreventingWebkitUpdates() {
-        // currently only animating a multi-touch zoom and fixed length
-        // animations prevent updates, but others can add their own conditions
-        // to this method if necessary.
-        return isZoomAnimating();
-    }
-
-    public ScaleGestureDetector getScaleGestureDetector() {
-        return mScaleDetector;
-    }
-
-    private class FocusMovementQueue {
-        private static final int QUEUE_CAPACITY = 5;
-        private float[] mQueue;
-        private float mSum;
-        private int mSize;
-        private int mIndex;
-
-        FocusMovementQueue() {
-            mQueue = new float[QUEUE_CAPACITY];
-            mSize = 0;
-            mSum = 0;
-            mIndex = 0;
-        }
-
-        private void clear() {
-            mSize = 0;
-            mSum = 0;
-            mIndex = 0;
-            for (int i = 0; i < QUEUE_CAPACITY; ++i) {
-                mQueue[i] = 0;
-            }
-        }
-
-        private void add(float focusDelta) {
-            mSum += focusDelta;
-            if (mSize < QUEUE_CAPACITY) {  // fill up the queue.
-                mSize++;
-            } else {  // circulate the queue.
-                mSum -= mQueue[mIndex];
-            }
-            mQueue[mIndex] = focusDelta;
-            mIndex = (mIndex + 1) % QUEUE_CAPACITY;
-        }
-
-        private float getSum() {
-            return mSum;
-        }
-    }
-
-    private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener {
-        private float mAccumulatedSpan;
-
-        public boolean onScaleBegin(ScaleGestureDetector detector) {
-            mInitialZoomOverview = false;
-            dismissZoomPicker();
-            mFocusMovementQueue.clear();
-            mFocusX = detector.getFocusX();
-            mFocusY = detector.getFocusY();
-            mWebView.mViewManager.startZoom();
-            mWebView.onPinchToZoomAnimationStart();
-            mAccumulatedSpan = 0;
-            return true;
-        }
-
-            // If the user moves the fingers but keeps the same distance between them,
-            // we should do panning only.
-        public boolean isPanningOnly(ScaleGestureDetector detector) {
-            float prevFocusX = mFocusX;
-            float prevFocusY = mFocusY;
-            mFocusX = detector.getFocusX();
-            mFocusY = detector.getFocusY();
-            float focusDelta = (prevFocusX == 0 && prevFocusY == 0) ? 0 :
-                    FloatMath.sqrt((mFocusX - prevFocusX) * (mFocusX - prevFocusX)
-                                   + (mFocusY - prevFocusY) * (mFocusY - prevFocusY));
-            mFocusMovementQueue.add(focusDelta);
-            float deltaSpan = detector.getCurrentSpan() - detector.getPreviousSpan() +
-                    mAccumulatedSpan;
-            final boolean result = mFocusMovementQueue.getSum() > Math.abs(deltaSpan);
-            if (result) {
-                mAccumulatedSpan += deltaSpan;
-            } else {
-                mAccumulatedSpan = 0;
-            }
-            return result;
-        }
-
-        public boolean handleScale(ScaleGestureDetector detector) {
-            float scale = detector.getScaleFactor() * mActualScale;
-
-            // if scale is limited by any reason, don't zoom but do ask
-            // the detector to update the event.
-            boolean isScaleLimited =
-                    isScaleOverLimits(scale) || scale < getZoomOverviewScale();
-
-            // Prevent scaling beyond overview scale.
-            scale = Math.max(computeScaleWithLimits(scale), getZoomOverviewScale());
-
-            if (mPinchToZoomAnimating || willScaleTriggerZoom(scale)) {
-                mPinchToZoomAnimating = true;
-                // limit the scale change per step
-                if (scale > mActualScale) {
-                    scale = Math.min(scale, mActualScale * 1.25f);
-                } else {
-                    scale = Math.max(scale, mActualScale * 0.8f);
-                }
-                scale = computeScaleWithLimits(scale);
-                // if the scale change is too small, regard it as jitter and skip it.
-                if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_WITHOUT_JITTER) {
-                    return isScaleLimited;
-                }
-                setZoomCenter(detector.getFocusX(), detector.getFocusY());
-                setZoomScale(scale, false);
-                mWebView.invalidate();
-                return true;
-            }
-            return isScaleLimited;
-        }
-
-        public boolean onScale(ScaleGestureDetector detector) {
-            if (isPanningOnly(detector) || handleScale(detector)) {
-                mFocusMovementQueue.clear();
-                return true;
-            }
-            return false;
-        }
-
-        public void onScaleEnd(ScaleGestureDetector detector) {
-            if (mPinchToZoomAnimating) {
-                mPinchToZoomAnimating = false;
-                mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX());
-                mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY());
-                // don't reflow when zoom in; when zoom out, do reflow if the
-                // new scale is almost minimum scale.
-                boolean reflowNow = !canZoomOut() || (mActualScale <= 0.8 * mTextWrapScale);
-                // force zoom after mPreviewZoomOnly is set to false so that the
-                // new view size will be passed to the WebKit
-                refreshZoomScale(reflowNow &&
-                    !mWebView.getSettings().getUseFixedViewport());
-                // call invalidate() to draw without zoom filter
-                mWebView.invalidate();
-            }
-
-            mWebView.mViewManager.endZoom();
-            mWebView.onPinchToZoomAnimationEnd(detector);
-        }
-    }
-
-    private void sanitizeMinMaxScales() {
-        if (mMinZoomScale > mMaxZoomScale) {
-            Log.w(LOGTAG, "mMinZoom > mMaxZoom!!! " + mMinZoomScale + " > " + mMaxZoomScale,
-                    new Exception());
-            mMaxZoomScale = mMinZoomScale;
-        }
-    }
-
-    public void onSizeChanged(int w, int h, int ow, int oh) {
-        // reset zoom and anchor to the top left corner of the screen
-        // unless we are already zooming
-        if (!isFixedLengthAnimationInProgress()) {
-            int visibleTitleHeight = mWebView.getVisibleTitleHeight();
-            mZoomCenterX = 0;
-            mZoomCenterY = visibleTitleHeight;
-            mAnchorX = mWebView.viewToContentX(mWebView.getScrollX());
-            mAnchorY = mWebView.viewToContentY(visibleTitleHeight + mWebView.getScrollY());
-        }
-
-        // update mMinZoomScale if the minimum zoom scale is not fixed
-        if (!mMinZoomScaleFixed) {
-            // when change from narrow screen to wide screen, the new viewWidth
-            // can be wider than the old content width. We limit the minimum
-            // scale to 1.0f. The proper minimum scale will be calculated when
-            // the new picture shows up.
-            mMinZoomScale = Math.min(1.0f, (float) mWebView.getViewWidth()
-                    / (mWebView.drawHistory() ? mWebView.getHistoryPictureWidth()
-                            : mZoomOverviewWidth));
-            // limit the minZoomScale to the initialScale if it is set
-            if (mInitialScale > 0 && mInitialScale < mMinZoomScale) {
-                mMinZoomScale = mInitialScale;
-            }
-            sanitizeMinMaxScales();
-        }
-
-        dismissZoomPicker();
-
-        // onSizeChanged() is called during WebView layout. And any
-        // requestLayout() is blocked during layout. As refreshZoomScale() will
-        // 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.getWebView().post(new PostScale(w != ow &&
-            !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview, w < ow));
-    }
-
-    private class PostScale implements Runnable {
-        final boolean mUpdateTextWrap;
-        // Remember the zoom overview state right after rotation since
-        // it could be changed between the time this callback is initiated and
-        // the time it's actually run.
-        final boolean mInZoomOverviewBeforeSizeChange;
-        final boolean mInPortraitMode;
-
-        public PostScale(boolean updateTextWrap,
-                         boolean inZoomOverview,
-                         boolean inPortraitMode) {
-            mUpdateTextWrap = updateTextWrap;
-            mInZoomOverviewBeforeSizeChange = inZoomOverview;
-            mInPortraitMode = inPortraitMode;
-        }
-
-        public void run() {
-            if (mWebView.getWebViewCore() != null) {
-                // we always force, in case our height changed, in which case we
-                // still want to send the notification over to webkit.
-                // Keep overview mode unchanged when rotating.
-                float newScale = mActualScale;
-                if (mWebView.getSettings().getUseWideViewPort() &&
-                    mInPortraitMode &&
-                    mInZoomOverviewBeforeSizeChange) {
-                    newScale = getZoomOverviewScale();
-                }
-                setZoomScale(newScale, mUpdateTextWrap, true);
-                // update the zoom buttons as the scale can be changed
-                updateZoomPicker();
-            }
-        }
-    }
-
-    public void updateZoomRange(WebViewCore.ViewState viewState,
-            int viewWidth, int minPrefWidth) {
-        if (viewState.mMinScale == 0) {
-            if (viewState.mMobileSite) {
-                if (minPrefWidth > Math.max(0, viewWidth)) {
-                    mMinZoomScale = (float) viewWidth / minPrefWidth;
-                    mMinZoomScaleFixed = false;
-                } else {
-                    mMinZoomScale = viewState.mDefaultScale;
-                    mMinZoomScaleFixed = true;
-                }
-            } else {
-                mMinZoomScale = mDefaultMinZoomScale;
-                mMinZoomScaleFixed = false;
-            }
-        } else {
-            mMinZoomScale = viewState.mMinScale;
-            mMinZoomScaleFixed = true;
-        }
-        if (viewState.mMaxScale == 0) {
-            mMaxZoomScale = mDefaultMaxZoomScale;
-        } else {
-            mMaxZoomScale = viewState.mMaxScale;
-        }
-        sanitizeMinMaxScales();
-    }
-
-    /**
-     * Updates zoom values when Webkit produces a new picture. This method
-     * should only be called from the UI thread's message handler.
-     *
-     * @return True if zoom value has changed
-     */
-    public boolean onNewPicture(WebViewCore.DrawData drawData) {
-        final int viewWidth = mWebView.getViewWidth();
-        final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth);
-        final float newZoomOverviewScale = getZoomOverviewScale();
-        WebSettingsClassic settings = mWebView.getSettings();
-        if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() &&
-            settings.getUseFixedViewport() &&
-            (mInitialZoomOverview || mInZoomOverview)) {
-            // Keep mobile site's text wrap scale unchanged.  For mobile sites,
-            // the text wrap scale is the same as zoom overview scale.
-            if (exceedsMinScaleIncrement(mTextWrapScale, mDefaultScale) ||
-                    exceedsMinScaleIncrement(newZoomOverviewScale, mDefaultScale)) {
-                mTextWrapScale = getReadingLevelScale();
-            } else {
-                mTextWrapScale = newZoomOverviewScale;
-            }
-        }
-
-        if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) {
-            mMinZoomScale = newZoomOverviewScale;
-            mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
-            sanitizeMinMaxScales();
-        }
-        // fit the content width to the current view for the first new picture
-        // after first layout.
-        boolean scaleHasDiff = exceedsMinScaleIncrement(newZoomOverviewScale, mActualScale);
-        // Make sure the actual scale is no less than zoom overview scale.
-        boolean scaleLessThanOverview =
-                (newZoomOverviewScale - mActualScale) >= MINIMUM_SCALE_INCREMENT;
-        // Make sure mobile sites are correctly handled since mobile site will
-        // change content width after rotating.
-        boolean mobileSiteInOverview = mInZoomOverview &&
-                !exceedsMinScaleIncrement(newZoomOverviewScale, mDefaultScale);
-        if (!mWebView.drawHistory() &&
-            ((scaleLessThanOverview && settings.getUseWideViewPort())||
-                ((mInitialZoomOverview || mobileSiteInOverview) &&
-                    scaleHasDiff && zoomOverviewWidthChanged))) {
-            mInitialZoomOverview = false;
-            setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
-                !mWebView.getSettings().getUseFixedViewport());
-        } else {
-            mInZoomOverview = !scaleHasDiff;
-        }
-        if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) {
-            // Set mInitialZoomOverview in case this is the first picture for non standard load,
-            // so next new picture could be forced into overview mode if it's true.
-            mInitialZoomOverview = mInZoomOverview;
-        }
-
-        return scaleHasDiff;
-    }
-
-    /**
-     * Set up correct zoom overview width based on different settings.
-     *
-     * @param drawData webviewcore draw data
-     * @param viewWidth current view width
-     */
-    private boolean setupZoomOverviewWidth(WebViewCore.DrawData drawData, final int viewWidth) {
-        WebSettings settings = mWebView.getSettings();
-        int newZoomOverviewWidth = mZoomOverviewWidth;
-        if (settings.getUseWideViewPort()) {
-            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(WebViewClassic.sMaxViewportWidth,
-                    drawData.mContentSize.x);
-            }
-        } else {
-            // If not use wide viewport, use view width as the zoom overview width.
-            newZoomOverviewWidth = Math.round(viewWidth / mDefaultScale);
-        }
-        if (newZoomOverviewWidth != mZoomOverviewWidth) {
-            setZoomOverviewWidth(newZoomOverviewWidth);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Updates zoom values after Webkit completes the initial page layout. It
-     * is called when visiting a page for the first time as well as when the
-     * user navigates back to a page (in which case we may need to restore the
-     * zoom levels to the state they were when you left the page). This method
-     * should only be called from the UI thread's message handler.
-     */
-    public void onFirstLayout(WebViewCore.DrawData drawData) {
-        // precondition check
-        assert drawData != null;
-        assert drawData.mViewState != null;
-        assert mWebView.getSettings() != null;
-
-        WebViewCore.ViewState viewState = drawData.mViewState;
-        final Point viewSize = drawData.mViewSize;
-        updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
-        setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
-        final float overviewScale = getZoomOverviewScale();
-        WebSettingsClassic settings = mWebView.getSettings();
-        if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) {
-            mMinZoomScale = (mInitialScale > 0) ?
-                    Math.min(mInitialScale, overviewScale) : overviewScale;
-            mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
-            sanitizeMinMaxScales();
-        }
-
-        if (!mWebView.drawHistory()) {
-            float scale;
-            if (mInitialScale > 0) {
-                scale = mInitialScale;
-            } else if (viewState.mIsRestored || viewState.mViewScale > 0) {
-                scale = (viewState.mViewScale > 0)
-                    ? viewState.mViewScale : overviewScale;
-                mTextWrapScale = (viewState.mTextWrapScale > 0)
-                    ? viewState.mTextWrapScale : getReadingLevelScale();
-            } else {
-                scale = overviewScale;
-                if (!settings.getUseWideViewPort()
-                    || !settings.getLoadWithOverviewMode()) {
-                    scale = Math.max(mDefaultScale, scale);
-                }
-                if (settings.isNarrowColumnLayout() &&
-                    settings.getUseFixedViewport()) {
-                    // When first layout, reflow using the reading level scale to avoid
-                    // reflow when double tapped.
-                    mTextWrapScale = getReadingLevelScale();
-                }
-            }
-            boolean reflowText = false;
-            if (!viewState.mIsRestored) {
-                if (settings.getUseFixedViewport()) {
-                    // Override the scale only in case of fixed viewport.
-                    scale = Math.max(scale, overviewScale);
-                    mTextWrapScale = Math.max(mTextWrapScale, overviewScale);
-                }
-                reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale);
-            }
-            mInitialZoomOverview = settings.getLoadWithOverviewMode() &&
-                    !exceedsMinScaleIncrement(scale, overviewScale);
-            setZoomScale(scale, reflowText);
-
-            // update the zoom buttons as the scale can be changed
-            updateZoomPicker();
-        }
-    }
-
-    public void saveZoomState(Bundle b) {
-        b.putFloat("scale", mActualScale);
-        b.putFloat("textwrapScale", mTextWrapScale);
-        b.putBoolean("overview", mInZoomOverview);
-    }
-
-    public void restoreZoomState(Bundle b) {
-        // as getWidth() / getHeight() of the view are not available yet, set up
-        // mActualScale, so that when onSizeChanged() is called, the rest will
-        // be set correctly
-        mActualScale = b.getFloat("scale", 1.0f);
-        mInvActualScale = 1 / mActualScale;
-        mTextWrapScale = b.getFloat("textwrapScale", mActualScale);
-        mInZoomOverview = b.getBoolean("overview");
-    }
-
-    private ZoomControlBase getCurrentZoomControl() {
-        if (mWebView.getSettings() != null && mWebView.getSettings().supportZoom()) {
-            if (mWebView.getSettings().getBuiltInZoomControls()) {
-                if ((mEmbeddedZoomControl == null)
-                        && mWebView.getSettings().getDisplayZoomControls()) {
-                    mEmbeddedZoomControl = new ZoomControlEmbedded(this, mWebView);
-                }
-                return mEmbeddedZoomControl;
-            } else {
-                if (mExternalZoomControl == null) {
-                    mExternalZoomControl = new ZoomControlExternal(mWebView);
-                }
-                return mExternalZoomControl;
-            }
-        }
-        return null;
-    }
-
-    public void invokeZoomPicker() {
-        ZoomControlBase control = getCurrentZoomControl();
-        if (control != null) {
-            control.show();
-        }
-    }
-
-    public void dismissZoomPicker() {
-        ZoomControlBase control = getCurrentZoomControl();
-        if (control != null) {
-            control.hide();
-        }
-    }
-
-    public boolean isZoomPickerVisible() {
-        ZoomControlBase control = getCurrentZoomControl();
-        return (control != null) ? control.isVisible() : false;
-    }
-
-    public void updateZoomPicker() {
-        ZoomControlBase control = getCurrentZoomControl();
-        if (control != null) {
-            control.update();
-        }
-    }
-
-    /**
-     * The embedded zoom control intercepts touch events and automatically stays
-     * visible. The external control needs to constantly refresh its internal
-     * timer to stay visible.
-     */
-    public void keepZoomPickerVisible() {
-        ZoomControlBase control = getCurrentZoomControl();
-        if (control != null && control == mExternalZoomControl) {
-            control.show();
-        }
-    }
-
-    public View getExternalZoomPicker() {
-        ZoomControlBase control = getCurrentZoomControl();
-        if (control != null && control == mExternalZoomControl) {
-            return mExternalZoomControl.getControls();
-        } else {
-            return null;
-        }
-    }
-
-    public void setHardwareAccelerated() {
-        mHardwareAccelerated = true;
-    }
-
-    /**
-     * OnPageFinished called by webview when a page is fully loaded.
-     */
-    /* package*/ void onPageFinished(String url) {
-        // Turn off initial zoom overview flag when a page is fully loaded.
-        mInitialZoomOverview = false;
-    }
-}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 4614c53..01ac8fd 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.IntProperty;
 import android.util.MathUtils;
@@ -176,6 +177,9 @@
      */
     private int mState;
 
+    /** Whether the preview image is visible. */
+    private boolean mShowingPreview;
+
     private BaseAdapter mListAdapter;
     private SectionIndexer mSectionIndexer;
 
@@ -769,6 +773,8 @@
         mDecorAnimation = new AnimatorSet();
         mDecorAnimation.playTogether(fadeOut, slideOut);
         mDecorAnimation.start();
+
+        mShowingPreview = false;
     }
 
     /**
@@ -790,6 +796,8 @@
         mDecorAnimation = new AnimatorSet();
         mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
         mDecorAnimation.start();
+
+        mShowingPreview = false;
     }
 
     /**
@@ -809,6 +817,8 @@
         mDecorAnimation = new AnimatorSet();
         mDecorAnimation.playTogether(fadeIn, slideIn);
         mDecorAnimation.start();
+
+        mShowingPreview = true;
     }
 
     private void postAutoHide() {
@@ -982,9 +992,10 @@
         if (mCurrentSection != sectionIndex) {
             mCurrentSection = sectionIndex;
 
-            if (transitionPreviewLayout(sectionIndex)) {
+            final boolean hasPreview = transitionPreviewLayout(sectionIndex);
+            if (!mShowingPreview && hasPreview) {
                 transitionToDragging();
-            } else {
+            } else if (mShowingPreview && !hasPreview) {
                 transitionToVisible();
             }
         }
@@ -1072,7 +1083,7 @@
 
         mPreviewAnimation.start();
 
-        return (text != null && text.length() > 0);
+        return !TextUtils.isEmpty(text);
     }
 
     /**
@@ -1184,7 +1195,19 @@
                     / positionsInSection;
         }
 
-        return (section + posWithinSection) / sectionCount;
+        float result = (section + posWithinSection) / sectionCount;
+
+        // Fake out the scroll bar for the last item. Since the section indexer
+        // won't ever actually move the list in this end space, make scrolling
+        // across the last item account for whatever space is remaining.
+        if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) {
+            final View lastChild = mList.getChildAt(visibleItemCount - 1);
+            final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom()
+                    - lastChild.getTop()) / lastChild.getHeight();
+            result += (1 - result) * lastItemVisible;
+        }
+
+        return result;
     }
 
     /**
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 061bb00..b152297 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -90,7 +90,7 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_USER_PRESENT);
-        getContext().registerReceiver(mReceiver, filter);
+        getContext().registerReceiver(mReceiver, filter, null, mHandler);
 
         if (mAutoStart) {
             // Automatically start when requested
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 430e43a..222e446 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -2918,29 +2918,38 @@
             }
         }
 
-        public void clearCurrentOwner(Object owner) {
+        public void clearCurrentOwner(Object owner, boolean silently) {
             if (mOwner == owner) {
-                mOwner = null;
                 mProc.decActiveServices(mName);
                 if (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
                         || mExecState != STATE_NOTHING) {
                     long now = SystemClock.uptimeMillis();
                     if (mStartedState != STATE_NOTHING) {
-                        Slog.wtfStack(TAG, "Service owner " + owner + " cleared while started: pkg="
-                                + mPackage + " service=" + mName + " proc=" + mProc);
+                        if (!silently) {
+                            Slog.wtfStack(TAG, "Service owner " + owner
+                                    + " cleared while started: pkg=" + mPackage + " service="
+                                    + mName + " proc=" + mProc);
+                        }
                         setStarted(false, 0, now);
                     }
                     if (mBoundState != STATE_NOTHING) {
-                        Slog.wtfStack(TAG, "Service owner " + owner + " cleared while bound: pkg="
-                                + mPackage + " service=" + mName + " proc=" + mProc);
+                        if (!silently) {
+                            Slog.wtfStack(TAG, "Service owner " + owner
+                                    + " cleared while bound: pkg=" + mPackage + " service="
+                                    + mName + " proc=" + mProc);
+                        }
                         setBound(false, 0, now);
                     }
                     if (mExecState != STATE_NOTHING) {
-                        Slog.wtfStack(TAG, "Service owner " + owner + " cleared while exec: pkg="
-                                + mPackage + " service=" + mName + " proc=" + mProc);
+                        if (!silently) {
+                            Slog.wtfStack(TAG, "Service owner " + owner
+                                    + " cleared while exec: pkg=" + mPackage + " service="
+                                    + mName + " proc=" + mProc);
+                        }
                         setExecuting(false, 0, now);
                     }
                 }
+                mOwner = null;
             }
         }
 
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 45a38be..63ff5a0 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -43,4 +43,5 @@
     oneway void showAssistant();
     oneway void dispatch(in MotionEvent event);
     oneway void launchCamera();
+    oneway void onBootCompleted();
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index e09fcff..5983120 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -60,7 +60,6 @@
 	android_text_AndroidCharacter.cpp \
 	android_text_AndroidBidi.cpp \
 	android_os_Debug.cpp \
-	android_os_FileUtils.cpp \
 	android_os_MemoryFile.cpp \
 	android_os_MessageQueue.cpp \
 	android_os_Parcel.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8518101..09577da 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -142,7 +142,6 @@
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
 extern int register_android_os_FileObserver(JNIEnv *env);
-extern int register_android_os_FileUtils(JNIEnv *env);
 extern int register_android_os_UEventObserver(JNIEnv* env);
 extern int register_android_os_MemoryFile(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
@@ -1169,7 +1168,6 @@
     REG_JNI(register_android_database_SQLiteDebug),
     REG_JNI(register_android_os_Debug),
     REG_JNI(register_android_os_FileObserver),
-    REG_JNI(register_android_os_FileUtils),
     REG_JNI(register_android_os_MessageQueue),
     REG_JNI(register_android_os_SELinux),
     REG_JNI(register_android_os_Trace),
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
deleted file mode 100644
index d1245da..0000000
--- a/core/jni/android_os_FileUtils.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* //device/libs/android_runtime/android_util_Process.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "FileUtils"
-
-#include <utils/Log.h>
-
-#include <android_runtime/AndroidRuntime.h>
-
-#include "JNIHelp.h"
-
-#include <sys/errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <linux/msdos_fs.h>
-
-namespace android {
-
-jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
-{
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return -1;
-    }
-    const char *pathStr = env->GetStringUTFChars(path, NULL);
-    int result = -1;
-    // only if our system supports this ioctl
-    #ifdef VFAT_IOCTL_GET_VOLUME_ID
-    int fd = open(pathStr, O_RDONLY);
-    if (fd >= 0) {
-        result = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID);
-        close(fd);
-    }
-    #endif
-
-    env->ReleaseStringUTFChars(path, pathStr);
-    return result;
-}
-
-static const JNINativeMethod methods[] = {
-    {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
-};
-
-static const char* const kFileUtilsPathName = "android/os/FileUtils";
-
-int register_android_os_FileUtils(JNIEnv* env)
-{
-    return AndroidRuntime::registerNativeMethods(
-        env, kFileUtilsPathName,
-        methods, NELEM(methods));
-}
-
-}
diff --git a/core/res/res/layout/input_method.xml b/core/res/res/layout/input_method.xml
index 00a3990..e50da00 100644
--- a/core/res/res/layout/input_method.xml
+++ b/core/res/res/layout/input_method.xml
@@ -23,6 +23,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
+    android:gravity="start|bottom"
     >
 
     <LinearLayout
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 1dfb176..1a9acd3 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Probeer weer oor <xliff:g id="COUNT">%d</xliff:g> sekondes"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Probeer later weer"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Sleep van bo af na onder om volskerm te verlaat"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Sleep van bo af na onder om volskerm te verlaat."</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index e44a56e..e64b6f5 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"በ<xliff:g id="COUNT">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"ቆይተው እንደገና ይሞክሩ"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"ከሙሉ ገጽ ማያ ለመውጣት ከላይ ወደታች ጣትዎን ያንቀሳቅሱ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"ከሙሉ ገጽ ማያ ለመውጣት ጣትዎን ከላይ ወደታች ያንሸራትቱ።"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 489701d..2d91e28 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"أعد المحاولة خلال <xliff:g id="COUNT">%d</xliff:g> ثانية"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"أعد المحاولة لاحقًا"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"التمرير من أعلى لأسفل للخروج من وضع ملء الشاشة"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"مرر بسرعة من أعلى لأسفل للخروج من وضع ملء الشاشة."</string>
 </resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 2f0781c..7772887 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -1584,6 +1584,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> saniyə sonra təkrar yoxlayın"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Daha sonra yenidən yoxlayın."</string>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Paneli göstərmək üçün ekranın küncünü sürüşdürün"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Sistem panelini göstərmək üçün ekranın küncündən sürüşdürün"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Paneli göstərmək üçün ekranın küncünü sürüşdürün"</string>
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 0ae4dda..82bf400 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1579,6 +1579,5 @@
     <item quantity="one" msgid="4835639969503729874">"Yanlış PİN. 1 saniyə sonra təkrar sınayın."</item>
     <item quantity="other" msgid="8030607343223287654">"Yanlış PİN. <xliff:g id="COUNT">%d</xliff:g> saniyə sonra təkrar sınayın."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Paneli göstərmək üçün ekranın küncünü sürüşdürün"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Sistem panelini göstərmək üçün ekranın küncündən sürüşdürün"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Paneli göstərmək üçün ekranın küncünü sürüşdürün"</string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e59c612..ccc2986 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1795,6 +1795,7 @@
     <!-- no translation found for restr_pin_countdown:other (4730868920742952817) -->
     <!-- no translation found for restr_pin_try_later (973144472490532377) -->
     <skip />
-    <!-- no translation found for transient_navigation_confirmation (8554991488096662508) -->
+    <!-- no translation found for immersive_mode_confirmation (7227416894979047467) -->
+    <!-- no translation found for immersive_mode_confirmation (8554991488096662508) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 9400fbf..99ba922 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Опитайте отново след <xliff:g id="COUNT">%d</xliff:g> секунди"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Опитайте отново по-късно"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"За изх. от цял екр. прeк. пръст отгоре надолу"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"За изход от цял екран прекарайте пръст отгоре надолу."</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 80fbe75..8b46e84 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -440,7 +440,7 @@
     <string name="permlab_readContacts" msgid="8348481131899886131">"lectura dels contactes"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permet que l\'aplicació llegeixi dades sobre els contactes que tinguis emmagatzemats a la tauleta, inclosa la freqüència amb què has trucat, has enviat correus electrònics o t\'has comunicat d\'altres maneres amb persones concretes. Aquest permís permet que les aplicacions desin les dades dels teus contactes, i és possible que les aplicacions malicioses comparteixin dades dels contactes sense el teu coneixement."</string>
     <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permet que l\'aplicació llegeixi dades sobre els contactes que tinguis emmagatzemats al telèfon, inclosa la freqüència amb què has trucat, has enviat correus electrònics o t\'has comunicat d\'altres maneres amb persones concretes. Aquest permís permet que les aplicacions desin les dades dels teus contactes, i és possible que les aplicacions malicioses comparteixin dades dels contactes sense el teu coneixement."</string>
-    <string name="permlab_writeContacts" msgid="5107492086416793544">"modificació dels contactes"</string>
+    <string name="permlab_writeContacts" msgid="5107492086416793544">"modificar els teus contactes"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permet que l\'aplicació modifiqui les dades sobre contactes emmagatzemades a la tauleta, inclosa la freqüència amb què has trucat, has enviat correus electrònics o t\'has comunicat d\'altres maneres amb contactes concrets. Aquest permís permet que les aplicacions suprimeixin dades de contactes."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permet que l\'aplicació modifiqui les dades sobre contactes emmagatzemades al telèfon, inclosa la freqüència amb què has trucat, has enviat correus electrònics o t\'has comunicat d\'altres maneres amb contactes concrets. Aquest permís permet que les aplicacions suprimeixin dades de contactes."</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"lectura del registre de trucades"</string>
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Torna-ho a provar d\'aquí a <xliff:g id="COUNT">%d</xliff:g> segons"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Torna-ho a provar més tard"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Fes llis. dit avall per sortir de pant. comp."</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Fes lliscar el dit cap avall per sortir de la pantalla completa."</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cbb0d9e4a2..73ca02b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Zkuste to znovu za <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Zkuste to znovu později"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Režim celé obrazovky ukončíte přejetím dolů"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Režim celé obrazovky ukončíte přejetím dolů."</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8213c87..0cf5bc5 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Prøv igen om <xliff:g id="COUNT">%d</xliff:g> sekunder"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Prøv igen senere"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Stryg ned fra toppen for at stoppe fuld skærm"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Stryg ned fra toppen for at afslutte fuld skærm"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index fca13d5..f799be4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"In <xliff:g id="COUNT">%d</xliff:g> Sek. wiederholen"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Später erneut versuchen"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Zum Schließen des Vollbilds von oben nach unten wischen"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Zum Schließen des Vollbilds von oben nach unten wischen"</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6f07768..605d845 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Επανάληψη σε <xliff:g id="COUNT">%d</xliff:g> δευτ."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Δοκιμάστε ξανά αργότερα"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Σάρωση προς τα κάτω για έξοδο από πλήρη οθόνη"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Σάρωση προς τα κάτω για έξοδο από πλήρη οθόνη"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 98db3f8..ca8509a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Try again in <xliff:g id="COUNT">%d</xliff:g> seconds"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Try again later"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Swipe down from the top to exit full screen"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Swipe down from the top to exit full screen."</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 98db3f8..ca8509a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Try again in <xliff:g id="COUNT">%d</xliff:g> seconds"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Try again later"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Swipe down from the top to exit full screen"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Swipe down from the top to exit full screen."</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 150774b..a7b512d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Intentar en <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Vuelve a intentar más tarde."</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Salir de pantalla completa: deslizar abajo"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Desliza el dedo hacia abajo para salir de la pantalla completa."</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 900a8ad..a89f35a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Inténtalo en <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Volver a intentar más tarde"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Desliza hacia abajo para salir de la pantalla completa"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Desliza el dedo hacia abajo para salir de la pantalla completa"</string>
 </resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index e08dd4a..066694d 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Proovige uuesti <xliff:g id="COUNT">%d</xliff:g> sekundi pärast"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Proovige hiljem uuesti"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Täisekraani sulgemiseks pühkige ülevalt alla"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Täisekraanilt väljumiseks pühkige ülevalt alla."</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 0694744..ec0d930 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"Vale PIN-kood. Proovige 1 s pärast."</item>
     <item quantity="other" msgid="8030607343223287654">"Vale PIN-kood. Proovige <xliff:g id="COUNT">%d</xliff:g> s pärast."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Riba kuvam. pühkige ekraani serva"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Süsteemiriba kuvamiseks pühkige ekraani servast"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Riba kuvam. pühkige ekraani serva"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index e42fa5d..61112f2 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"امتحان پس از <xliff:g id="COUNT">%d</xliff:g> ثانیه"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"بعداً دوباره امتحان کنید"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"برای خروج از تمام صفحه از بالا به پایین بکشید"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"برای خروج از حالت تمام صفحه، انگشت خود را به تندی از بالای صفحه به پایین بکشید."</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c258cbb..3130cba 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Yritä uud. <xliff:g id="COUNT">%d</xliff:g> s kul."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Yritä myöhemmin uudelleen"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Poistu koko näytön tilasta liu\'uttamalla alas"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Poistu koko näytön tilasta liu\'uttamalla alas."</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 8078aaf..ad27a23 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Réessayer dans <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Réessayez plus tard"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Balayez vers le bas pour quitter plein écran"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Balayez vers le bas pour quitter le mode plein écran"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9bfefbd..0953fe3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Réessayer dans <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Veuillez réessayer ultérieurement."</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Balayer vers le bas pour quitter le plein écran"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Faites glisser le doigt vers le bas pour quitter le mode plein écran."</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index efad925..9426c2d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -205,8 +205,8 @@
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"चित्र या वीडियो कैप्‍चर के लिए कैमरे पर सीधी पहुंच."</string>
     <string name="permgrouplab_screenlock" msgid="8275500173330718168">"स्‍क्रीन लॉक करें"</string>
     <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"आपके उपकरण की लॉक स्क्रीन का व्यवहार प्रभावित करने की क्षमता."</string>
-    <string name="permgrouplab_appInfo" msgid="8028789762634147725">"आपके एप्‍स की जानकारी"</string>
-    <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"अपने उपकरण पर अन्‍य एप्‍स के व्‍यवहार को प्रभावित करने की क्षमता."</string>
+    <string name="permgrouplab_appInfo" msgid="8028789762634147725">"आपके ऐप्स की जानकारी"</string>
+    <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"अपने उपकरण पर अन्‍य ऐप्स के व्‍यवहार को प्रभावित करने की क्षमता."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"वॉलपेपर"</string>
     <string name="permgroupdesc_wallpaper" msgid="5630417854750540154">"उपकरण की वॉलपेपर सेटिंग बदलें."</string>
     <string name="permgrouplab_systemClock" msgid="406535759236612992">"घड़ी"</string>
@@ -225,8 +225,8 @@
     <string name="permgroupdesc_systemTools" msgid="8162102602190734305">"सिस्‍टम का निम्‍न-स्‍तर पहुंच और नियंत्रण."</string>
     <string name="permgrouplab_developmentTools" msgid="3446164584710596513">"डेवलपमेंट टूल"</string>
     <string name="permgroupdesc_developmentTools" msgid="7058828032358142018">"सुविधाएं जो केवल एप्स डेवलपर के लिए आवश्यक हैं."</string>
-    <string name="permgrouplab_display" msgid="4279909676036402636">"अन्‍य एप्‍स UI"</string>
-    <string name="permgroupdesc_display" msgid="6051002031933013714">"अन्‍य एप्‍स के UI को प्रभावित करें."</string>
+    <string name="permgrouplab_display" msgid="4279909676036402636">"अन्‍य ऐप्स UI"</string>
+    <string name="permgroupdesc_display" msgid="6051002031933013714">"अन्‍य ऐप्स के UI को प्रभावित करें."</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"संग्रहण"</string>
     <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"USB संग्रहण में पहुंचें."</string>
     <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD कार्ड में पहुंचें."</string>
@@ -243,7 +243,7 @@
     <string name="permlab_statusBar" msgid="7417192629601890791">"स्‍थिति बार अक्षम या बदलें"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"एप्स को स्थिति बार अक्षम करने या सिस्‍टम आइकन को जोड़ने या निकालने देता है."</string>
     <string name="permlab_statusBarService" msgid="7247281911387931485">"स्‍थिति बार"</string>
-    <string name="permdesc_statusBarService" msgid="716113660795976060">"एप्‍स को स्‍थिति बार होने देता है."</string>
+    <string name="permdesc_statusBarService" msgid="716113660795976060">"ऐप्स को स्‍थिति बार होने देता है."</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"स्‍थिति बार विस्‍तृत/संक्षिप्त करें"</string>
     <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"एप्स को स्थिति बार को विस्तृत या संक्षिप्त करने देता है."</string>
     <string name="permlab_install_shortcut" msgid="4279070216371564234">"शॉर्टकट इंस्‍टॉल करें"</string>
@@ -251,7 +251,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट अनइंस्टॉल करें"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"एप्‍लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना होमस्‍क्रीन शॉर्टकट निकालने की अनुमति देता है."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"आउटगोइंग कॉल को कहीं और भेजें"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"एप्‍स को आउटगोइंग कॉल संसाधित करने और डायल किए जाने वाला नंबर बदलने देता है. यह अनुमति एप्‍स को आउटगोइंग कॉल की निगरानी करने, रीडायरेक्‍ट करने, या उन्‍हें रोकने देती है."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"ऐप्स को आउटगोइंग कॉल संसाधित करने और डायल किए जाने वाला नंबर बदलने देता है. यह अनुमति ऐप्स को आउटगोइंग कॉल की निगरानी करने, रीडायरेक्‍ट करने, या उन्‍हें रोकने देती है."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"पाठ संदेश (SMS) प्राप्त करें"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"एप्स को SMS संदेशों को प्राप्‍त और संसाधित करने देता है. इसका अर्थ है कि एप्स आपके उपकरण पर भेजे गए संदेशों की निगरानी आपको दिखाए बिना कर सकता है और उन्‍हें हटा सकता है."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"पाठ संदेश (MMS) प्राप्त करें"</string>
@@ -259,7 +259,7 @@
     <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"आपातकालीन प्रसारण प्राप्त करें"</string>
     <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"एप्स को आपातकालीन प्रसारण संदेशों को प्राप्त करने और संसाधित करने देता है. यह अनुमति केवल सिस्टम एप्स में उपलब्ध है."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण संदेश पढ़ें"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"एप्‍स को आपके उपकरण द्वारा प्राप्त सेल प्रसारण संदेशों को पढ़ने देता है. कुछ स्‍थानों पर आपको आपातकालीन स्‍थितियों की चेतावनी देने के लिए सेल प्रसारण अलर्ट वितरित किए जाते हैं. आपातकालीन सेल प्रसारण प्राप्त होने पर दुर्भावनापूर्ण एप्‍स आपके उपकरण के निष्‍पादन या संचालन में हस्‍तक्षेप कर सकते हैं."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ऐप्स को आपके उपकरण द्वारा प्राप्त सेल प्रसारण संदेशों को पढ़ने देता है. कुछ स्‍थानों पर आपको आपातकालीन स्‍थितियों की चेतावनी देने के लिए सेल प्रसारण अलर्ट वितरित किए जाते हैं. आपातकालीन सेल प्रसारण प्राप्त होने पर दुर्भावनापूर्ण ऐप्स आपके उपकरण के निष्‍पादन या संचालन में हस्‍तक्षेप कर सकते हैं."</string>
     <string name="permlab_sendSms" msgid="5600830612147671529">"SMS संदेश भेजें"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"एप्स को SMS संदेशों को भेजने देता है. इसके परिणामस्वरूप अप्रत्‍याशित शुल्‍क लागू हो सकते हैं. दुर्भावनापूर्ण एप्स आपकी पुष्टि के बिना संदेश भेजकर आपका धन व्‍यय कर सकते हैं."</string>
     <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"संदेश-द्वारा-जवाब भेजें ईवेंट"</string>
@@ -272,7 +272,7 @@
     <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"एप्स को आपके फ़ोन या सिम कार्ड में संग्रहीत SMS संदेशों को लिखने देता है.  दुर्भावनापूर्ण एप्स आपके संदेशों को हटा सकते हैं."</string>
     <string name="permlab_receiveWapPush" msgid="5991398711936590410">"पाठ संदेश (WAP) प्राप्त करें"</string>
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"एप्स को WAP संदेशों को प्राप्‍त और संसाधित करने देता है. इस अनुमति में आपको भेजे गए संदेशों की निगरानी आपको दिखाए बिना करने और हटाने की क्षमता शामिल है."</string>
-    <string name="permlab_getTasks" msgid="6466095396623933906">"चल रहे एप्‍स पुनर्प्राप्त करें"</string>
+    <string name="permlab_getTasks" msgid="6466095396623933906">"चल रहे ऐप्स पुनर्प्राप्त करें"</string>
     <string name="permdesc_getTasks" msgid="7454215995847658102">"एप्स को वर्तमान में और हाल ही में चल रहे कार्यों के बारे में जानकारी को पुन: प्राप्‍त करने देता है. इससे एप्स उपकरण पर उपयोग किए गए एप्स के बारे में जानकारी खोज सकता है."</string>
     <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"उपयोगकर्ताओं के बीच सहभागिता करें"</string>
     <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"एप्स को उपकरण पर भिन्न उपयोगकर्ताओं के बीच कार्य निष्पादित करने देता है. दुर्भावनापूर्ण एप्स उपयोगकर्ताओं के बीच सुरक्षा का उल्लंघन करने के लिए इसका उपयोग कर सकते हैं."</string>
@@ -280,49 +280,49 @@
     <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"उपयोगकर्ताओं के बीच सभी संभव सहभागिता करने देता है."</string>
     <string name="permlab_manageUsers" msgid="1676150911672282428">"उपयोगकर्ता प्रबंधित करें"</string>
     <string name="permdesc_manageUsers" msgid="8409306667645355638">"एप्स को उपकरण पर क्वेरी, निर्माण और हटाने सहित उपयोगकर्ताओं को प्रबंधित करने की सुविधा देता है."</string>
-    <string name="permlab_getDetailedTasks" msgid="6229468674753529501">"चल रहे एप्‍स के विवरण प्राप्त करें"</string>
+    <string name="permlab_getDetailedTasks" msgid="6229468674753529501">"चल रहे ऐप्स के विवरण प्राप्त करें"</string>
     <string name="permdesc_getDetailedTasks" msgid="153824741440717599">"एप्स को वर्तमान में और हाल ही में चल रहे कार्यों की जानकारी प्राप्त करने देता है. दुर्भावनापूर्ण एप्स अन्य एप्स के बारे में निजी जानकारी खोज सकते हैं."</string>
-    <string name="permlab_reorderTasks" msgid="2018575526934422779">"चल रहे एप्‍स पुन: क्रमित करें"</string>
+    <string name="permlab_reorderTasks" msgid="2018575526934422779">"चल रहे ऐप्स पुन: क्रमित करें"</string>
     <string name="permdesc_reorderTasks" msgid="7734217754877439351">"एप्स को कार्यों को अग्रभूमि और पृष्‍ठभूमि पर ले जाने देता है. एप्स आपके इनपुट के बिना यह कर सकता है."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"चलने वाले एप्स रोकें"</string>
-    <string name="permdesc_removeTasks" msgid="1394714352062635493">"किसी एप्‍स को कार्यों को निकालने और उनके एप्‍स समाप्त करने देता है. दुर्भावनापूर्ण एप्‍स अन्‍य एप्‍स का व्‍यवहार बाधित कर सकते हैं."</string>
+    <string name="permdesc_removeTasks" msgid="1394714352062635493">"किसी ऐप्स को कार्यों को निकालने और उनके ऐप्स समाप्त करने देता है. दुर्भावनापूर्ण ऐप्स अन्‍य ऐप्स का व्‍यवहार बाधित कर सकते हैं."</string>
     <string name="permlab_manageActivityStacks" msgid="7391191384027303065">"गतिविधि स्टैक प्रबंधित करें"</string>
     <string name="permdesc_manageActivityStacks" msgid="1615881933034084440">"एप्स को ऐसे गतिविधि स्टैक जोड़ने, निकालने, और बदलने देता है जिनमें अन्य एप्स चलते हों. दुर्भावनापूर्ण एप्स अन्य एप्स के व्यवहार में बाधा डाल सकते हैं."</string>
     <string name="permlab_startAnyActivity" msgid="2918768238045206456">"कोई गतिविधि प्रारंभ करें"</string>
-    <string name="permdesc_startAnyActivity" msgid="997823695343584001">"अनुमति सुरक्षा या निर्यात की स्‍थिति पर ध्‍यान दिए बिना, एप्‍स को कोई गतिविधि प्रारंभ करने देता है."</string>
+    <string name="permdesc_startAnyActivity" msgid="997823695343584001">"अनुमति सुरक्षा या निर्यात की स्‍थिति पर ध्‍यान दिए बिना, ऐप्स को कोई गतिविधि प्रारंभ करने देता है."</string>
     <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"स्‍क्रीन संगतता सेट करें"</string>
-    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"एप्‍स को अन्‍य एप्‍स के स्‍क्रीन संगतता मोड को नियंत्रित करने देता है. दुर्भावनापूर्ण एप्‍स अन्‍य एप्‍स का व्‍यवहार बाधित कर सकते हैं."</string>
-    <string name="permlab_setDebugApp" msgid="3022107198686584052">"एप्‍स डीबग करना सक्षम करें"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"ऐप्स को अन्‍य ऐप्स के स्‍क्रीन संगतता मोड को नियंत्रित करने देता है. दुर्भावनापूर्ण ऐप्स अन्‍य ऐप्स का व्‍यवहार बाधित कर सकते हैं."</string>
+    <string name="permlab_setDebugApp" msgid="3022107198686584052">"ऐप्स डीबग करना सक्षम करें"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"एप्स को अन्य एप्स के लिए डीबग किया जाना चालू करने देता है. दुर्भावनापूर्ण एप्स इसका उपयोग अन्य एप्स को समाप्त करने के लिए कर सकते हैं."</string>
     <string name="permlab_changeConfiguration" msgid="4162092185124234480">"सिस्‍टम प्रदर्शन सेटिंग बदलें"</string>
     <string name="permdesc_changeConfiguration" msgid="4372223873154296076">"एप्स को वर्तमान कॉन्फ़िगरेशन, जैसे स्थान या समग्र अक्षरों का आकार, बदलने देता है."</string>
     <string name="permlab_enableCarMode" msgid="5684504058192921098">"कार मोड सक्षम करें"</string>
     <string name="permdesc_enableCarMode" msgid="4853187425751419467">"एप्स को कार मोड सक्षम करने देता है."</string>
-    <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्‍य एप्‍स बंद करें"</string>
-    <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"एप्स को अन्‍य एप्‍स की पृष्ठभूमि प्रक्रियाओं को समाप्त करने देता है. यह अन्य एप्स का चलना रोक सकता है."</string>
-    <string name="permlab_forceStopPackages" msgid="2329627428832067700">"अन्‍य एप्‍स बलपूर्वक बंद करें"</string>
+    <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्‍य ऐप्स बंद करें"</string>
+    <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"एप्स को अन्‍य ऐप्स की पृष्ठभूमि प्रक्रियाओं को समाप्त करने देता है. यह अन्य एप्स का चलना रोक सकता है."</string>
+    <string name="permlab_forceStopPackages" msgid="2329627428832067700">"अन्‍य ऐप्स बलपूर्वक बंद करें"</string>
     <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"एप्स को अन्य एप्स बलपूर्वक बंद करने देता है."</string>
-    <string name="permlab_forceBack" msgid="652935204072584616">"एप्‍स को बलपूर्वक बंद करें"</string>
-    <string name="permdesc_forceBack" msgid="3892295830419513623">"एप्स को अग्रभूमि में चल रही कोई भी गतिविधि बलपूर्वक बंद करने और वापस जाने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permlab_forceBack" msgid="652935204072584616">"ऐप्स को बलपूर्वक बंद करें"</string>
+    <string name="permdesc_forceBack" msgid="3892295830419513623">"एप्स को अग्रभूमि में चल रही कोई भी गतिविधि बलपूर्वक बंद करने और वापस जाने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_dump" msgid="1681799862438954752">"सिस्‍टम की आंतरिक स्‍थिति पुनर्प्राप्त करें"</string>
-    <string name="permdesc_dump" msgid="1778299088692290329">"एप्‍स को सिस्‍टम की आंतरिक स्‍थिति पुनर्प्राप्त करने देता है. दुर्भावनापूर्ण एप्‍स विभिन्‍न प्रकार की निजी और सुरक्षा जानकारी प्राप्त कर सकते हैं जिनकी उन्‍हें सामान्‍यत: आवश्‍यकता नहीं होती."</string>
+    <string name="permdesc_dump" msgid="1778299088692290329">"ऐप्स को सिस्‍टम की आंतरिक स्‍थिति पुनर्प्राप्त करने देता है. दुर्भावनापूर्ण ऐप्स विभिन्‍न प्रकार की निजी और सुरक्षा जानकारी प्राप्त कर सकते हैं जिनकी उन्‍हें सामान्‍यत: आवश्‍यकता नहीं होती."</string>
     <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"स्‍क्रीन सामग्री पुनर्प्राप्त करें"</string>
-    <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"एप्‍स को सक्रिय विंडो की सामग्री पुनर्प्राप्त करने देता है. दुर्भावनापूर्ण एप्‍स विंडो की संपूर्ण सामग्री प्राप्त कर सकते हैं और पासवर्ड को छोड़कर इसके सभी पाठ जांच सकते हैं."</string>
+    <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"ऐप्स को सक्रिय विंडो की सामग्री पुनर्प्राप्त करने देता है. दुर्भावनापूर्ण ऐप्स विंडो की संपूर्ण सामग्री प्राप्त कर सकते हैं और पासवर्ड को छोड़कर इसके सभी पाठ जांच सकते हैं."</string>
     <string name="permlab_temporary_enable_accessibility" msgid="2312612135127310254">"आसान तरीका को अस्थायी रूप से सक्षम करें"</string>
     <string name="permdesc_temporary_enable_accessibility" msgid="8079456293182975464">"एप्स को उपकरण पर आसान तरीका को अस्थायी रूप से सक्षम करने देता है. दुर्भावनापूर्ण एप्स उपयोगकर्ता की सहमति के बिना आसान तरीका को सक्षम कर सकते हैं."</string>
     <string name="permlab_retrieve_window_info" msgid="8532295199112519378">"विंडो जानकारी प्राप्त करें"</string>
-    <string name="permdesc_retrieve_window_info" msgid="4998836370424186849">"एप्‍स को विंडो प्रबंधक से windows के बारे में जानकारी प्राप्त करने देता है. दुर्भावनापूर्ण एप्‍स आंतरिक सिस्टम उपयोग के लिए अभिप्रेत जानकारी को प्राप्त कर सकते हैं."</string>
+    <string name="permdesc_retrieve_window_info" msgid="4998836370424186849">"ऐप्स को विंडो प्रबंधक से windows के बारे में जानकारी प्राप्त करने देता है. दुर्भावनापूर्ण ऐप्स आंतरिक सिस्टम उपयोग के लिए अभिप्रेत जानकारी को प्राप्त कर सकते हैं."</string>
     <string name="permlab_filter_events" msgid="8675535648807427389">"ईवेंट फ़िल्टर करें"</string>
-    <string name="permdesc_filter_events" msgid="8006236315888347680">"एप्‍स को इनपुट फ़िल्‍टर पंजीकृत करने देता है, जो सभी उपयोगकर्ता ईवेंट के स्‍ट्रीम को भेजे जाने से पहले फ़िल्‍टर करता है. दुर्भावनापूर्ण एप्‍स उपयोगकर्ता के हस्‍तक्षेप के बिना सिस्‍टम UI को नियंत्रित कर सकता है."</string>
+    <string name="permdesc_filter_events" msgid="8006236315888347680">"ऐप्स को इनपुट फ़िल्‍टर पंजीकृत करने देता है, जो सभी उपयोगकर्ता ईवेंट के स्‍ट्रीम को भेजे जाने से पहले फ़िल्‍टर करता है. दुर्भावनापूर्ण ऐप्स उपयोगकर्ता के हस्‍तक्षेप के बिना सिस्‍टम UI को नियंत्रित कर सकता है."</string>
     <string name="permlab_magnify_display" msgid="5973626738170618775">"डिस्प्ले को आवर्धित करें"</string>
     <string name="permdesc_magnify_display" msgid="7121235684515003792">"एप्स को डिस्प्ले की सामग्री आवर्धित करने देता है. दुर्भावनापूर्ण एप्स डिस्प्ले सामग्री को इस तरह से बदल सकते हैं कि उपकरण अनुपयोगी रेंडर होता है."</string>
     <string name="permlab_shutdown" msgid="7185747824038909016">"आंशिक शटडाउन"</string>
     <string name="permdesc_shutdown" msgid="7046500838746291775">"गतिविधि प्रबंधक को शटडाउन स्‍थिति में रखता है. पूर्ण शटडाउन निष्‍पादित नहीं करता है."</string>
-    <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"एप्‍स स्‍विच करने से रोकता है"</string>
-    <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"उपयोगकर्ता को दूसरे एप्‍स पर स्‍विच करने से रोकता है."</string>
+    <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ऐप्स स्‍विच करने से रोकता है"</string>
+    <string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"उपयोगकर्ता को दूसरे ऐप्स पर स्‍विच करने से रोकता है."</string>
     <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"वर्तमान एप्स की जानकारी प्राप्त करें"</string>
     <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"धारक को स्क्रीन के अग्रभाग में स्थित वर्तमान एप्स के बारे में निजी जानकारी प्राप्त करने देती है."</string>
-    <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"सभी एप्‍स की लॉन्‍चिंग की निगरानी करें और उसे नियंत्रित करें"</string>
+    <string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"सभी ऐप्स की लॉन्‍चिंग की निगरानी करें और उसे नियंत्रित करें"</string>
     <string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"एप्स को यह निगरानी और नियंत्रित करने देता है कि सिस्टम कैसे गतिविधियां लॉन्च करता है. दुर्भावनापूर्ण एप्स सिस्टम को पूरी तरह से जोखिम में डाल सकते हैं. इस अनुमति की आवश्यकता केवल विकास के लिए है, सामान्य उपयोग के लिए कभी नहीं."</string>
     <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"पैकेज निकाले गए प्रसारण भेजें"</string>
     <string name="permdesc_broadcastPackageRemoved" msgid="6621901216207931089">"एप्स को कोई ऐसी सूचना प्रसारित करने देता है जिसे किसी एप्स पैकेज ने निकाल दिया गया हो. दुर्भावनापूर्ण एप्स इसका उपयोग चल रहे अन्य एप्स को समाप्त करने के लिए कर सकते हैं."</string>
@@ -332,111 +332,111 @@
     <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"एप्स को वह सूचना प्रसारित करने देता है जो WAP PUSH संदेश को प्राप्त हुआ है. दुर्भावनापूर्ण एप्स इसका उपयोग नकली MMS संदेश प्राप्त करने या किसी वेबपृष्ठ की सामग्री को दुर्भावनापूर्ण दूसरे रूप से चुपचाप प्रतिस्थापित करने के लिए कर सकते हैं."</string>
     <string name="permlab_setProcessLimit" msgid="2451873664363662666">"चल रही प्रक्रियाओं की संख्‍या सीमित करें"</string>
     <string name="permdesc_setProcessLimit" msgid="7318061314040879542">"एप्स को चलाई जाने वाली अधिकतम प्रक्रियाओं को नियंत्रित करने देता है. सामान्य एप्स के लिए कभी आवश्यक नहीं होती."</string>
-    <string name="permlab_setAlwaysFinish" msgid="550958507798796965">"पृष्ठभूमि एप्‍स को बलपूर्वक बंद करें"</string>
+    <string name="permlab_setAlwaysFinish" msgid="550958507798796965">"पृष्ठभूमि ऐप्स को बलपूर्वक बंद करें"</string>
     <string name="permdesc_setAlwaysFinish" msgid="7471310652868841499">"एप्स को यह नियंत्रित करने देता है कि पृष्ठभूमि में जाते ही गतिविधियां पूर्ण हो जाती है या नही. सामान्य एप्स के लिए कभी आवश्यकता नहीं होती."</string>
     <string name="permlab_batteryStats" msgid="2789610673514103364">"बैटरी के आंकड़े पढ़ें"</string>
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"एप्स को वर्तमान निम्न-स्तरीय बैटरी उपयोग डेटा पढ़ने देती है. एप्स को आपके द्वारा उपयोग किए जाने वाले एप्स के बारे में विस्तृत जानकारी ढूंढने दे सकती है."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"बैटरी के आंकड़े संशोधित करें"</string>
-    <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"एप्‍स को बैटरी के संकलित आंकड़ों को संशोधित करने देती है. सामान्‍य एप्‍स के द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"ऐप्स को बैटरी के संकलित आंकड़ों को संशोधित करने देती है. सामान्‍य ऐप्स के द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"एप्स संचालन आंकड़े प्राप्त करें"</string>
     <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"एप्स को संकलित एप्स संचालन आंकड़े प्राप्त करने देता है. सामान्य एप्स के द्वारा उपयोग के लिए नहीं."</string>
     <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"एप्स कार्यवाही के आंकड़े बदलें"</string>
     <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"एप्स को एप्स कार्यवाही के एकत्रित आंकड़े बदलने देता है. सामान्य एप्स के द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_backup" msgid="470013022865453920">"सिस्‍टम सुरक्षा नियंत्रित और पुनर्स्‍थापित करें"</string>
-    <string name="permdesc_backup" msgid="6912230525140589891">"एप्स को सिस्टम के बैकअप को नियंत्रित और क्रियाविधि को पुर्नस्थापित करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_backup" msgid="6912230525140589891">"एप्स को सिस्टम के बैकअप को नियंत्रित और क्रियाविधि को पुर्नस्थापित करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"पूर्ण सुरक्षा या पुनर्स्‍थापना कार्यवाही की पुष्टि करें"</string>
-    <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"एप्‍स को पूर्ण बैकअप पुष्टिकरण UI लॉन्‍च करने देता है. किसी एप्‍स द्वारा उपयोग के लिए नहीं."</string>
+    <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"ऐप्स को पूर्ण बैकअप पुष्टिकरण UI लॉन्‍च करने देता है. किसी ऐप्स द्वारा उपयोग के लिए नहीं."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"अनधिकृत विंडो दिखाएं"</string>
-    <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"किसी एप्‍स को ऐसी विंडो बनाने देता है जिनका उपयोग आंतरिक सिस्‍टम उपयोगकर्ता इंटरफ़ेस द्वारा किया जाना है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"अन्‍य एप्‍स पर खींचें"</string>
+    <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"किसी ऐप्स को ऐसी विंडो बनाने देता है जिनका उपयोग आंतरिक सिस्‍टम उपयोगकर्ता इंटरफ़ेस द्वारा किया जाना है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"अन्‍य ऐप्स पर खींचें"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"एप्स को अन्य एप्स के शीर्ष पर या उपयोगकर्ता इंटरफ़ेस के हिस्सों पर आने देती है. वे किसी भी एप्स में इंटरफ़ेस के आपके उपयोग में हस्तक्षेप कर सकते हैं, या उस चीज को बदल सकती है जिसके बारे में आपको लगता है कि आप उसे अन्य एप्स में देख रहे हैं."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"वैश्विक एनिमेशन गति बदलें"</string>
-    <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"एप्‍स को किसी भी समय वैश्विक एनिमेशन गति (तेज़ या धीमे एनिमेशन) बदलने देता है."</string>
-    <string name="permlab_manageAppTokens" msgid="1286505717050121370">"एप्‍स टोकन प्रबंधित करें"</string>
-    <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"एप्स को उनके सामान्य Z-क्रमों पर न पहुंचते हुए उनके स्वयं के टोकन बनाने और प्रबंधित करने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"ऐप्स को किसी भी समय वैश्विक एनिमेशन गति (तेज़ या धीमे एनिमेशन) बदलने देता है."</string>
+    <string name="permlab_manageAppTokens" msgid="1286505717050121370">"ऐप्स टोकन प्रबंधित करें"</string>
+    <string name="permdesc_manageAppTokens" msgid="8043431713014395671">"एप्स को उनके सामान्य Z-क्रमों पर न पहुंचते हुए उनके स्वयं के टोकन बनाने और प्रबंधित करने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_freezeScreen" msgid="4708181184441880175">"स्क्रीन को स्थिर करें"</string>
     <string name="permdesc_freezeScreen" msgid="8558923789222670064">"पूर्ण-स्क्रीन संक्रमण के लिए एप्स को अस्थायी रूप से स्क्रीन को स्थिर करने देता है."</string>
     <string name="permlab_injectEvents" msgid="1378746584023586600">"कुंजियों और नियंत्रण बटन को दबाएं"</string>
-    <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"एप्‍स को स्‍वयं के इनपुट ईवेंट (कुंजी दबाना, आदि) को अन्‍य एप्‍स को वितरित करने देता है. दुर्भावनापूर्ण एप्‍स टेबलेट को टेक ओवर करने में इसका उपयोग कर सकते हैं."</string>
-    <string name="permdesc_injectEvents" product="default" msgid="653128057572326253">"एप्‍स को स्‍वयं के इनपुट ईवेंट (कुंजी दबाना, आदि) अन्‍य एप्‍स को वितरित करने देता है. दुर्भावनापूर्ण एप्‍स इसका उपयोग फ़ोन को टेक ओवर करने में कर सकते हैं."</string>
+    <string name="permdesc_injectEvents" product="tablet" msgid="206352565599968632">"ऐप्स को स्‍वयं के इनपुट ईवेंट (कुंजी दबाना, आदि) को अन्‍य ऐप्स को वितरित करने देता है. दुर्भावनापूर्ण ऐप्स टेबलेट को टेक ओवर करने में इसका उपयोग कर सकते हैं."</string>
+    <string name="permdesc_injectEvents" product="default" msgid="653128057572326253">"ऐप्स को स्‍वयं के इनपुट ईवेंट (कुंजी दबाना, आदि) अन्‍य ऐप्स को वितरित करने देता है. दुर्भावनापूर्ण ऐप्स इसका उपयोग फ़ोन को टेक ओवर करने में कर सकते हैं."</string>
     <string name="permlab_readInputState" msgid="469428900041249234">"आप जो भी लिखते हैं और जो कार्यवाहियां करते हैं उन्‍हें रिकॉर्ड करें"</string>
-    <string name="permdesc_readInputState" msgid="8387754901688728043">"एप्स को अन्य एप्स के साथ सहभागिता करते समय भी आपके द्वारा दबाई जाने वाली कुंजियां देखने देता है (जैसे कोई पासवर्ड लिखना). सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_readInputState" msgid="8387754901688728043">"एप्स को अन्य एप्स के साथ सहभागिता करते समय भी आपके द्वारा दबाई जाने वाली कुंजियां देखने देता है (जैसे कोई पासवर्ड लिखना). सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"किसी इनपुट विधि से आबद्ध करें"</string>
-    <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"धारक को किसी इनपुट विधि के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"धारक को किसी इनपुट विधि के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"आसान तरीका सेवा से आबद्ध करें"</string>
-    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"धारक को किसी आसान तरीका सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"धारक को किसी आसान तरीका सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"प्रिंट सेवा से आबद्ध करें"</string>
-    <string name="permdesc_bindPrintService" msgid="7960067623209111135">"धारक को किसी प्रिंट सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindPrintService" msgid="7960067623209111135">"धारक को किसी प्रिंट सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindPrintSpoolerService" msgid="6807762783744125954">"प्रिंट स्पूलर सेवा से आबद्ध करें"</string>
-    <string name="permdesc_bindPrintSpoolerService" msgid="3680552285933318372">"धारक को प्रिंट स्पूलर सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindPrintSpoolerService" msgid="3680552285933318372">"धारक को प्रिंट स्पूलर सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindNfcService" msgid="2752731300419410724">"NFC सेवा से आबद्ध रहें"</string>
     <string name="permdesc_bindNfcService" msgid="6120647629174066862">"धारक को ऐसे एप्स से आबद्ध रहने देता है जो NFC कार्ड का अनुकरण कर रहे हैं. सामान्य एप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
     <string name="permlab_bindTextService" msgid="7358378401915287938">"किसी पाठ सेवा पर बने रहें"</string>
-    <string name="permdesc_bindTextService" msgid="8151968910973998670">"धारक को किसी पाठ सेवा (उदा. SpellCheckerService) के शीर्ष-स्‍तर इंटरफ़ेस पर आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindTextService" msgid="8151968910973998670">"धारक को किसी पाठ सेवा (उदा. SpellCheckerService) के शीर्ष-स्‍तर इंटरफ़ेस पर आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindVpnService" msgid="4708596021161473255">"किसी VPN सेवा से आबद्ध करें"</string>
-    <string name="permdesc_bindVpnService" msgid="2067845564581693905">"धारक को किसी Vpn सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindVpnService" msgid="2067845564581693905">"धारक को किसी Vpn सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindWallpaper" msgid="8716400279937856462">"वॉलपेपर से आबद्ध करें"</string>
-    <string name="permdesc_bindWallpaper" msgid="7108428692595491668">"धारक को किसी वॉलपेपर के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindWallpaper" msgid="7108428692595491668">"धारक को किसी वॉलपेपर के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"किसी विजेट सेवा से आबद्ध करें"</string>
-    <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"धारक को किसी विजेट सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"धारक को किसी विजेट सेवा के शीर्ष-स्‍तर इंटरफ़ेस से आबद्ध होने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"किसी उपकरण व्‍यवस्‍थापक के साथ सहभागिता करें"</string>
-    <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"धारक को किसी उपकरण व्‍यवस्‍थापक को उद्देश्य भेजने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"धारक को किसी उपकरण व्‍यवस्‍थापक को उद्देश्य भेजने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"उपकरण उपकरण सुचारू ढ़ंग से चलाने वाले को जोड़ें या निकालें"</string>
     <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"धारक को सक्रिय डिवाइस व्यवस्थापकों को जोड़ने या निकालने देता है. सामान्य एप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"स्‍क्रीन अभिविन्‍यास बदलें"</string>
-    <string name="permdesc_setOrientation" msgid="3046126619316671476">"एप्‍स को किसी भी समय स्‍क्रीन का रोटेशन बदलने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_setOrientation" msgid="3046126619316671476">"ऐप्स को किसी भी समय स्‍क्रीन का रोटेशन बदलने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_setPointerSpeed" msgid="9175371613322562934">"सूचक गति बदलें"</string>
-    <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"एप्स को माउस या ट्रैकपैड सूचक गति को किसी भी समय बदलने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_setPointerSpeed" msgid="6866563234274104233">"एप्स को माउस या ट्रैकपैड सूचक गति को किसी भी समय बदलने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_setKeyboardLayout" msgid="4778731703600909340">"कीबोर्ड लेआउट बदलें"</string>
-    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"एप्‍स को कीबोर्ड लेआउट बदलने देता है. सामान्‍य एप्‍स के लिए कभी आवश्‍यक नहीं है."</string>
+    <string name="permdesc_setKeyboardLayout" msgid="8480016771134175879">"ऐप्स को कीबोर्ड लेआउट बदलने देता है. सामान्‍य ऐप्स के लिए कभी आवश्‍यक नहीं है."</string>
     <string name="permlab_signalPersistentProcesses" msgid="4539002991947376659">"एप्स को Linux सिग्नल भेजें"</string>
-    <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"एप्‍स को यह अनुरोध करने देता है कि दिया गया सिग्नल सभी जारी प्रक्रियाओं को भेजा जाए."</string>
-    <string name="permlab_persistentActivity" msgid="8841113627955563938">"एप्‍स को हमेशा चलने वाला बनाएं"</string>
-    <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"एप्‍स को स्मृति में स्‍वयं के कुछ हिस्सों को लगातार बनाने की अनुमति देता है. यह अन्‍य एप्स के लिए उपलब्‍ध स्‍मृति को सीमित कर टेबलेट को धीमा कर सकता है."</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"एप्‍स को स्मृति में स्‍वयं के कुछ हिस्सों को लगातार बनाने देता है. यह अन्‍य एप्स के लिए उपलब्‍ध स्‍मृति को सीमित कर फ़ोन को धीमा कर सकता है."</string>
-    <string name="permlab_deletePackages" msgid="184385129537705938">"एप्‍स हटाएं"</string>
+    <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ऐप्स को यह अनुरोध करने देता है कि दिया गया सिग्नल सभी जारी प्रक्रियाओं को भेजा जाए."</string>
+    <string name="permlab_persistentActivity" msgid="8841113627955563938">"ऐप्स को हमेशा चलने वाला बनाएं"</string>
+    <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ऐप्स को स्मृति में स्‍वयं के कुछ हिस्सों को लगातार बनाने की अनुमति देता है. यह अन्‍य एप्स के लिए उपलब्‍ध स्‍मृति को सीमित कर टेबलेट को धीमा कर सकता है."</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ऐप्स को स्मृति में स्‍वयं के कुछ हिस्सों को लगातार बनाने देता है. यह अन्‍य एप्स के लिए उपलब्‍ध स्‍मृति को सीमित कर फ़ोन को धीमा कर सकता है."</string>
+    <string name="permlab_deletePackages" msgid="184385129537705938">"ऐप्स हटाएं"</string>
     <string name="permdesc_deletePackages" msgid="7411480275167205081">"एप्स को Android पैकेज हटाने देता है. दुर्भावनापूर्ण एप्स इसका उपयोग महत्वपूर्ण एप्स हटाने के लिए कर सकते हैं."</string>
-    <string name="permlab_clearAppUserData" msgid="274109191845842756">"अन्‍य एप्‍स का डेटा हटाएं"</string>
+    <string name="permlab_clearAppUserData" msgid="274109191845842756">"अन्‍य ऐप्स का डेटा हटाएं"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"एप्स को उपयोगकर्ता डेटा साफ़ करने देता है."</string>
-    <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"अन्‍य एप्‍स के संचय हटाएं"</string>
+    <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"अन्‍य ऐप्स के संचय हटाएं"</string>
     <string name="permdesc_deleteCacheFiles" msgid="3812998599006730196">"एप्स को संचय फ़ाइलें हटाने देता है."</string>
     <string name="permlab_getPackageSize" msgid="7472921768357981986">"एप्स संग्रहण स्थान मापें"</string>
     <string name="permdesc_getPackageSize" msgid="3921068154420738296">"एप्स को उसका कोड, डेटा, और संचय आकारों को प्राप्त करने देता है"</string>
-    <string name="permlab_installPackages" msgid="2199128482820306924">"सीधे एप्‍स इंस्‍टॉल करें"</string>
+    <string name="permlab_installPackages" msgid="2199128482820306924">"सीधे ऐप्स इंस्‍टॉल करें"</string>
     <string name="permdesc_installPackages" msgid="5628530972548071284">"एप को नए या नई जानकारी वाले Android पैकेज इंस्टॉल करने देता है. दुर्भावनापूर्ण एप्स इसका उपयोग अनियंत्रित रूप से सशक्त अनुमतियों वाले नए एप्स जोड़ने में कर सकते हैं."</string>
     <string name="permlab_clearAppCache" msgid="7487279391723526815">"सभी एप्स संचय डेटा हटाएं"</string>
     <string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"एप्स को अन्य एप्स की संचय निर्देशिकाओं में से फ़ाइलें हटाकर टेबलेट संग्रहण को खाली करने देती है. इससे अन्य एप्स अधिक धीमे प्रारंभ हो सकते हैं क्योंकि उन्हें अपना डेटा पुनर्प्राप्त करने की आवश्यकता होती है."</string>
     <string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"एप्स को अन्य एप्स की संचय निर्देशिकाओं में से फ़ाइलें हटाकर फ़ोन संग्रहण को खाली करने देती है. इससे अन्य एप्स अधिक धीमे प्रारंभ हो सकते हैं क्योंकि उन्हें अपना डेटा पुनर्प्राप्त करने की आवश्यकता होती है."</string>
-    <string name="permlab_movePackage" msgid="3289890271645921411">"एप्‍स संसाधनों को ले जाएं"</string>
-    <string name="permdesc_movePackage" msgid="319562217778244524">"एप्‍स को एप्‍स संसाधनों को आंतरिक से बाहरी मीडिया में और इसके विपरीत ले जाने देता है."</string>
+    <string name="permlab_movePackage" msgid="3289890271645921411">"ऐप्स संसाधनों को ले जाएं"</string>
+    <string name="permdesc_movePackage" msgid="319562217778244524">"ऐप्स को ऐप्स संसाधनों को आंतरिक से बाहरी मीडिया में और इसके विपरीत ले जाने देता है."</string>
     <string name="permlab_readLogs" msgid="6615778543198967614">"संवेदनशील लॉग डेटा पढ़ें"</string>
-    <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"किसी एप्‍स को सिस्‍टम की विभिन्‍न लॉग फ़ाइलों से पढ़ने देता है. संभावित रूप से व्यक्तिगत या निजी जानकारी शामिल करते हुए, टेबलेट के साथ आप क्‍या कर रहे हैं इस बारे में सामान्‍य जानकारी खोजने देता है."</string>
-    <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"एप्‍स को सिस्‍टम की विभिन्‍न लॉग फ़ाइलें पढ़ने देता है. संभावित रूप से व्यक्तिगत या निजी जानकारी सहित, यह इसे इस बारे में सामान्य जानकारी खोजने देता है कि आप फ़ोन से क्‍या कर रहे हैं."</string>
+    <string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">"किसी ऐप्स को सिस्‍टम की विभिन्‍न लॉग फ़ाइलों से पढ़ने देता है. संभावित रूप से व्यक्तिगत या निजी जानकारी शामिल करते हुए, टेबलेट के साथ आप क्‍या कर रहे हैं इस बारे में सामान्‍य जानकारी खोजने देता है."</string>
+    <string name="permdesc_readLogs" product="default" msgid="2063438140241560443">"ऐप्स को सिस्‍टम की विभिन्‍न लॉग फ़ाइलें पढ़ने देता है. संभावित रूप से व्यक्तिगत या निजी जानकारी सहित, यह इसे इस बारे में सामान्य जानकारी खोजने देता है कि आप फ़ोन से क्‍या कर रहे हैं."</string>
     <string name="permlab_anyCodecForPlayback" msgid="715805555823881818">"प्लेबैक के लिए किसी भी मीडिया डीकोडर का उपयोग करें"</string>
     <string name="permdesc_anyCodecForPlayback" msgid="8283912488433189010">"एप्स को प्लेबैक डीकोड करने के लिए किसी भी इंस्टॉल किए गए डीकोडर का उपयोग करने देता है."</string>
     <string name="permlab_manageCaCertificates" msgid="1678391896786882014">"विश्वसनीय क्रेडेंशियल प्रबंधित करें"</string>
     <string name="permdesc_manageCaCertificates" msgid="4015644047196937014">"एप्स को CA प्रमाणपत्रों को विश्वसनीय क्रेडेंशियल के रूप में इंस्टॉल और अनइंस्टॉल करने दें"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"निदान के स्‍वामित्‍व वाले संसाधनों को पढ़ें/लिखें"</string>
-    <string name="permdesc_diagnostic" msgid="6608295692002452283">"एप्‍स को diag समूह के स्‍वामित्‍व वाले किसी संसाधन को पढ़ने और उसमें लिखने देता है; उदाहरण के लिए, /dev की फ़ाइलें. यह सिस्‍टम की स्‍थिरता और सुरक्षा को संभावित रूप से प्रभावित कर सकता है. इसका उपयोग निर्माता या ऑपरेटर द्वारा केवल हार्डवेयर-विशिष्ट निदान के लिए किया जाना चाहिए."</string>
-    <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="permdesc_diagnostic" msgid="6608295692002452283">"ऐप्स को diag समूह के स्‍वामित्‍व वाले किसी संसाधन को पढ़ने और उसमें लिखने देता है; उदाहरण के लिए, /dev की फ़ाइलें. यह सिस्‍टम की स्‍थिरता और सुरक्षा को संभावित रूप से प्रभावित कर सकता है. इसका उपयोग निर्माता या ऑपरेटर द्वारा केवल हार्डवेयर-विशिष्ट निदान के लिए किया जाना चाहिए."</string>
+    <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="permlab_setPreferredApplications" msgid="8463181628695396391">"पसंदीदा ऐप्स सेट करें"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"एप्स को आपके पसंदीदा एप्स को संशोधित करने देता है. दुर्भावनापूर्ण एप्स आपसे निजी डेटा एकत्रित करने के लिए आपके मौजूदा एप्स को स्पूफ़ करके, चलाए जाने वाले एप्स को चुपचाप बदल सकते हैं."</string>
     <string name="permlab_writeSettings" msgid="2226195290955224730">"सिस्‍टम सेटिंग बदलें"</string>
     <string name="permdesc_writeSettings" msgid="7775723441558907181">"एप्स को सिस्टम सेटिंग डेटा संशोधित करने देता है. दुर्भावनापूर्ण एप्स आपके सिस्टम के कॉन्फ़िगरेशन को दूषित कर सकते हैं."</string>
     <string name="permlab_writeSecureSettings" msgid="204676251876718288">"सुरक्षित सिस्‍टम सेटिंग बदलें"</string>
-    <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"एप्स को सिस्टम के सुरक्षित सेटिंग डेटा को संशोधित करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"एप्स को सिस्टम के सुरक्षित सेटिंग डेटा को संशोधित करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_writeGservices" msgid="2149426664226152185">"Google सेवाएं नक्शा बदलें"</string>
-    <string name="permdesc_writeGservices" msgid="1287309437638380229">"एप्‍स को Google सेवाओं का नक्शे संशोधित करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_writeGservices" msgid="1287309437638380229">"ऐप्स को Google सेवाओं का नक्शे संशोधित करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"प्रारंभ होने पर चलाएं"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"एप्स को सिस्टम द्वारा बूटिंग पूर्ण करते ही स्वतः आरंभ करने देता है. इससे टेबलेट को आरंभ होने में अधिक समय लग सकता है और एप्स को निरंतर चलाकर संपूर्ण टेबलेट को धीमा करने देता है."</string>
     <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"एप्स को सिस्टम द्वारा बूटिंग पूर्ण करते ही स्वतः प्रारंभ होने देता है. इससे फ़ोन को प्रारंभ होने में अधिक समय लग सकता है और एप्स के निरंतर चलते रहने से संपूर्ण फ़ोन प्रक्रियाएं धीमी हो सकती हैं."</string>
     <string name="permlab_broadcastSticky" msgid="7919126372606881614">"स्टिकी प्रसारण भेजें"</string>
-    <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"एप्‍स को स्‍टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, टेबलेट की बहुत अधिक स्मृति का उपयोग करके उसे धीमा या अस्‍थिर कर सकता है."</string>
-    <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"एप्‍स को स्‍टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, फ़ोन की बहुत अधिक स्मृति का उपयोग करके उसे धीमा या अस्‍थिर कर सकता है."</string>
+    <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"ऐप्स को स्‍टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, टेबलेट की बहुत अधिक स्मृति का उपयोग करके उसे धीमा या अस्‍थिर कर सकता है."</string>
+    <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"ऐप्स को स्‍टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, फ़ोन की बहुत अधिक स्मृति का उपयोग करके उसे धीमा या अस्‍थिर कर सकता है."</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"अपने संपर्क पढ़ें"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"एप्स को आपके टेबलेट में संग्रहीत संपर्कों के डेटा को, साथ ही आपके द्वारा विशिष्ट व्यक्तियों को कॉल करने, ईमेल करने, या अन्‍य तरीके से संवाद करने की आवृत्ति को पढ़ने देता है. यह अनुमति एप्स को आपके संपर्क डेटा को सहेजने देती है, और दुर्भावनापूर्ण एप्स आपकी जानकारी के बिना संपर्क डेटा को साझा कर सकते हैं."</string>
     <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"एप्स को आपके फ़ोन में संग्रहीत संपर्कों के डेटा को, साथ ही आपके द्वारा विशिष्ट व्यक्तियों को कॉल करने, ईमेल करने, या अन्‍य तरीके से संवाद करने की आवृत्ति को पढ़ने देता है. यह अनुमति एप्स को आपके संपर्क डेटा को सहेजने देती है, और दुर्भावनापूर्ण एप्स आपकी जानकारी के बिना संपर्क डेटा को साझा कर सकते हैं."</string>
@@ -447,8 +447,8 @@
     <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"एप्स को आपके फ़ोन का कॉल लॉग पढ़ने देता है, जिसमें इनकमिंग और आउटगोइंग कॉल का डेटा शामिल है. यह अनुमति एप्स को आपके कॉल लॉग डेटा को सहेजने देती है, और दुर्भावनापूर्ण एप्स आपकी जानकारी के बिना कॉल लॉग डेटा को साझा कर सकते हैं."</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"एप्स को आपके फ़ोन का कॉल लॉग पढ़ने देता है, जिसमें इनकमिंग और आउटगोइंग कॉल का डेटा शामिल है. यह अनुमति एप्स को आपके कॉल लॉग डेटा को सहेजने देती है, और दुर्भावनापूर्ण एप्स आपकी जानकारी के बिना कॉल लॉग डेटा को साझा कर सकते हैं."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिखें"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"एप्‍स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके टेबलेट का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण एप्‍स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"एप्‍स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके फ़ोन का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण एप्‍स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ऐप्स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके टेबलेट का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ऐप्स को इनकमिंग और आउटगोइंग कॉल के डेटा सहित, आपके फ़ोन का कॉल लॉग संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स आपके कॉल लॉग को मिटाने या संशोधित करने के लिए इसका उपयोग कर सकते हैं."</string>
     <string name="permlab_readProfile" msgid="4701889852612716678">"स्‍वयं का संपर्क कार्ड पढ़ें"</string>
     <string name="permdesc_readProfile" product="default" msgid="5462475151849888848">"एप्स को आपके उपकरण में संग्रहीत व्यक्तिगत प्रोफ़ाइल जानकारी, जैसे आपका नाम और संपर्क जानकारी, पढ़ने देता है. इसका अर्थ है कि एप्स आपको पहचान सकता है और आपकी प्रोफ़ाइल जानकारी अन्य लोगों को भेज सकता है."</string>
     <string name="permlab_writeProfile" msgid="907793628777397643">"स्‍वयं का संपर्क कार्ड बदलें"</string>
@@ -474,21 +474,21 @@
     <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"अनुमानित स्थान (नेटवर्क-आधारित)"</string>
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"एप्स को आपका अनुमानित स्थान प्राप्त करने देती है. इस स्थान को सेल टॉवर और Wi-Fi जैसे नेटवर्क स्थान स्रोतों का उपयोग करके स्थान सेवाओं द्वारा प्राप्त किया गया है. एप्स द्वारा इन स्थान सेवाओं का उपयोग करने के लिए इन्हें चालू होना चाहिए और आपके उपकरण में उपलब्ध होना चाहिए. एप्स इसका उपयोग यह पता लगाने में कर सकते हैं कि आप लगभग कहां पर हैं."</string>
     <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlinger में पहुंचें"</string>
-    <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"एप्‍स को SurfaceFlinger निम्‍न-स्‍तर सुविधाएं उपयोग करने देता है."</string>
+    <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"ऐप्स को SurfaceFlinger निम्‍न-स्‍तर सुविधाएं उपयोग करने देता है."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"फ़्रेम बफ़र पढ़ें"</string>
-    <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"एप्‍स को फ़्रेम बफ़र की सामग्री पढ़ने देता है."</string>
+    <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"ऐप्स को फ़्रेम बफ़र की सामग्री पढ़ने देता है."</string>
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wifi डिस्प्ले को कॉन्फ़िगर करें"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"एप्स को कॉन्फ़िगर करने देता है और Wifi डिस्प्ले से कनेक्ट करता है."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wifi डिस्प्ले को नियंत्रित करें"</string>
     <string name="permdesc_controlWifiDisplay" msgid="4543912292681826986">"एप्स को Wifi डिस्प्ले की निम्न-स्तर की सुविधाएं नियंत्रित करने देता है."</string>
     <string name="permlab_captureAudioOutput" msgid="6857134498402346708">"ऑडियो आउटपुट को कैप्‍चर करें"</string>
-    <string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"एप्‍स को ऑडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
+    <string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"ऐप्स को ऑडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
     <string name="permlab_captureAudioHotword" msgid="1890553935650349808">"हॉटवर्ड पहचान"</string>
     <string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"एप्लिकेशन को हॉटवर्ड पहचान के लिए ऑडियो कैप्चर करने देती है. कैप्चर पृष्ठभूमि में हो सकता है लेकिन वह अन्य ऑडियो कैप्चर (उदा. कैमकॉर्डर) को नहीं रोकता."</string>
     <string name="permlab_captureVideoOutput" msgid="2246828773589094023">"वीडियो आउटपुट को कैप्‍चर करें"</string>
-    <string name="permdesc_captureVideoOutput" msgid="359481658034149860">"एप्‍स को वीडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
+    <string name="permdesc_captureVideoOutput" msgid="359481658034149860">"ऐप्स को वीडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
     <string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"सुरक्षित वीडियो आउटपुट को कैप्‍चर करें"</string>
-    <string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"एप्‍स को सुरक्षित वीडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
+    <string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"ऐप्स को सुरक्षित वीडियो आउटपुट को कैप्‍चर और रीडायरेक्‍ट करने देता है."</string>
     <string name="permlab_mediaContentControl" msgid="8749790560720562511">"मीडिया प्लेबैक और मेटाडेटा एक्सेस नियंत्रित करें"</string>
     <string name="permdesc_mediaContentControl" msgid="1637478200272062">"एप्लिकेशन को मीडिया प्लेबैक नियंत्रित करने देती है और मीडिया जानकारी (शीर्षक, लेखक...) एक्सेस करने देती है."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"अपनी ऑडियो सेटिंग बदलें"</string>
@@ -501,53 +501,53 @@
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"पहले से इंस्टॉल किए गए सिस्टम एप्स को कैमरे को संकेतक LED का उपयोग करने से अक्षम करती है."</string>
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"स्‍थायी रूप से टेबलेट अक्षम करें"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"फ़ोन को स्‍थायी रूप से अक्षम करें"</string>
-    <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"एप्‍स को संपूर्ण टेबलेट को स्‍थायी रूप से अक्षम करने देता है. यह बहुत खतरनाक है."</string>
-    <string name="permdesc_brick" product="default" msgid="5788903297627283099">"एप्‍स को संपूर्ण फ़ोन को स्‍थायी रूप से अक्षम करने देता है. यह बहुत खतरनाक है."</string>
+    <string name="permdesc_brick" product="tablet" msgid="4334818808001699530">"ऐप्स को संपूर्ण टेबलेट को स्‍थायी रूप से अक्षम करने देता है. यह बहुत खतरनाक है."</string>
+    <string name="permdesc_brick" product="default" msgid="5788903297627283099">"ऐप्स को संपूर्ण फ़ोन को स्‍थायी रूप से अक्षम करने देता है. यह बहुत खतरनाक है."</string>
     <string name="permlab_reboot" product="tablet" msgid="3436634972561795002">"टेबलेट रीबूट के लिए बाध्‍य करें"</string>
     <string name="permlab_reboot" product="default" msgid="2898560872462638242">"फ़ोन रीबूट के लिए बाध्‍य करें"</string>
-    <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"एप्‍स को टेबलेट रीबूट करने के लिए बाध्‍य करने देता है."</string>
-    <string name="permdesc_reboot" product="default" msgid="5326008124289989969">"एप्‍स को फ़ोन बलपूर्वक रीबूट करने देता है."</string>
+    <string name="permdesc_reboot" product="tablet" msgid="8172056180063700741">"ऐप्स को टेबलेट रीबूट करने के लिए बाध्‍य करने देता है."</string>
+    <string name="permdesc_reboot" product="default" msgid="5326008124289989969">"ऐप्स को फ़ोन बलपूर्वक रीबूट करने देता है."</string>
     <string name="permlab_mount_unmount_filesystems" product="nosdcard" msgid="2927361537942591841">"USB संग्रहण फ़ाइल सिस्‍टम पर पहुंचें"</string>
     <string name="permlab_mount_unmount_filesystems" product="default" msgid="4402305049890953810">"SD कार्ड फ़ाइल सिस्‍टम पर पहुंचें"</string>
-    <string name="permdesc_mount_unmount_filesystems" msgid="1829290701658992347">"एप्‍स को निकाले जाने योग्‍य संग्रहण के लिए फ़ाइल सिस्‍टम माउंट और अनमाउंट करने देता है."</string>
+    <string name="permdesc_mount_unmount_filesystems" msgid="1829290701658992347">"ऐप्स को निकाले जाने योग्‍य संग्रहण के लिए फ़ाइल सिस्‍टम माउंट और अनमाउंट करने देता है."</string>
     <string name="permlab_mount_format_filesystems" product="nosdcard" msgid="6227819582624904972">"USB संग्रहण मिटाएं"</string>
     <string name="permlab_mount_format_filesystems" product="default" msgid="262582698639274056">"SD कार्ड मिटाएं"</string>
-    <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"एप्‍स को निकालने योग्‍य संग्रहण फ़ॉर्मेट करने देता है."</string>
+    <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"ऐप्स को निकालने योग्‍य संग्रहण फ़ॉर्मेट करने देता है."</string>
     <string name="permlab_asec_access" msgid="3411338632002193846">"मोबाइल संग्रहण पर जानकारी प्राप्त करें"</string>
     <string name="permdesc_asec_access" msgid="3094563844593878548">"एप्स को मोबाइल संग्रहण की जानकारी प्राप्‍त करने देता है."</string>
     <string name="permlab_asec_create" msgid="6414757234789336327">"मोबाइल संग्रहण बनाएं"</string>
-    <string name="permdesc_asec_create" msgid="4558869273585856876">"एप्‍स को मोबाइल संग्रहण बनाने देता है."</string>
+    <string name="permdesc_asec_create" msgid="4558869273585856876">"ऐप्स को मोबाइल संग्रहण बनाने देता है."</string>
     <string name="permlab_asec_destroy" msgid="526928328301618022">"मोबाइल संग्रहण नष्ट करें"</string>
-    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"एप्‍स को मोबाइल संग्रहण नष्ट करने देता है."</string>
+    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"ऐप्स को मोबाइल संग्रहण नष्ट करने देता है."</string>
     <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"मोबाइल संग्रहण माउंट/अनमाउंट करें"</string>
-    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"एप्‍स को मोबाइल संग्रहण माउंट/अनमाउंट करने देता है."</string>
+    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"ऐप्स को मोबाइल संग्रहण माउंट/अनमाउंट करने देता है."</string>
     <string name="permlab_asec_rename" msgid="7496633954080472417">"मोबाइल संग्रहण का नाम बदलें"</string>
-    <string name="permdesc_asec_rename" msgid="1794757588472127675">"एप्‍स को मोबाइल संग्रहण का नाम बदलने देता है."</string>
+    <string name="permdesc_asec_rename" msgid="1794757588472127675">"ऐप्स को मोबाइल संग्रहण का नाम बदलने देता है."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कंपन नियंत्रित करें"</string>
-    <string name="permdesc_vibrate" msgid="6284989245902300945">"एप्‍स को कंपनकर्ता नियंत्रित करने देता है."</string>
+    <string name="permdesc_vibrate" msgid="6284989245902300945">"ऐप्स को कंपनकर्ता नियंत्रित करने देता है."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"फ़्लैशलाइट नियंत्रित करें"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"एप्‍स को फ़्लैशलाइट नियंत्रित करने देता है."</string>
+    <string name="permdesc_flashlight" msgid="6522284794568368310">"ऐप्स को फ़्लैशलाइट नियंत्रित करने देता है."</string>
     <string name="permlab_manageUsb" msgid="1113453430645402723">"USB उपकरणों की प्राथमिकताएं और अनुमतियां प्रबंधित करें"</string>
-    <string name="permdesc_manageUsb" msgid="7776155430218239833">"एप्‍स को USB उपकरणों की प्राथमिकताओं और अनुमतियों को प्रबंधित करने देता है."</string>
+    <string name="permdesc_manageUsb" msgid="7776155430218239833">"ऐप्स को USB उपकरणों की प्राथमिकताओं और अनुमतियों को प्रबंधित करने देता है."</string>
     <string name="permlab_accessMtp" msgid="4953468676795917042">"MTP प्रोटोकॉल लागू करें"</string>
     <string name="permdesc_accessMtp" msgid="6532961200486791570">"MTP USB प्रोटोकॉल लागू करने के लिए कर्नेल MTP ड्राइवर में पहुंच की अनुमति देता है."</string>
     <string name="permlab_hardware_test" msgid="4148290860400659146">"परीक्षण हार्डवेयर"</string>
-    <string name="permdesc_hardware_test" msgid="6597964191208016605">"एप्‍स को हार्डवेयर परीक्षण के लिए विविध सहायक उपकरणों को नियंत्रित करने देता है."</string>
+    <string name="permdesc_hardware_test" msgid="6597964191208016605">"ऐप्स को हार्डवेयर परीक्षण के लिए विविध सहायक उपकरणों को नियंत्रित करने देता है."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"फ़ोन नंबर पर सीधे कॉल करें"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"एप्स को आपके हस्‍तक्षेप के बिना फ़ोन नंबर पर कॉल करने देता है. इसके परिणाम अप्रत्‍याशित शुल्‍क या कॉल हो सकते हैं. ध्यान दें कि यह एप्स को आपातकालीन नंबर पर कॉल नहीं करने देता. दुर्भावनापूर्ण एप्स आपकी पुष्टि के बिना कॉल करके आपका धन व्‍यय कर सकते हैं."</string>
     <string name="permlab_callPrivileged" msgid="4198349211108497879">"किसी भी फ़ोन नंबर पर सीधे कॉल करें"</string>
-    <string name="permdesc_callPrivileged" msgid="1689024901509996810">"एप्‍स को आपके हस्‍तक्षेप के बिना आपातकालीन नंबरों सहित, किसी भी फ़ोन नंबर पर कॉल करने देता है. दुर्भावनापूर्ण एप्‍स आपातकालीन सेवाओं पर अनावश्‍यक और अवैध कॉल कर सकते हैं."</string>
+    <string name="permdesc_callPrivileged" msgid="1689024901509996810">"ऐप्स को आपके हस्‍तक्षेप के बिना आपातकालीन नंबरों सहित, किसी भी फ़ोन नंबर पर कॉल करने देता है. दुर्भावनापूर्ण ऐप्स आपातकालीन सेवाओं पर अनावश्‍यक और अवैध कॉल कर सकते हैं."</string>
     <string name="permlab_performCdmaProvisioning" product="tablet" msgid="4842576994144604821">"सीधे CDMA टेबलेट सेटअप प्रारंभ करें"</string>
     <string name="permlab_performCdmaProvisioning" product="default" msgid="5604848095315421425">"सीधे CDMA फ़ोन सेटअप प्रारंभ करें"</string>
-    <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"एप्‍स को CDMA प्रावधान प्रारंभ करने देता है. दुर्भावनापूर्ण एप्‍स अनावश्‍यक रूप से CDMA प्रावधान प्रारंभ कर सकते हैं."</string>
+    <string name="permdesc_performCdmaProvisioning" msgid="1994193538802314186">"ऐप्स को CDMA प्रावधान प्रारंभ करने देता है. दुर्भावनापूर्ण ऐप्स अनावश्‍यक रूप से CDMA प्रावधान प्रारंभ कर सकते हैं."</string>
     <string name="permlab_locationUpdates" msgid="7785408253364335740">"स्‍थान के बारे में नई जानकारी की सूचनाओं को नियंत्रित करें"</string>
-    <string name="permdesc_locationUpdates" msgid="1120741557891438876">"एप को रेडियो से स्‍थान के बारे में नई जानकारी की सूचनाएं सक्षम/अक्षम करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_locationUpdates" msgid="1120741557891438876">"एप को रेडियो से स्‍थान के बारे में नई जानकारी की सूचनाएं सक्षम/अक्षम करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_checkinProperties" msgid="7855259461268734914">"चेकइन गुणों में पहुंचें"</string>
-    <string name="permdesc_checkinProperties" msgid="4024526968630194128">"एप्स को चेकइन सेवा द्वारा अपलोड किए गए गुणों पर पढ़ें/लिखें पहुंच देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_checkinProperties" msgid="4024526968630194128">"एप्स को चेकइन सेवा द्वारा अपलोड किए गए गुणों पर पढ़ें/लिखें पहुंच देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_bindGadget" msgid="776905339015863471">"विजेट चुनें"</string>
-    <string name="permdesc_bindGadget" msgid="8261326938599049290">"एप्स को सिस्टम को यह बताने देता है कि किस एप्स द्वारा कौन से विजेट का उपयोग किया जा सकता है. कोई एप्स, इस अनुमति के साथ अन्य एप्स के निजी डेटा पर पहुंच सकते हैं. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_bindGadget" msgid="8261326938599049290">"एप्स को सिस्टम को यह बताने देता है कि किस एप्स द्वारा कौन से विजेट का उपयोग किया जा सकता है. कोई एप्स, इस अनुमति के साथ अन्य एप्स के निजी डेटा पर पहुंच सकते हैं. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"फ़ोन स्‍थिति बदलें"</string>
-    <string name="permdesc_modifyPhoneState" msgid="1029877529007686732">"एप्‍स को उपकरण की फ़ोन सुविधाएं नियंत्रित करने देता है. इस अनुमति वाला कोई एप्‍स आपको सूचित किए बिना नेटवर्क स्‍विच कर सकता है, फ़ोन का रेडियो चालू और बंद कर सकता है और ऐसे ही अन्य कार्य कर सकता है."</string>
+    <string name="permdesc_modifyPhoneState" msgid="1029877529007686732">"ऐप्स को उपकरण की फ़ोन सुविधाएं नियंत्रित करने देता है. इस अनुमति वाला कोई ऐप्स आपको सूचित किए बिना नेटवर्क स्‍विच कर सकता है, फ़ोन का रेडियो चालू और बंद कर सकता है और ऐसे ही अन्य कार्य कर सकता है."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"फ़ोन की स्‍थिति और पहचान पढ़ें"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"एप्स को उपकरण की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति एप्स को फ़ोन नंबर और उपकरण आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्‍थ नंबर निर्धारित करने देती है."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टेबलेट को निष्‍क्रिय होने से रोकें"</string>
@@ -559,32 +559,32 @@
     <string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"एप्लिकेशन को फ़ोन के इंफ़्रारेड ट्रांसमीटर का उपयोग करने देती है."</string>
     <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"टेबलेट चालू या बंद करें"</string>
     <string name="permlab_devicePower" product="default" msgid="4928622470980943206">"फ़ोन चालू या बंद करें"</string>
-    <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"एप्‍स को टेबलेट चालू या बंद करने देता है."</string>
-    <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"एप्‍स को फ़ोन चालू या बंद करने देता है."</string>
+    <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"ऐप्स को टेबलेट चालू या बंद करने देता है."</string>
+    <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"ऐप्स को फ़ोन चालू या बंद करने देता है."</string>
     <string name="permlab_factoryTest" msgid="3715225492696416187">"फ़ैक्‍ट्री परीक्षण मोड में चलाएं"</string>
     <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"टेबलेट हार्डवेयर में पूर्ण पहुंच की अनुमति देते हुए निम्‍न-स्‍तर निर्माता परीक्षण के रूप में चलाएं. केवल तभी उपलब्‍ध जब कोई टेबलेट निर्माता परीक्षण मोड में चल रहा हो."</string>
     <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"फ़ोन हार्डवेयर में पूर्ण पहुंच की अनुमति देते हुए निम्‍न-स्‍तर निर्माता परीक्षण के रूप में चलाएं. केवल तभी उपलब्‍ध जब कोई फ़ोन निर्माता परीक्षण मोड में चल रहा हो."</string>
     <string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करें"</string>
-    <string name="permdesc_setWallpaper" msgid="7373447920977624745">"एप्‍स को सिस्‍टम वॉलपेपर सेट करने देता है."</string>
+    <string name="permdesc_setWallpaper" msgid="7373447920977624745">"ऐप्स को सिस्‍टम वॉलपेपर सेट करने देता है."</string>
     <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"अपने वॉलपेपर का आकार एडजस्ट करें"</string>
-    <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"एप्‍स को सिस्‍टम वॉलपेपर आकार संकेत सेट करने देता है."</string>
+    <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ऐप्स को सिस्‍टम वॉलपेपर आकार संकेत सेट करने देता है."</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"फ़ैक्‍ट्री डिफ़ॉल्‍ट पर सिस्‍टम रीसेट करें"</string>
     <string name="permdesc_masterClear" msgid="3665380492633910226">"एप्स को सभी डेटा, कॉन्फ़िगरेशन, और इंस्टॉल एप्स मिटाकर, सिस्टम को पूरी तरह उसकी फ़ैक्टरी सेटिंग पर रीसेट करने देता है."</string>
     <string name="permlab_setTime" msgid="2021614829591775646">"समय सेट करें"</string>
-    <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"एप्‍स को टेबलेट की घड़ी का समय बदलने देता है."</string>
-    <string name="permdesc_setTime" product="default" msgid="1855702730738020">"एप्‍स को फ़ोन की घड़ी का समय बदलने देता है."</string>
+    <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ऐप्स को टेबलेट की घड़ी का समय बदलने देता है."</string>
+    <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ऐप्स को फ़ोन की घड़ी का समय बदलने देता है."</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"समय क्षेत्र सेट करें"</string>
-    <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"एप्‍स को टेबलेट का समय क्षेत्र बदलने देता है."</string>
-    <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"एप्‍स को टेबलेट का समय क्षेत्र बदलने देता है."</string>
+    <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"ऐप्स को टेबलेट का समय क्षेत्र बदलने देता है."</string>
+    <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"ऐप्स को टेबलेट का समय क्षेत्र बदलने देता है."</string>
     <string name="permlab_accountManagerService" msgid="4829262349691386986">"खाता प्रबंधक सेवा के रूप में कार्य करें"</string>
-    <string name="permdesc_accountManagerService" msgid="1948455552333615954">"एप्‍स को खाता प्रमाणकों को कॉल करने देता है."</string>
+    <string name="permdesc_accountManagerService" msgid="1948455552333615954">"ऐप्स को खाता प्रमाणकों को कॉल करने देता है."</string>
     <string name="permlab_getAccounts" msgid="1086795467760122114">"उपकरण पर खाते ढूंढें"</string>
     <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"एप्स को टेबलेट द्वारा ज्ञात खातों की सूची प्राप्‍त करने देता है. इसमें वे खाते शामिल हो सकते हैं जिन्‍हें आपके द्वारा इंस्‍टॉल किए गए एप्स ने बनाया है."</string>
     <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"एप्स को फ़ोन द्वारा ज्ञात खातों की सूची प्राप्‍त करने देता है. इसमें वे खाते शामिल हो सकते हैं जिन्‍हें आपके द्वारा इंस्‍टॉल किए गए एप्स ने बनाया है."</string>
     <string name="permlab_authenticateAccounts" msgid="5265908481172736933">"खाते बनाएं और पासवर्ड सेट करें"</string>
     <string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"एप्‍िलकेशन को खाता बनाने और उनके पासवर्ड प्राप्त करने और सेट करने सहित, खाता प्रबंधक की खाता प्रमाणक क्षमताओं का उपयोग करने देता है."</string>
     <string name="permlab_manageAccounts" msgid="4983126304757177305">"खाते जोडें या निकालें"</string>
-    <string name="permdesc_manageAccounts" msgid="8698295625488292506">"एप्‍स को खाते जोड़ना और निकालना और उनके पासवर्ड हटाने जैसे कार्य करने देता है."</string>
+    <string name="permdesc_manageAccounts" msgid="8698295625488292506">"ऐप्स को खाते जोड़ना और निकालना और उनके पासवर्ड हटाने जैसे कार्य करने देता है."</string>
     <string name="permlab_useCredentials" msgid="235481396163877642">"उपकरण पर खातों का उपयोग करें"</string>
     <string name="permdesc_useCredentials" msgid="7984227147403346422">"एप्स को प्रमाणीकरण टोकन का अनुरोध करने देता है."</string>
     <string name="permlab_accessNetworkState" msgid="4951027964348974773">"नेटवर्क कनेक्‍शन देखें"</string>
@@ -596,9 +596,9 @@
     <string name="permlab_changeNetworkState" msgid="958884291454327309">"नेटवर्क कनेक्‍टिविटी बदलें"</string>
     <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"एप्स को नेटवर्क कनेक्टिविटी की स्थिति बदलने देता है."</string>
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"टेदर की गई कनेक्‍टिविटी बदलें"</string>
-    <string name="permdesc_changeTetherState" msgid="1524441344412319780">"एप्‍स को टेदर की गई नेटवर्क कनेक्‍टिविटी की स्‍थिति बदलने देता है."</string>
+    <string name="permdesc_changeTetherState" msgid="1524441344412319780">"ऐप्स को टेदर की गई नेटवर्क कनेक्‍टिविटी की स्‍थिति बदलने देता है."</string>
     <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"पृष्ठभूमि डेटा उपयोग सेटिंग बदलें"</string>
-    <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"एप्‍स को पृष्ठभूमि डेटा उपयोग सेटिंग बदलने देता है."</string>
+    <string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"ऐप्स को पृष्ठभूमि डेटा उपयोग सेटिंग बदलने देता है."</string>
     <string name="permlab_accessWifiState" msgid="5202012949247040011">"Wi-Fi कनेक्‍शन देखें"</string>
     <string name="permdesc_accessWifiState" msgid="5002798077387803726">"एप्स को Wi-Fi नेटवर्क के बारे में जानकारी, जैसे WI-Fi सक्षम है या नहीं और कनेक्‍ट किए गए Wi-Fi उपकरणों के नाम, देखने देता है."</string>
     <string name="permlab_changeWifiState" msgid="6550641188749128035">"Wi-Fi से कनेक्‍ट और डिस्‍कनेक्‍ट करें"</string>
@@ -607,23 +607,23 @@
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"एप्स को Wi-Fi नेटवर्क पर मल्टीकास्ट पते के उपयोग से केवल आपके टेबलेट पर ही नहीं, बल्कि सभी उपकरणों पर भेजे गए पैकेट प्राप्‍त करने देता है. यह गैर-मल्टीकास्ट मोड से अधिक पावर का उपयोग करता है."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"एप्स को Wi-Fi नेटवर्क पर मल्टीकास्ट पते के उपयोग से केवल आपके फ़ोन पर ही नहीं, बल्कि सभी उपकरणों पर भेजे गए पैकेट प्राप्‍त करने देता है. यह गैर-मल्टीकास्ट मोड से अधिक पावर का उपयोग करता है."</string>
     <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"Bluetooth सेटिंग पर पहुंचें"</string>
-    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"किसी एप्‍स को स्‍थानीय Bluetooth टेबलेट कॉन्‍फ़िगर करने की और रिमोट उपकरणों के साथ खोजने और युग्‍मित करने देता है."</string>
-    <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"एप्‍स को स्‍थानीय Bluetooth फ़ोन कॉन्‍फ़िगर करने देता है, और रिमोट उपकरणों के साथ खोजने और युग्‍मित करने देता है."</string>
+    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"किसी ऐप्स को स्‍थानीय Bluetooth टेबलेट कॉन्‍फ़िगर करने की और रिमोट उपकरणों के साथ खोजने और युग्‍मित करने देता है."</string>
+    <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"ऐप्स को स्‍थानीय Bluetooth फ़ोन कॉन्‍फ़िगर करने देता है, और रिमोट उपकरणों के साथ खोजने और युग्‍मित करने देता है."</string>
     <string name="permlab_bluetoothPriv" msgid="4009494246009513828">"एप्‍लिकेशन के द्वारा Bluetooth युग्‍मन करने देती है"</string>
     <string name="permdesc_bluetoothPriv" product="tablet" msgid="8045735193417468857">"एप्‍लिकेशन को उपयोगकर्ता के इंटरैक्शन के बिना दूरस्‍थ उपकरणों के साथ युग्‍मित करने देती है."</string>
     <string name="permdesc_bluetoothPriv" product="default" msgid="8045735193417468857">"एप्‍लिकेशन को उपयोगकर्ता के इंटरैक्शन के बिना दूरस्‍थ उपकरणों के साथ युग्‍मित करने देती है."</string>
     <string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX से कनेक्ट और डिस्कनेक्ट करें"</string>
     <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"एप्स को WiMAX सक्षम है या नहीं और कनेक्‍ट किए गए किसी WiMAX नेटवर्क के बारे में जानकारी निर्धारित करने देता है."</string>
     <string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX स्‍थिति बदलें"</string>
-    <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"एप्‍स को WiMAX नेटवर्क से टेबलेट को कनेक्‍ट और डिस्‍कनेक्‍ट करने देता है."</string>
-    <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"एप्‍स को WiMAX नेटवर्क से फ़ोन को कनेक्‍ट और डिस्‍कनेक्‍ट करने देता है."</string>
+    <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ऐप्स को WiMAX नेटवर्क से टेबलेट को कनेक्‍ट और डिस्‍कनेक्‍ट करने देता है."</string>
+    <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ऐप्स को WiMAX नेटवर्क से फ़ोन को कनेक्‍ट और डिस्‍कनेक्‍ट करने देता है."</string>
     <string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetooth उपकरणों के साथ युग्मित करें"</string>
-    <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"एप्‍स को टेबलेट पर Bluetooth का कॉन्‍फ़िगरेशन देखने, और युग्‍मित उपकरणों के साथ कनेक्‍शन बनाने और स्‍वीकार करने देता है."</string>
-    <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"एप्‍स को फ़ोन पर Bluetooth का कॉन्‍फ़िगरेशन देखने, और युग्‍मित उपकरणों के साथ कनेक्‍शन बनाने और स्‍वीकार करने देता है."</string>
+    <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"ऐप्स को टेबलेट पर Bluetooth का कॉन्‍फ़िगरेशन देखने, और युग्‍मित उपकरणों के साथ कनेक्‍शन बनाने और स्‍वीकार करने देता है."</string>
+    <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"ऐप्स को फ़ोन पर Bluetooth का कॉन्‍फ़िगरेशन देखने, और युग्‍मित उपकरणों के साथ कनेक्‍शन बनाने और स्‍वीकार करने देता है."</string>
     <string name="permlab_nfc" msgid="4423351274757876953">"नियर फ़ील्‍ड कम्‍यूनिकेशन नियंत्रित करें"</string>
     <string name="permdesc_nfc" msgid="7120611819401789907">"एप्स को नियर फ़ील्ड कम्यूनिकेशन (NFC) टैग, कार्ड, और रीडर के साथ संचार करने देता है."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"अपना स्‍क्रीन लॉक अक्षम करें"</string>
-    <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"एप्‍स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है."</string>
+    <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"एप्स को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग एप्स किसी खाते के साथ समन्‍वयित है या नहीं."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्‍वयन बंद या चालू टॉगल करें"</string>
@@ -631,11 +631,11 @@
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"समन्वयन आंकड़े पढ़ें"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"एप्स को किसी खाते के समन्वयन आंकड़े, साथ ही समन्‍वयित ईवेंट का इतिहास और समन्‍वयित डेटा की मात्रा पढ़ने देता है."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ग्राहकी-प्राप्त फ़ीड पढ़ें"</string>
-    <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"एप्‍स को वर्तमान में समन्वयित फ़ीड के बारे में विवरण प्राप्त करने देता है."</string>
+    <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"ऐप्स को वर्तमान में समन्वयित फ़ीड के बारे में विवरण प्राप्त करने देता है."</string>
     <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"ग्राहकी-प्राप्त फ़ीड लिखें"</string>
     <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"एप्स को आपके वर्तमान समन्वयित फ़ीड को संशोधित करने देता है. दुर्भावनापूर्ण एप्स आपके समन्वयित फ़ीड को बदल सकते है."</string>
     <string name="permlab_readDictionary" msgid="4107101525746035718">"शब्दकोश में आपके द्वारा जोड़े गए शब्‍दों को पढ़ें"</string>
-    <string name="permdesc_readDictionary" msgid="659614600338904243">"एप्‍स को ऐसे सभी शब्‍दों, नामों और वाक्यांशों को पढ़ने देता है जो संभवत: उपयोगकर्ता द्वारा उपयोगकर्ता ‍शब्दकोश में संग्रहीत किए गए हों."</string>
+    <string name="permdesc_readDictionary" msgid="659614600338904243">"ऐप्स को ऐसे सभी शब्‍दों, नामों और वाक्यांशों को पढ़ने देता है जो संभवत: उपयोगकर्ता द्वारा उपयोगकर्ता ‍शब्दकोश में संग्रहीत किए गए हों."</string>
     <string name="permlab_writeDictionary" msgid="2183110402314441106">"उपयोगकर्ता द्वारा परिभाषित शब्दकोश में शब्द जोड़ें"</string>
     <string name="permdesc_writeDictionary" msgid="8185385716255065291">"एप्स को उपयोगकर्ता शब्दकोश में नए शब्द लिखने देता है."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"अपने USB संग्रहण की सामग्री पढ़ें"</string>
@@ -653,17 +653,17 @@
     <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचें"</string>
     <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"एप्स को सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचने दें."</string>
     <string name="permlab_cache_filesystem" msgid="5656487264819669824">"कैश फ़ाइल सिस्‍टम में पहंचे"</string>
-    <string name="permdesc_cache_filesystem" msgid="5578967642265550955">"एप्‍स को संचय फ़ाइल सिस्‍टम पढ़ने और लिखने देता है."</string>
+    <string name="permdesc_cache_filesystem" msgid="5578967642265550955">"ऐप्स को संचय फ़ाइल सिस्‍टम पढ़ने और लिखने देता है."</string>
     <string name="permlab_use_sip" msgid="5986952362795870502">"इंटरनेट कॉल करें/प्राप्त करें"</string>
     <string name="permdesc_use_sip" msgid="4717632000062674294">"एप्स को इंटरनेट कॉल करने/प्राप्त करने के लिए SIP सेवा का उपयोग करने देता है."</string>
     <string name="permlab_bind_call_service" msgid="6724009726671246551">"इन-कॉल स्क्रीन से सहभागिता करें"</string>
     <string name="permdesc_bind_call_service" msgid="8732547662442572435">"एप्लिकेशन को यह नियंत्रित करने देती है कि उपयोगकर्ता को इन-कॉल स्क्रीन कब और कैसी दिखाई देती है."</string>
     <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"ऐतिहासिक नेटवर्क उपयोग पढें"</string>
-    <string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"किसी एप्स को विशिष्ट नेटवर्क और एप्‍स के लिए ऐतिहासिक नेटवर्क उपयोग को पढ़ने देता है."</string>
+    <string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"किसी एप्स को विशिष्ट नेटवर्क और ऐप्स के लिए ऐतिहासिक नेटवर्क उपयोग को पढ़ने देता है."</string>
     <string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"नेटवर्क नीति प्रबंधित करें"</string>
-    <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"एप्‍स को नेटवर्क नीतियां प्रबंधित करने और एप्‍स-विशिष्‍ट नियमों को परिभाषित करने देता है."</string>
+    <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"ऐप्स को नेटवर्क नीतियां प्रबंधित करने और ऐप्स-विशिष्‍ट नियमों को परिभाषित करने देता है."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क उपयोग हिसाब बदलें"</string>
-    <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"एप्स को यह संशोधित करने देता है कि एप्‍स की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"एप्स को यह संशोधित करने देता है कि ऐप्स की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_markNetworkSocket" msgid="3658527214914959749">"सॉकेट मार्क बदलें"</string>
     <string name="permdesc_markNetworkSocket" msgid="7655568433696356578">"एप्स को रूटिंग के लिए सॉकेट मार्क बदलने देता है"</string>
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"सूचनाओं तक पहुंचें"</string>
@@ -671,7 +671,7 @@
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"धारक को सूचना श्रवणकर्ता सेवा के शीर्ष स्तरीय इंटरफ़ेस से जुड़ने देती है. सामान्य एप्स के लिए कभी भी आवश्यक नहीं होनी चाहिए."</string>
     <string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"वाहक के द्वारा उपलब्ध कराया गया कॉन्फ़िगरेशन एप्स प्रारंभ करें"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"धारक को वाहक के द्वारा उपलब्ध कराया गया कॉन्फ़िगरेशन एप्स प्रारंभ करने देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"धारक को वाहक के द्वारा उपलब्ध कराया गया कॉन्फ़िगरेशन एप्स प्रारंभ करने देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"नेटवर्क स्थितियों के अवलोकनों को सुनें"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"एप्स को नेटवर्क स्थितियों के अवलोकनों को सुनने देता है. सामान्य एप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string>
@@ -691,7 +691,7 @@
     <string name="policylab_expirePassword" msgid="885279151847254056">"स्‍क्रीन लॉक करें पासवर्ड समाप्ति सेट करें"</string>
     <string name="policydesc_expirePassword" msgid="1729725226314691591">"नियंत्रित करें कि कितने समय में लॉक-स्‍क्रीन पासवर्ड बदला जाना चाहिए."</string>
     <string name="policylab_encryptedStorage" msgid="8901326199909132915">"संग्रहण एन्‍क्रिप्‍शन सेट करें"</string>
-    <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"संग्रहीत एप्‍स डेटा को एन्क्रिप्ट किया जाना आवश्‍यक है."</string>
+    <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"संग्रहीत ऐप्स डेटा को एन्क्रिप्ट किया जाना आवश्‍यक है."</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"कैमरों को अक्षम करें"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"सभी उपकरण कैमरों का उपयोग रोकें."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="266329104542638802">"कीगार्ड में सुविधाएं अक्षम करें"</string>
@@ -945,15 +945,15 @@
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"एप्स को आपके टेबलेट में संग्रहीत ब्राउज़र के इतिहास या बुकमार्क को संशोधित करने देता है. इससे एप्स ब्राउज़र डेटा को मिटा सकता है या संशोधित कर सकता है. ध्‍यान दें: यह अनुमति तृतीय-पक्ष ब्राउज़र या वेब ब्राउज़िंग क्षमताओं वाले अन्‍य एप्स द्वारा लागू नहीं की जा सकती."</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"एप्स को आपके फ़ोन में संग्रहीत ब्राउज़र के इतिहास या बुकमार्क को संशोधित करने देता है. इससे एप्स ब्राउज़र डेटा को मिटा सकता है या संशोधित कर सकता है. ध्‍यान दें: यह अनुमति तृतीय-पक्ष ब्राउज़र या वेब ब्राउज़िंग क्षमताओं वाले अन्‍य एप्स द्वारा लागू नहीं की जा सकती."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"अलार्म सेट करें"</string>
-    <string name="permdesc_setAlarm" msgid="316392039157473848">"एप्‍स को इंस्‍टॉल किए गए अलार्म घड़ी एप्‍स में अलार्म सेट करने देता है. हो सकता है कुछ अलार्म घड़ी एप्‍स में यह सुविधा न हो."</string>
+    <string name="permdesc_setAlarm" msgid="316392039157473848">"ऐप्स को इंस्‍टॉल किए गए अलार्म घड़ी ऐप्स में अलार्म सेट करने देता है. हो सकता है कुछ अलार्म घड़ी ऐप्स में यह सुविधा न हो."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ध्‍वनिमेल जोड़ें"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"एप्स को आपके ध्‍वनिमेल इनबॉक्‍स में संदेश जोड़ने देता है."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउज़र भौगोलिक-स्थान अनुमतियों को बदलें"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"एप्‍स को ब्राउज़र के भौगोलिक-स्‍थान की अनुमतियां संशोधित करने देता है. दुर्भावनापूर्ण एप्‍स इसका उपयोग एकपक्षीय वेबसाइट को स्‍थान जानकारी भेजने में कर सकते हैं."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ऐप्स को ब्राउज़र के भौगोलिक-स्‍थान की अनुमतियां संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स इसका उपयोग एकपक्षीय वेबसाइट को स्‍थान जानकारी भेजने में कर सकते हैं."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"पैकेज सत्‍यापि‍त करें"</string>
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"एप्‍लि‍केशन को इंस्‍टॉल करने योग्‍य पैकेज सत्‍यापि‍त करने देता है."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"पैकेज प्रमाणक से आबद्ध करें"</string>
-    <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"धारक को पैकेज प्रमाणक के अनुरोध की अनुमति‍ देता है. सामान्‍य एप्‍स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"धारक को पैकेज प्रमाणक के अनुरोध की अनुमति‍ देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"सीरियल पोर्ट पर पहुंचें"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API का उपयोग करके धारक को सीरियल पोर्ट पर पहुंच प्रदान करता है."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"बाह्य रूप से सामग्री प्रदाताओं पर पहुंच"</string>
@@ -1120,7 +1120,7 @@
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"सिस्‍टम सेटिंग &gt; Apps &gt; डाउनलोड किए गए में डिफ़ॉल्‍ट साफ करें."</string>
     <string name="chooseActivity" msgid="7486876147751803333">"कोई क्रिया चुनें"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"USB उपकरण के लिए कोई एप्स चुनें"</string>
-    <string name="noApplications" msgid="2991814273936504689">"कोई भी एप्‍स यह कार्यवाही नहीं कर सकता."</string>
+    <string name="noApplications" msgid="2991814273936504689">"कोई भी ऐप्स यह कार्यवाही नहीं कर सकता."</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
     <string name="aerr_application" msgid="932628488013092776">"दुर्भाग्‍यवश, <xliff:g id="APPLICATION">%1$s</xliff:g> रुक गया है."</string>
     <string name="aerr_process" msgid="4507058997035697579">"दुर्भाग्‍यवश, <xliff:g id="PROCESS">%1$s</xliff:g> प्रक्रिया रुक गई है."</string>
@@ -1139,20 +1139,20 @@
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"हमेशा दिखाएं"</string>
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"इसे सिस्‍टम सेटिंग &gt; Apps &gt; डाउनलोड किए गए में पुन: सक्षम करें."</string>
-    <string name="smv_application" msgid="3307209192155442829">"एप्‍स <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने उसकी स्‍वयं लागू होने वाली StrictMode नीति का उल्‍लंघन किया है."</string>
+    <string name="smv_application" msgid="3307209192155442829">"ऐप्स <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने उसकी स्‍वयं लागू होने वाली StrictMode नीति का उल्‍लंघन किया है."</string>
     <string name="smv_process" msgid="5120397012047462446">"प्रक्रिया <xliff:g id="PROCESS">%1$s</xliff:g> ने उसकी स्‍व-प्रवर्तित StrictMode नीति का उल्‍लंघन किया है."</string>
     <string name="android_upgrading_title" msgid="1584192285441405746">"Android अपग्रेड हो रहा है..."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> में से <xliff:g id="NUMBER_0">%1$d</xliff:g> एप्स अनुकूलित हो रहा है."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"एप्स प्रारंभ होने वाले हैं"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्‍त हो रहा है."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> चल रही है"</string>
-    <string name="heavy_weight_notification_detail" msgid="1721681741617898865">"एप्‍स पर स्‍विच करने के लिए स्‍पर्श करें"</string>
+    <string name="heavy_weight_notification_detail" msgid="1721681741617898865">"ऐप्स पर स्‍विच करने के लिए स्‍पर्श करें"</string>
     <string name="heavy_weight_switcher_title" msgid="7153167085403298169">"एप्स स्विच करें?"</string>
-    <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"दूसरा एप्स पहले से चल रहा है जिसे किसी नए एप्‍स को प्रारंभ करने के पहले बंद किया जाना आवश्‍यक है."</string>
+    <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"दूसरा एप्स पहले से चल रहा है जिसे किसी नए ऐप्स को प्रारंभ करने के पहले बंद किया जाना आवश्‍यक है."</string>
     <string name="old_app_action" msgid="493129172238566282">"<xliff:g id="OLD_APP">%1$s</xliff:g> पर वापस लौटें"</string>
-    <string name="old_app_description" msgid="2082094275580358049">"नया एप्‍स प्रारंभ न करें."</string>
+    <string name="old_app_description" msgid="2082094275580358049">"नया ऐप्स प्रारंभ न करें."</string>
     <string name="new_app_action" msgid="5472756926945440706">"<xliff:g id="OLD_APP">%1$s</xliff:g> प्रारंभ करें"</string>
-    <string name="new_app_description" msgid="1932143598371537340">"पुराने एप्‍स को बिना सहेजे बंद करें."</string>
+    <string name="new_app_description" msgid="1932143598371537340">"पुराने ऐप्स को बिना सहेजे बंद करें."</string>
     <string name="sendText" msgid="5209874571959469142">"पाठ के लिए किसी क्रिया को चुनें"</string>
     <string name="volume_ringtone" msgid="6885421406845734650">"रिंगर वॉल्‍यूम"</string>
     <string name="volume_music" msgid="5421651157138628171">"मीडिया वॉल्‍यूम"</string>
@@ -1247,7 +1247,7 @@
     <string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB संग्रहण बंद करें"</string>
     <string name="usb_storage_stop_error_message" msgid="1970374898263063836">"USB संग्रहण बंद करने में कोई समस्‍या हुई थी. जांचें कि आपने USB होस्‍ट अनमाउंट किया है या नहीं, तब पुन: प्रयास करें."</string>
     <string name="dlg_confirm_kill_storage_users_title" msgid="963039033470478697">"USB संग्रहण चालू करें"</string>
-    <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"यदि आप USB संग्रहण चालू करते हैं, तो आपके द्वारा उपयोग किए जा रहे कुछ एप्‍स रुक जाएंगे और हो सकता है कि वे तब तक अनुपलब्‍ध रहें जब तक कि आप USB संग्रहण बंद नहीं कर देते."</string>
+    <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"यदि आप USB संग्रहण चालू करते हैं, तो आपके द्वारा उपयोग किए जा रहे कुछ ऐप्स रुक जाएंगे और हो सकता है कि वे तब तक अनुपलब्‍ध रहें जब तक कि आप USB संग्रहण बंद नहीं कर देते."</string>
     <string name="dlg_error_title" msgid="7323658469626514207">"USB कार्यवाही विफल"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"ठीक है"</string>
     <string name="usb_mtp_notification_title" msgid="3699913097391550394">"किसी मीडिया उपकरण के रूप में कनेक्‍ट किया गया"</string>
@@ -1296,9 +1296,9 @@
     <string name="ext_media_nomedia_notification_message" product="default" msgid="3870120652983659641">"SD कार्ड निकाला गया. एक नया सम्‍मिलित करें."</string>
     <string name="activity_list_empty" msgid="1675388330786841066">"कोई मिलती-जुलती गतिविधि नहीं मिली."</string>
     <string name="permlab_pkgUsageStats" msgid="8787352074326748892">"घटक उपयोग आंकड़ों की नई जानकारी पाएं"</string>
-    <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"एप्‍स को घटक उपयोग के संकलित आंकड़े संशोधित करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_pkgUsageStats" msgid="1106612424254277630">"ऐप्स को घटक उपयोग के संकलित आंकड़े संशोधित करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_copyProtectedData" msgid="4341036311211406692">"सामग्री की प्रतिलिपि बनाएं"</string>
-    <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"एप्स को सामग्री की प्रतिलिपि बनाने के लिए डिफ़ॉल्ट कंटेनर सेवा शुरू करने देता है. सामान्‍य एप्‍स द्वारा उपयोग करने के लिए नहीं."</string>
+    <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"एप्स को सामग्री की प्रतिलिपि बनाने के लिए डिफ़ॉल्ट कंटेनर सेवा शुरू करने देता है. सामान्‍य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
     <string name="permlab_route_media_output" msgid="1642024455750414694">"मीडिया आउटपुट को रूट करें"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"एप्स को मीडिया आउटपुट को अन्य बाहरी उपकरणों पर रूट करने देता है."</string>
     <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"कीगार्ड सुरक्षित संग्रहण एक्सेस करें"</string>
@@ -1316,7 +1316,7 @@
     <string name="ime_action_default" msgid="2840921885558045721">"निष्‍पादित करें"</string>
     <string name="dial_number_using" msgid="5789176425167573586">"<xliff:g id="NUMBER">%s</xliff:g> के उपयोग द्वारा \n नंबर डायल करें"</string>
     <string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g> का उपयोग करके\n संपर्क बनाएं"</string>
-    <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"निम्‍न एक या अधिक एप्‍स अभी और भविष्‍य में आपके खाते में पहुंच की अनुमति का अनुरोध करते हैं."</string>
+    <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"निम्‍न एक या अधिक ऐप्स अभी और भविष्‍य में आपके खाते में पहुंच की अनुमति का अनुरोध करते हैं."</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"क्या आप इस अनुरोध को अनुमति देना चाहते हैं?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"पहुंच अनुरोध"</string>
     <string name="allow" msgid="7225948811296386551">"अनुमति दें"</string>
@@ -1413,7 +1413,7 @@
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mode change"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
-    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"कोई एप्‍स चुनें"</string>
+    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"कोई ऐप्स चुनें"</string>
     <string name="shareactionprovider_share_with" msgid="806688056141131819">"इसके साथ साझा करें:"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> के साथ साझा करें"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"स्लाइडिंग हैंडल. स्पर्श करके रखें."</string>
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> सेकंड में पुन: प्रयास करें"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"बाद में पुनः प्रयास करें"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"पूर्ण स्क्रीन से बाहर आने हेतु ऊपर से नीचे स्वाइप करें"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"पूर्ण स्क्रीन से बाहर आने के लिए ऊपर से नीचे स्वाइप करें."</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 653cc67..37dc141 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Ponovite za <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Pokušajte ponovo kasnije"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Prijeđite prstom s vrha prema dolje za izlaz"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Prijeđite prstom s vrha prema dolje za izlaz iz cijelog zaslona."</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 4b4bbaf..4ae5938 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Próbálja újra <xliff:g id="COUNT">%d</xliff:g> másodperc múlva"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Próbálkozzon később"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"A kilépéshez húzza ujját a tetejétől lefelé"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"A teljes képernyős nézetből való kilépéshez húzza ujját a tetejétől lefelé."</string>
 </resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 4f8f8ed..96ff31e 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1142,7 +1142,7 @@
     <string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> ծրագիրը (գործընթաց <xliff:g id="PROCESS">%2$s</xliff:g>) խախտել է իր ինքնահարկադրված Խիստ ռեժիմ  քաղաքականությունը:"</string>
     <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> գործընթացը խախտել է իր ինքնահարկադրված Խիստ ռեժիմ քաղաքականությունը:"</string>
     <string name="android_upgrading_title" msgid="1584192285441405746">"Android-ը նորացվում է..."</string>
-    <string name="android_upgrading_apk" msgid="7904042682111526169">"Հավելվածը օպտիմալացվում է <xliff:g id="NUMBER_0">%1$d</xliff:g>-ից <xliff:g id="NUMBER_1">%2$d</xliff:g>-ի:"</string>
+    <string name="android_upgrading_apk" msgid="7904042682111526169">"Օպտիմալացվում է հավելված <xliff:g id="NUMBER_0">%1$d</xliff:g>-ը <xliff:g id="NUMBER_1">%2$d</xliff:g>-ից:"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Հավելվածները մեկնարկում են:"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Բեռնումն ավարտվում է:"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string>
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Կրկին փորձեք <xliff:g id="COUNT">%d</xliff:g> վայրկյանից"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Կրկին փորձեք մի փոքր ուշ"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Սահահարվածեք վերից վար՝ ամբողջական էկրանից դուրս գալու համար"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Սահահարվածեք վերից վար՝ ամբողջական էկրանից դուրս գալու համար:"</string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 60c5c55..1b04923 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1581,6 +1581,5 @@
     <item quantity="one" msgid="4835639969503729874">"PIN-ը սխալ է: Փորձեք կրկին 1 վայրկյանից:"</item>
     <item quantity="other" msgid="8030607343223287654">"PIN-ը սխալ է: Փորձեք կրկին <xliff:g id="COUNT">%d</xliff:g> վայրկյանից:"</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Սահեցրեք էկրանի եզրով՝ գոտին բացելու համար"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Սահեցրեք էկրանի եզրով՝ համակարգային գոտին բացելու համար"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Սահեցրեք էկրանի եզրով՝ գոտին բացելու համար"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0c7bd4d..eccabdd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Coba <xliff:g id="COUNT">%d</xliff:g> detik lagi"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Coba lagi nanti"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Gesek ke bawah untuk keluar dari layar penuh"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Gesek dari atas ke bawah untuk keluar dari layar penuh."</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a662d9e..d2dbfc3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Riprova tra <xliff:g id="COUNT">%d</xliff:g> s."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Riprova più tardi"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Uscita schermo intero: scorri in basso da alto"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Scorri dall\'alto verso il basso per uscire dalla modalità schermo intero."</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 76f20d2d..51f3889 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"נסה שוב בעוד <xliff:g id="COUNT">%d</xliff:g> שניות"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"נסה שוב מאוחר יותר"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"החלק מטה מהחלק העליון כדי לצאת ממסך מלא"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"החלק מטה מהחלק העליון כדי לצאת ממסך מלא."</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 0f2b5a1..246a82e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g>秒後に再試行"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"しばらくしてから再試行"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"全画面表示を終了するには、上から下にスワイプ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"全画面表示を終了するには、上から下にスワイプ"</string>
 </resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index d3f1458..2152c33 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"კიდევ ერთხელ სცადეთ <xliff:g id="COUNT">%d</xliff:g> წამში"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"სცადეთ მოგვიანებით"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"ჩამოასრიალეთ ზევიდან სრული ეკრანის დასახურად"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"ჩამოასრიალეთ ზევიდან სრული ეკრანის დასახურად."</string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 94fb8d0..a7c16ed 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"არასწორი PIN. სცადეთ ისევ 1 წამში."</item>
     <item quantity="other" msgid="8030607343223287654">"არასწორი PIN. სცადეთ ისევ <xliff:g id="COUNT">%d</xliff:g> წამში."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"გაასრიალეთ ეკრანის კიდეზე ზოლის გამოსაჩენად"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"გაასრიალეთ ეკრანის კიდიდან სისტემის ზოლის გამოსაჩენად"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"გაასრიალეთ ეკრანის კიდეზე ზოლის გამოსაჩენად"</string>
 </resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index f1e9234..c04e0d4 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"សូម​ព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈពេល <xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"សូម​ព្យាយាម​ម្ដងទៀត​នៅ​ពេល​ក្រោយ។"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"អូស​​​ចុះក្រោម ដើម្បី​ចេញ​ពី​ការ​បង្ហាញ​ពេញ​អេក្រង់"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"អូស​​​​ពីលើ​ចុះក្រោម ដើម្បី​ចេញ​ពី​ការ​បង្ហាញ​ពេញ​អេក្រង់។"</string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index e8c8bb4..3d62d1a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"លេខ​កូដ​ PIN មិន​ត្រឹមត្រូវ។ ព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈ​ពេល ១ វិនាទី។"</item>
     <item quantity="other" msgid="8030607343223287654">"លេខ​កូដ​ PIN មិន​ត្រឹមត្រូវ។ ព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈ​ពេល <xliff:g id="COUNT">%d</xliff:g> វិនាទី​។"</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"អូស​គែម​អេក្រង់ ដើម្បី​បង្ហាញ​របារ"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"អូស​ពី​គែម​អេក្រង់ ដើម្បី​បង្ហាញ​របារ​ប្រព័ន្ធ"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"អូស​គែម​អេក្រង់ ដើម្បី​បង្ហាញ​របារ"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 026ebcb..9ed05d6 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g>초 후에 다시 시도하세요."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"나중에 다시 시도"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"전체화면을 종료하려면 위에서 아래로 스와이프"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"전체화면을 종료하려면 위에서 아래로 스와이프하세요."</string>
 </resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index d54182c..003b3f9 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"ລອງໃໝ່ໃນອີກ <xliff:g id="COUNT">%d</xliff:g> ວິນາທີ"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"ລອງໃໝ່ອີກຄັ້ງໃນພາຍຫລັງ."</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"ປັດລົງຈາກເທິງສຸດເພື່ອອກຈາກໂໝດເຕັມໜ້າຈໍ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"ປັດລົງມາຈາກທາງເທິງເພື່ອອອກຈາກໂໝດເຕັມໜ້າຈໍ."</string>
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 9d07ae6..68ece0f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"PIN ​ບໍ່​ຖືກ​ຕ້ອງ​. ລອງໃໝ່ໃນອີກ 1 ວິນາທີ."</item>
     <item quantity="other" msgid="8030607343223287654">"PIN ບໍ່​ຖືກ​ຕ້ອງ​. ລອງໃໝ່ໃນອີກ <xliff:g id="COUNT">%d</xliff:g> ວິ​ນາ​ທີ​."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"ປັດຢູ່ຂອບຂອງໜ້າຈໍເພື່ອສະແດງແຖບ"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"ປັດຢູ່ຂອບຂອງໜ້າຈໍເພື່ອສະແດງແຖບຂອງລະບົບ"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"ປັດຢູ່ຂອບຂອງໜ້າຈໍເພື່ອສະແດງແຖບ"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 86077e2..9fa41c9 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Band. dar po <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Vėliau bandykite dar kartą"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Perbr. žemyn, kad išeit. iš viso ekr. rež."</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Perbraukite nuo viršaus žemyn, kad išeitumėte iš viso ekrano režimo"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index df1cba5..036d5d6 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Mēģ. vēl pēc <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Vēlāk mēģiniet vēlreiz."</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Lai izietu no pilnekr., velc. no augšas lejup."</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Lai izietu no pilnekrāna režīma, velciet no augšas uz leju."</string>
 </resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 4a39904..fe3d78b 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> секундын дараа дахин оролдоно уу"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Дараа дахин оролдоно уу"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Бүтэн дэлгэцээс гарахын тулд дээрээс нь эхлэн доош шудрана уу"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Бүтэн дэлгэцээс гарахын тулд дээрээс нь эхлэн доош шудрана уу."</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 67ec195..e187320 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1579,6 +1579,5 @@
     <item quantity="one" msgid="4835639969503729874">"Буруу PIN. 1 секундын дараа дахин оролдоно уу."</item>
     <item quantity="other" msgid="8030607343223287654">"Буруу PIN. <xliff:g id="COUNT">%d</xliff:g> секундын дараа дахин оролдоно уу."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Баганыг харуулахын тулд дэлгэцийн ирмэгийг шудрана уу"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Системийн баганыг гаргахын тулд дэлгэцийн ирмэгээс шудрана уу"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Баганыг харуулахын тулд дэлгэцийн ирмэгийг шудрана уу"</string>
 </resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 4a0803a..66fbefe 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Cuba <xliff:g id="COUNT">%d</xliff:g> saat lagi"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Cuba sebentar lagi"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Leret ke bawah untuk keluar dari skrin penuh"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Leret ke bawah dari atas untuk keluar dari skrin penuh."</string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e6dc0ca..f522a48 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"PIN salah. Cuba lagi dalam masa 1 saat."</item>
     <item quantity="other" msgid="8030607343223287654">"PIN salah. Cuba lagi dalam masa <xliff:g id="COUNT">%d</xliff:g> saat."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"Leret bhg tepi skrin utk serlah bar"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"Leret dari tepi skrin untuk menampakkan bar sistem"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"Leret bhg tepi skrin utk serlah bar"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index cf60244..3be8817 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Prøv på nytt om <xliff:g id="COUNT">%d</xliff:g> sekunder"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Prøv på nytt senere"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Sveip ned for å avslutte fullskjermvisning"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Sveip ned fra toppen av skjermen for å gå ut av fullskjermvisningen."</string>
 </resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 20ab30e..fe6b669 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1584,6 +1584,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"पछि पुनः प्रयास गर्नुहोस्"</string>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"पट्टि देखिने बनाउन स्क्रिनको छेउमा स्वाइप गर्नुहोस्"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"प्रणाली पट्टि देखिने बनाउन स्क्रिनको छेउबाट स्वाइप गर्नुहोस्"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"पट्टि देखिने बनाउन स्क्रिनको छेउमा स्वाइप गर्नुहोस्"</string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index b6e4fc3..7ff8dae 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1583,6 +1583,5 @@
     <item quantity="one" msgid="4835639969503729874">"गलत PIN । १ सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</item>
     <item quantity="other" msgid="8030607343223287654">"गलत PIN । <xliff:g id="COUNT">%d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"पट्टि देखिने बनाउन स्क्रिनको छेउमा स्वाइप गर्नुहोस्"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"प्रणाली पट्टि देखिने बनाउन स्क्रिनको छेउबाट स्वाइप गर्नुहोस्"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"पट्टि देखिने बनाउन स्क्रिनको छेउमा स्वाइप गर्नुहोस्"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e74ef8a..30afea7 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Probeer het over <xliff:g id="COUNT">%d</xliff:g> seconden opnieuw"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Probeer het later opnieuw"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Van boven omlaag vegen: voll. scherm sluiten"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Veeg omlaag vanaf de bovenkant om het volledige scherm te sluiten."</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 032dd8b..0e1bee9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Spróbuj za <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Spróbuj ponownie później"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Przesuń z góry w dół, by zamknąć pełny ekran"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Przesuń z góry w dół, by zamknąć pełny ekran."</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c493f0a..f3c4020 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Tente em: <xliff:g id="COUNT">%d</xliff:g> seg"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Tente novamente mais tarde"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Deslize para baixo para sair do ecrã inteiro"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Deslize rapidamente para baixo para sair do ecrã inteiro."</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 648df40..1ea7b9a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Tente novamente em <xliff:g id="COUNT">%d</xliff:g> segundos"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Tente novamente mais tarde"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Deslize para baixo para sair da tela inteira"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Deslize de cima para baixo para sair da tela inteira"</string>
 </resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index a64f6d2b..9154621 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -2732,6 +2732,7 @@
     <!-- no translation found for restr_pin_countdown:other (4730868920742952817) -->
     <!-- no translation found for restr_pin_try_later (973144472490532377) -->
     <skip />
-    <!-- no translation found for transient_navigation_confirmation (8554991488096662508) -->
+    <!-- no translation found for immersive_mode_confirmation (7227416894979047467) -->
+    <!-- no translation found for immersive_mode_confirmation (8554991488096662508) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ed31a33..dc5a211 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Reîncercați în <xliff:g id="COUNT">%d</xliff:g> sec."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Reîncercați mai târziu"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Glisați în jos pt. a ieși din ecran complet"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Glisați în jos pentru a ieși din ecran complet."</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index dded653..2e59477 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Повтор через <xliff:g id="COUNT">%d</xliff:g> сек."</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Повторите попытку позже."</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Чтобы вернуться в обычный режим, проведите пальцем вниз"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Чтобы вернуться в обычный режим, проведите пальцем вниз."</string>
 </resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 9dc197f..367de51 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -1587,6 +1587,5 @@
     <item quantity="other" msgid="4730868920742952817">"තත්පර <xliff:g id="COUNT">%d</xliff:g> කින් නැවත උත්සහ කරන්න"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"පසුව නැවත උත්සාහ කරන්න"</string>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"තීරුව අනාවරණයට තිරයේ කෙලවර අදින්න"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"පද්ධති තීරුව අනාවරණයට තිරයේ කෙලවරින් අදින්න"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"තීරුව අනාවරණයට තිරයේ කෙලවර අදින්න"</string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 165163b..7e12eab 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1586,6 +1586,5 @@
     <item quantity="one" msgid="4835639969503729874">"වැරදි PIN. තත්පරයකින් නැවත උත්සහ කරන්න."</item>
     <item quantity="other" msgid="8030607343223287654">"වැරදි PIN. තත්පර <xliff:g id="COUNT">%d</xliff:g> කින් නැවත උත්සහ කරන්න."</item>
   </plurals>
-    <string name="transient_navigation_confirmation" msgid="4907844043611123426">"තීරුව අනාවරණයට තිරයේ කෙලවර අදින්න"</string>
-    <string name="transient_navigation_confirmation_long" msgid="8061685920508086697">"පද්ධති තීරුව අනාවරණයට තිරයේ කෙලවරින් අදින්න"</string>
+    <string name="immersive_mode_confirmation" msgid="8554991488096662508">"තීරුව අනාවරණයට තිරයේ කෙලවර අදින්න"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 634b0e9..d47c64f 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Skúste to zas o <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Skúste to znova neskôr"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Režim celej obraz. ukončíte posunutím nadol"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Režim celej obrazovky ukončíte posunutím nadol."</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 01ee3a9..43da811 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Poskusite znova čez <xliff:g id="COUNT">%d</xliff:g> s"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Poskusite znova pozneje"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Povlecite z vrha, da zaprete celozas. način"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Povlecite z vrha, da zaprete celozaslonski način."</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d07f1b7..14b22cc 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Покушајте опет за <xliff:g id="COUNT">%d</xliff:g> сек"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Покушајте поново касније"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Превуците надоле од врха за излаз из целог екрана"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Превуците прстом одозго надоле да бисте изашли из целог екрана."</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0fd3727..aa20a64 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -830,7 +830,7 @@
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Försök igen"</string>
     <string name="lockscreen_password_wrong" msgid="5737815393253165301">"Försök igen"</string>
     <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
-    <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Laddar (<xliff:g id="PERCENT">%%</xliff:g> <xliff:g id="NUMBER">%d</xliff:g>)"</string>
+    <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="321635745684060624">"Batteriet har laddats"</string>
     <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
     <string name="lockscreen_low_battery" msgid="1482873981919249740">"Anslut din laddare."</string>
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Försök igen om <xliff:g id="COUNT">%d</xliff:g> sekunder"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Försök igen senare"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Dra nedåt om du vill avbryta fullskärmsläget"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Dra nedåt om du vill avbryta fullskärmsläget."</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 7a5fc28..a587950 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Jaribu tena baada ya sekunde <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Jaribu tena baadaye"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Telezesha kidole kwa kasi chini kuanzia juu ili uondoke kwenye skrini kamili"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Telezesha kidole kwa kasi chini kuanzia juu ili uondoke kwenye skrini kamili."</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 94f7070..58f5268 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"ลองอีกใน <xliff:g id="COUNT">%d</xliff:g> วินาที"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"ลองอีกครั้งในภายหลัง"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"กวาดนิ้วจากบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วจากบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d4a90e7..cf2f78b 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Subukan muli sa <xliff:g id="COUNT">%d</xliff:g> seg"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Subukang muli sa ibang pagkakataon"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Mag-swipe pababa upang lumabas sa full screen"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Mag-swipe pababa mula sa itaas upang lumabas sa full screen."</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 1af37b9..9e1bd8b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> saniye içinde tekrar deneyin"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Daha sonra tekrar deneyin"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Tam ekrandan çıkmak için aşağıya hızlıca kaydırın"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Tam ekrandan çıkmak için yukarıdan aşağıya hızlıca kaydırın."</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b9590b6..61f45ec 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Повтор за <xliff:g id="COUNT">%d</xliff:g> с"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Спробуйте пізніше"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Проведіть пальцем зверху вниз, щоб зменшити"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Проведіть пальцем зверху вниз, щоб вийти з повноекранного режиму."</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1b816a4..6fe43a0 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Hãy thử lại sau <xliff:g id="COUNT">%d</xliff:g> giây"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Hãy thử lại sau"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Vuốt từ trên xuống để thoát toàn màn hình"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Vuốt từ trên xuống để thoát toàn màn hình."</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 36eb3f8..05d5c34 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g>秒后重试"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"稍后重试"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"从顶部向下滑动即可退出全屏模式"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"从顶部向下滑动即可退出全屏模式。"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4c5f0ee..e3ca5b9 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> 秒後再試一次"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"稍後再試"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"從頂端往下快速滑動即可退出全螢幕"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"從頂端往下快速滑動即可退出全螢幕。"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6149ab1..3ec8d4c 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -255,7 +255,7 @@
     <string name="permlab_receiveSms" msgid="8673471768947895082">"接收簡訊 (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"允許應用程式接收和處理簡訊。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"接收簡訊 (MMS)"</string>
-    <string name="permdesc_receiveMms" msgid="533019437263212260">"允許應用程式接收和處理 MMS 訊息。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
+    <string name="permdesc_receiveMms" msgid="533019437263212260">"允許應用程式接收和處理多媒體訊息。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
     <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"接收緊急廣播"</string>
     <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"允許應用程式接收及處理緊急廣播訊息,只有系統應用程式可以具備這項權限。"</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"讀取區域廣播訊息"</string>
@@ -759,7 +759,7 @@
     <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"公司行動電話"</string>
     <string name="phoneTypeWorkPager" msgid="649938731231157056">"公司呼叫器"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"助理"</string>
-    <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
+    <string name="phoneTypeMms" msgid="7254492275502768992">"多媒體簡訊"</string>
     <string name="eventTypeCustom" msgid="7837586198458073404">"自訂"</string>
     <string name="eventTypeBirthday" msgid="2813379844211390740">"生日"</string>
     <string name="eventTypeAnniversary" msgid="3876779744518284000">"週年紀念日"</string>
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"<xliff:g id="COUNT">%d</xliff:g> 秒後再試一次"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"稍後再試"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"從頂端往下滑動即可結束全螢幕"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"從頂端往下滑動即可結束全螢幕。"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4a4ffc5..57d59c3 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1649,5 +1649,5 @@
     <item quantity="other" msgid="4730868920742952817">"Zama futhi kumasekhondi angu-<xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Zama futhi emva kwesikhathi"</string>
-    <string name="transient_navigation_confirmation" msgid="8554991488096662508">"Swayipha ngezansi kusuka ngaphezulu ukuze uphume kusikrini esigcwele"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"Swayiphela phansi kusukela phezulu ukuze uphume kusikrini esigcwele."</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9ee8bae..42e3b50 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2621,6 +2621,9 @@
         <!-- Fully qualified class name of an activity that allows the user to manually
              add printers to this print service. -->
         <attr name="addPrintersActivity" format="string"/>
+        <!-- Fully qualified class name of an activity with advanced print options
+             specific to this print service. -->
+        <attr name="advancedPrintOptionsActivity" format="string"/>
         <!-- The vendor name if this print service is vendor specific. -->
         <attr name="vendor" format="string"/>
     </declare-styleable>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index aad6252..f96195c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -53,7 +53,7 @@
     <!-- Minimum size of the fastscroll overlay -->
     <dimen name="fastscroll_overlay_size">104dp</dimen>
     <!-- Text size of the fastscroll overlay -->
-    <dimen name="fastscroll_overlay_text_size">24sp</dimen>
+    <dimen name="fastscroll_overlay_text_size">52sp</dimen>
     <!-- Padding of the fastscroll overlay -->
     <dimen name="fastscroll_overlay_padding">16dp</dimen>
     <!-- Width of the fastscroll thumb -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4635733..8187939 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2080,6 +2080,7 @@
   <public type="attr" name="accessibilityLiveRegion" id="0x010103ee" />
   <public type="attr" name="windowTranslucentStatus" id="0x010103ef" />
   <public type="attr" name="windowTranslucentNavigation" id="0x010103f0" />
+  <public type="attr" name="advancedPrintOptionsActivity" id="0x10103f1"/>
 
   <public type="style" name="Theme.Holo.NoActionBar.TranslucentDecor" id="0x010301e1" />
   <public type="style" name="Theme.Holo.Light.NoActionBar.TranslucentDecor" id="0x010301e2" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 611c085..f578694 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -939,7 +939,6 @@
   <java-symbol type="string" name="restr_pin_try_later" />
   <java-symbol type="string" name="write_fail_reason_cancelled" />
   <java-symbol type="string" name="write_fail_reason_cannot_write" />
-  <java-symbol type="string" name="transient_navigation_confirmation" />
   <java-symbol type="string" name="ssl_ca_cert_noti_by_unknown" />
   <java-symbol type="string" name="ssl_ca_cert_noti_managed" />
   <java-symbol type="string" name="ssl_ca_cert_warning" />
diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf
index 76fe737..91dd47f 100644
--- a/data/fonts/MTLmr3m.ttf
+++ b/data/fonts/MTLmr3m.ttf
Binary files differ
diff --git a/docs/html/design/videos/index.jd b/docs/html/design/videos/index.jd
index 8ddd4aa..91a784a 100644
--- a/docs/html/design/videos/index.jd
+++ b/docs/html/design/videos/index.jd
@@ -1,7 +1,70 @@
 page.title=Videos
 @jd:body
 
-<p>The Android Design Team was pleased to present five fantastic design-oriented sessions at Google I/O 2012. Visit these pages to view the videos and presentations from the conference.</p>
+<p>The Android Design Team presents design-oriented sessions at Google I/O every year. Visit these pages to view the videos and presentations from the conferences.</p>
+
+<img src="{@docRoot}images/home/io-logo-2013-alt.png">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326368573">Enchant, Simplify, Amaze: Android's Design Principles</a></h3>
+    <p>Want to enchant people, simplify their lives, and make them feel amazing with your app? Learn how Android's Design Principles can help you create products that resonate with people. Find out about the meaning and research behind the principles. See real-world examples and practices from the Android Design team. Discover techniques for applying the principles in your daily work. No design experience necessary.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="//www.youtube.com/embed/s0HIP8EdlnE" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326301704">Structure in Android App Design</a></h3>
+    <p>Life is simple when your app is simple. But when your apps gets more complex, how do you choose between spinners, tabs, and drawers for navigation? Members of the Android Design team look at techniques for making your app predictable and pleasing to use.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="//www.youtube.com/embed/XpqyiBR0lJ4" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326425499">Fireside Chat with the Android Team</a></h3>
+    <p>Pull up a chair and join the Android platform team for a fireside chat. It's your opportunity to ask us about the platform and learn a little bit more about why things work the way they do, from the people who built it. </p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="//www.youtube.com/embed/A5OOJDIrYls" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326483138">Agile UX Research Practice in Android</a></h3>
+    <p>In the Android UX team, it is critical to get user feedback frequently and consistently so that we are able to iterate and develop the best-in-class designs for our users. We will discuss how the team applied ""Pulse Studies"" (iterative research sessions) in order to put new ideas, designs, and concepts in front of users on a regular basis; it requires minimal advance planning, it can have an immediate product impact, and it can meet urgent needs. </p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="//www.youtube.com/embed/6MOeVNbh9cY" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326460111">Cognitive Science and Design</a></h3>
+    <p>This session will provide an in-depth look at human perception and cognition, and its implications for interactive and visual design. The human brain is purely treated as an information processing machine, and we will teach the audience its attributes, its advantages, its limitations, and generally how to hack it. </p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="//www.youtube.com/embed/z2exxj4COhU" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
 <img src="{@docRoot}design/media/extras_googleio_12.png">
 
 <div class="vspace size-2">&nbsp;</div>
diff --git a/docs/html/images/home/io-logo-2013-alt.png b/docs/html/images/home/io-logo-2013-alt.png
new file mode 100644
index 0000000..1cdb4f4
--- /dev/null
+++ b/docs/html/images/home/io-logo-2013-alt.png
Binary files differ
diff --git a/docs/html/training/animation/index.jd b/docs/html/training/animation/index.jd
index b2815fc..b6940f8 100644
--- a/docs/html/training/animation/index.jd
+++ b/docs/html/training/animation/index.jd
@@ -1,5 +1,5 @@
 page.title=Adding Animations
-page.tags="animation","views","layout","user interface"
+page.tags="Animator","views","layout","user interface"
 trainingnavtop=true
 startpage=true
 
diff --git a/docs/html/training/beam-files/index.jd b/docs/html/training/beam-files/index.jd
index 7155092..e4bac2e 100644
--- a/docs/html/training/beam-files/index.jd
+++ b/docs/html/training/beam-files/index.jd
@@ -1,4 +1,5 @@
 page.title=Sharing Files with NFC
+page.tags="NfcAdapter","Android Beam","share","file transfer"
 
 trainingnavtop=true
 startpage=true
diff --git a/docs/html/training/secure-file-sharing/index.jd b/docs/html/training/secure-file-sharing/index.jd
index 19a1042..aa009fc 100644
--- a/docs/html/training/secure-file-sharing/index.jd
+++ b/docs/html/training/secure-file-sharing/index.jd
@@ -1,4 +1,5 @@
 page.title=Sharing Files
+page.tags="FileProvider","share","ContentProvider"
 
 trainingnavtop=true
 startpage=true
diff --git a/docs/html/training/secure-file-sharing/setup-sharing.jd b/docs/html/training/secure-file-sharing/setup-sharing.jd
index d1ab993..8c8fa0f 100644
--- a/docs/html/training/secure-file-sharing/setup-sharing.jd
+++ b/docs/html/training/secure-file-sharing/setup-sharing.jd
@@ -30,6 +30,14 @@
     implementation of {@link android.support.v4.content.FileProvider} to your app, and how to
     specify the files you want to offer to other apps.
 </p>
+
+<p class="note">
+  <strong>Note:</strong> The {@link android.support.v4.content.FileProvider} class is part of the
+  <a href="{@docRoot}tools/support-library/features.html#v4">v4 Support Library</a>. For information
+  about including this library in your application, see
+  <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a>.
+</p>
+
 <h2 id="DefineProvider">Specify the FileProvider</h2>
 <p>
     Defining a {@link android.support.v4.content.FileProvider} for your app requires an entry in
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 7865ec8..6dbb3cd 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -812,6 +812,7 @@
         final OnClientUpdateListener l;
         synchronized(mInfoLock) {
             l = mOnClientUpdateListener;
+            mMetadataEditor = null;
         }
         if (l != null) {
             l.onClientChange(clearing);
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index a581e08..19d2ebe 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -20,5 +20,6 @@
         <item name="android:windowBackground">@*android:drawable/dialog_full_holo_light</item>
         <item name="android:colorBackgroundCacheHint">@null</item>
         <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowAnimationStyle">@*android:style/Animation.Holo.Dialog</item>
     </style>
 </resources>
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 99a4260..5169fef 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -16,6 +16,14 @@
             </intent-filter>
         </provider>
 
+        <receiver android:name=".MountReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_MOUNTED" />
+                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
+                <data android:scheme="file" />
+            </intent-filter>
+        </receiver>
+
         <!-- TODO: find a better place for tests to live -->
         <provider
             android:name=".TestDocumentsProvider"
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 189e985..d42354f 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,20 +16,25 @@
 
 package com.android.externalstorage;
 
+import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
 import android.graphics.Point;
-import android.media.ExifInterface;
 import android.os.CancellationSignal;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
+import android.util.Log;
 import android.webkit.MimeTypeMap;
 
+import com.android.internal.annotations.GuardedBy;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -44,6 +49,8 @@
 public class ExternalStorageProvider extends DocumentsProvider {
     private static final String TAG = "ExternalStorage";
 
+    public static final String AUTHORITY = "com.android.externalstorage.documents";
+
     // docId format: root:path/to/file
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
@@ -63,42 +70,91 @@
         public String docId;
     }
 
+    private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+
+    private StorageManager mStorageManager;
+
+    private final Object mRootsLock = new Object();
+
+    @GuardedBy("mRootsLock")
     private ArrayList<RootInfo> mRoots;
+    @GuardedBy("mRootsLock")
     private HashMap<String, RootInfo> mIdToRoot;
+    @GuardedBy("mRootsLock")
     private HashMap<String, File> mIdToPath;
 
     @Override
     public boolean onCreate() {
+        mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
+
         mRoots = Lists.newArrayList();
         mIdToRoot = Maps.newHashMap();
         mIdToPath = Maps.newHashMap();
 
-        // TODO: support multiple storage devices, requiring that volume serial
-        // number be burned into rootId so we can identify files from different
-        // volumes. currently we only use a static rootId for emulated storage,
-        // since that storage never changes.
-        if (!Environment.isExternalStorageEmulated()) return true;
-
-        try {
-            final String rootId = "primary";
-            final File path = Environment.getExternalStorageDirectory();
-            mIdToPath.put(rootId, path);
-
-            final RootInfo root = new RootInfo();
-            root.rootId = rootId;
-            root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
-                    | Root.FLAG_SUPPORTS_SEARCH;
-            root.title = getContext().getString(R.string.root_internal_storage);
-            root.docId = getDocIdForFile(path);
-            mRoots.add(root);
-            mIdToRoot.put(rootId, root);
-        } catch (FileNotFoundException e) {
-            throw new IllegalStateException(e);
-        }
+        updateVolumes();
 
         return true;
     }
 
+    public void updateVolumes() {
+        synchronized (mRootsLock) {
+            updateVolumesLocked();
+        }
+    }
+
+    private void updateVolumesLocked() {
+        mRoots.clear();
+        mIdToPath.clear();
+        mIdToRoot.clear();
+
+        final StorageVolume[] volumes = mStorageManager.getVolumeList();
+        for (StorageVolume volume : volumes) {
+            final boolean mounted = Environment.MEDIA_MOUNTED.equals(volume.getState())
+                    || Environment.MEDIA_MOUNTED_READ_ONLY.equals(volume.getState());
+            if (!mounted) continue;
+
+            final String rootId;
+            if (volume.isPrimary() && volume.isEmulated()) {
+                rootId = ROOT_ID_PRIMARY_EMULATED;
+            } else if (volume.getUuid() != null) {
+                rootId = volume.getUuid();
+            } else {
+                Log.d(TAG, "Missing UUID for " + volume.getPath() + "; skipping");
+                continue;
+            }
+
+            if (mIdToPath.containsKey(rootId)) {
+                Log.w(TAG, "Duplicate UUID " + rootId + "; skipping");
+                continue;
+            }
+
+            try {
+                final File path = volume.getPathFile();
+                mIdToPath.put(rootId, path);
+
+                final RootInfo root = new RootInfo();
+                root.rootId = rootId;
+                root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
+                        | Root.FLAG_SUPPORTS_SEARCH;
+                if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) {
+                    root.title = getContext().getString(R.string.root_internal_storage);
+                } else {
+                    root.title = volume.getUserLabel();
+                }
+                root.docId = getDocIdForFile(path);
+                mRoots.add(root);
+                mIdToRoot.put(rootId, root);
+            } catch (FileNotFoundException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        Log.d(TAG, "After updating volumes, found " + mRoots.size() + " active roots");
+
+        getContext().getContentResolver()
+                .notifyChange(DocumentsContract.buildRootsUri(AUTHORITY), null, false);
+    }
+
     private static String[] resolveRootProjection(String[] projection) {
         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
     }
@@ -112,11 +168,13 @@
 
         // Find the most-specific root path
         Map.Entry<String, File> mostSpecific = null;
-        for (Map.Entry<String, File> root : mIdToPath.entrySet()) {
-            final String rootPath = root.getValue().getPath();
-            if (path.startsWith(rootPath) && (mostSpecific == null
-                    || rootPath.length() > mostSpecific.getValue().getPath().length())) {
-                mostSpecific = root;
+        synchronized (mRootsLock) {
+            for (Map.Entry<String, File> root : mIdToPath.entrySet()) {
+                final String rootPath = root.getValue().getPath();
+                if (path.startsWith(rootPath) && (mostSpecific == null
+                        || rootPath.length() > mostSpecific.getValue().getPath().length())) {
+                    mostSpecific = root;
+                }
             }
         }
 
@@ -142,7 +200,10 @@
         final String tag = docId.substring(0, splitIndex);
         final String path = docId.substring(splitIndex + 1);
 
-        File target = mIdToPath.get(tag);
+        File target;
+        synchronized (mRootsLock) {
+            target = mIdToPath.get(tag);
+        }
         if (target == null) {
             throw new FileNotFoundException("No root for " + tag);
         }
@@ -198,16 +259,18 @@
     @Override
     public Cursor queryRoots(String[] projection) throws FileNotFoundException {
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
-        for (String rootId : mIdToPath.keySet()) {
-            final RootInfo root = mIdToRoot.get(rootId);
-            final File path = mIdToPath.get(rootId);
+        synchronized (mRootsLock) {
+            for (String rootId : mIdToPath.keySet()) {
+                final RootInfo root = mIdToRoot.get(rootId);
+                final File path = mIdToPath.get(rootId);
 
-            final RowBuilder row = result.newRow();
-            row.add(Root.COLUMN_ROOT_ID, root.rootId);
-            row.add(Root.COLUMN_FLAGS, root.flags);
-            row.add(Root.COLUMN_TITLE, root.title);
-            row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
-            row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
+                final RowBuilder row = result.newRow();
+                row.add(Root.COLUMN_ROOT_ID, root.rootId);
+                row.add(Root.COLUMN_FLAGS, root.flags);
+                row.add(Root.COLUMN_TITLE, root.title);
+                row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
+                row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
+            }
         }
         return result;
     }
@@ -276,7 +339,11 @@
     public Cursor querySearchDocuments(String rootId, String query, String[] projection)
             throws FileNotFoundException {
         final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
-        final File parent = mIdToPath.get(rootId);
+
+        final File parent;
+        synchronized (mRootsLock) {
+            parent = mIdToPath.get(rootId);
+        }
 
         final LinkedList<File> pending = new LinkedList<File>();
         pending.add(parent);
@@ -313,19 +380,7 @@
             String documentId, Point sizeHint, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
-        final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
-                file, ParcelFileDescriptor.MODE_READ_ONLY);
-
-        try {
-            final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
-            final long[] thumb = exif.getThumbnailRange();
-            if (thumb != null) {
-                return new AssetFileDescriptor(pfd, thumb[0], thumb[1]);
-            }
-        } catch (IOException e) {
-        }
-
-        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+        return DocumentsContract.openImageThumbnail(file);
     }
 
     private static String getTypeForFile(File file) {
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java
new file mode 100644
index 0000000..8a6c7d6
--- /dev/null
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/MountReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.externalstorage;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+
+public class MountReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final ContentProviderClient client = context.getContentResolver()
+                .acquireContentProviderClient(ExternalStorageProvider.AUTHORITY);
+        try {
+            ((ExternalStorageProvider) client.getLocalContentProvider()).updateVolumes();
+        } finally {
+            ContentProviderClient.releaseQuietly(client);
+        }
+    }
+}
diff --git a/packages/FusedLocation/res/values-pt-rPT/strings.xml b/packages/FusedLocation/res/values-pt-rPT/strings.xml
index 0d2cccc..2707fa9 100644
--- a/packages/FusedLocation/res/values-pt-rPT/strings.xml
+++ b/packages/FusedLocation/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="5379477904423203699">"Fused Location"</string>
+    <string name="app_label" msgid="5379477904423203699">"Localização Fundida"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ka-rGE/strings.xml b/packages/InputDevices/res/values-ka-rGE/strings.xml
index 17ca302..6e507aa 100644
--- a/packages/InputDevices/res/values-ka-rGE/strings.xml
+++ b/packages/InputDevices/res/values-ka-rGE/strings.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="8016145283189546017">"Input Devices"</string>
+    <string name="app_label" msgid="8016145283189546017">"შეყვანის მოწყობილობები"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Android-ის კლავიატურა"</string>
-    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"ინგლისური (ბრიტ.)"</string>
+    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"ინგლისური (გართ. სამ.)"</string>
     <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"ინგლისური (აშშ)"</string>
     <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"ინგლისური (აშშ), საერთაშორისო სტილი"</string>
-    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ანგლისური (აშშ), Colemak სტილი"</string>
+    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"ინგლისური (აშშ), Colemak სტილი"</string>
     <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"ინგლისური (აშშ), Dvorak სტილი"</string>
     <string name="keyboard_layout_german_label" msgid="8451565865467909999">"გერმანული"</string>
     <string name="keyboard_layout_french_label" msgid="813450119589383723">"ფრანგული"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 5a80358..a4cbfd7 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -5,19 +5,19 @@
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Клавиатура Android"</string>
     <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"английский (Великобритания)"</string>
     <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"английский (США)"</string>
-    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"English (US), International style"</string>
-    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"English (US), Colemak style"</string>
-    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"English (US), Dvorak style"</string>
+    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"английский (США, международная)"</string>
+    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"английский (США, Colemak)"</string>
+    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"английский (США, Dvorak)"</string>
     <string name="keyboard_layout_german_label" msgid="8451565865467909999">"немецкий"</string>
     <string name="keyboard_layout_french_label" msgid="813450119589383723">"французский"</string>
-    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Французский (Канада)"</string>
+    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"французский (Канада)"</string>
     <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"русский"</string>
-    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"Russian, Mac style"</string>
+    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"русский (Mac)"</string>
     <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"испанский"</string>
-    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"швейцарский французский"</string>
-    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швейцарский немецкий"</string>
-    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Нидерландский (Бельгия)"</string>
-    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгарский"</string>
+    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"французский (Швейцария)"</string>
+    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"немецкий (Швейцария)"</string>
+    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"нидерландский (Бельгия)"</string>
+    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарский"</string>
     <string name="keyboard_layout_italian" msgid="6497079660449781213">"итальянский"</string>
     <string name="keyboard_layout_danish" msgid="8036432066627127851">"датский"</string>
     <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежский"</string>
@@ -28,7 +28,7 @@
     <string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string>
     <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string>
     <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string>
-    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Португальский (Бразилия)"</string>
+    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"португальский (Бразилия)"</string>
     <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"португальский"</string>
     <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацкий"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенский"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index fdc06a6..e94cf18 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -16,10 +16,6 @@
 
 package com.android.keyguard;
 
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
-
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -55,6 +51,9 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.RemoteViews.OnClickHandler;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
 
 import java.io.File;
 import java.lang.ref.WeakReference;
@@ -280,7 +279,7 @@
             if (newState != mTransportState) {
                 mTransportState = newState;
                 if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
-                KeyguardHostView.this.post(mSwitchPageRunnable);
+                KeyguardHostView.this.postShowAppropriateWidgetPage();
             }
         }
         @Override
@@ -292,7 +291,7 @@
                 if (newState != mTransportState) {
                     mTransportState = newState;
                     if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
-                    KeyguardHostView.this.post(mSwitchPageRunnable);
+                    KeyguardHostView.this.postShowAppropriateWidgetPage();
                 }
             }
         }
@@ -496,6 +495,7 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
+        removeCallbacks(mSwitchPageRunnable);
         mAppWidgetHost.stopListening();
         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
     }
@@ -1438,7 +1438,7 @@
         mAppWidgetToShow = ss.appWidgetToShow;
         setInsets(ss.insets);
         if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
-        post(mSwitchPageRunnable);
+        postShowAppropriateWidgetPage();
     }
 
     @Override
@@ -1471,13 +1471,22 @@
         }
     }
 
-    private void showAppropriateWidgetPage() {
+    void showAppropriateWidgetPage() {
         int state = mTransportState;
         ensureTransportPresentOrRemoved(state);
+        if (mAppWidgetContainer.isLayoutRequested()) {
+            postShowAppropriateWidgetPage();
+            return;
+        }
         int pageToShow = getAppropriateWidgetPage(state);
         mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
+    void postShowAppropriateWidgetPage() {
+        removeCallbacks(mSwitchPageRunnable);
+        post(mSwitchPageRunnable);
+    }
+
     /**
      * Examines the current state and adds the transport to the widget pager when the state changes.
      *
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index d7c5fe2..36b2446 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -141,6 +141,10 @@
             checkPermission();
             mKeyguardViewMediator.launchCamera();
         }
+        public void onBootCompleted() {
+            checkPermission();
+            mKeyguardViewMediator.onBootCompleted();
+        }
     };
 
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
index b7720ef..a7af6a4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -101,9 +101,7 @@
             new RemoteController.OnClientUpdateListener() {
         @Override
         public void onClientChange(boolean clearing) {
-            if (clearing) {
-                clearMetadata();
-            }
+            clearMetadata();
         }
 
         @Override
@@ -302,6 +300,7 @@
             mPopulateMetadataWhenAttached = null;
         }
         if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+        mMetadata.clear();
         mAudioManager.registerRemoteController(mRemoteController);
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor);
     }
@@ -321,7 +320,9 @@
         if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
         mAudioManager.unregisterRemoteController(mRemoteController);
         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
+        mMetadata.clear();
         mUserSeeking = false;
+        removeCallbacks(mUpdateSeekBars);
     }
 
     void setBadgeIcon(Drawable bmp) {
@@ -394,10 +395,10 @@
             Log.e(TAG, "Couldn't get remote control client package icon", e);
         }
         setBadgeIcon(badgeIcon);
-        if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
-            mTrackTitle.setText(mMetadata.trackTitle);
-        }
-        StringBuilder sb = new StringBuilder();
+        mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle)
+                ? mMetadata.trackTitle : null);
+
+        final StringBuilder sb = new StringBuilder();
         if (!TextUtils.isEmpty(mMetadata.artist)) {
             if (sb.length() != 0) {
                 sb.append(" - ");
@@ -410,7 +411,10 @@
             }
             sb.append(mMetadata.albumTitle);
         }
-        mTrackArtistAlbum.setText(sb.toString());
+
+        final String trackArtistAlbum = sb.toString();
+        mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ?
+                trackArtistAlbum : null);
 
         if (mMetadata.duration >= 0) {
             setSeekBarsEnabled(true);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 45cd3d4..520cea3 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -635,15 +635,14 @@
      * PhoneWindowManager in this case.
      */
     protected void dispatchBootCompleted() {
-        if (!mBootCompleted) {
-            mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
-        }
+        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
     }
 
     /**
      * Handle {@link #MSG_BOOT_COMPLETED}
      */
     protected void handleBootCompleted() {
+        if (mBootCompleted) return;
         mBootCompleted = true;
         mAudioManager = new AudioManager(mContext);
         mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index a0cad84..fd7cae6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -431,17 +431,13 @@
     public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) {
         if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
         mScreenOn = true;
-        final IBinder token;
 
-        // If keyguard is disabled, we need to inform PhoneWindowManager with a null
+        // If keyguard is not showing, we need to inform PhoneWindowManager with a null
         // token so it doesn't wait for us to draw...
-        final boolean disabled =
-                mLockPatternUtils.isLockScreenDisabled() && !mLockPatternUtils.isSecure();
-        if (mKeyguardHost == null || disabled) {
-            token = null;
-        } else {
-            token = mKeyguardHost.getWindowToken();
-        }
+        final IBinder token = isShowing() ? mKeyguardHost.getWindowToken() : null;
+
+        if (DEBUG && token == null) Slog.v(TAG, "send wm null token: "
+                + (mKeyguardHost == null ? "host was null" : "not showing"));
 
         if (mKeyguardView != null) {
             mKeyguardView.onScreenTurnedOn();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index a37a3a4..b92ae90 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -530,9 +530,6 @@
             mSystemReady = true;
             mUpdateMonitor.registerCallback(mUpdateCallback);
 
-            // Send boot completed message if it hasn't already been sent.
-            mUpdateMonitor.dispatchBootCompleted();
-
             // Suppress biometric unlock right after boot until things have settled if it is the
             // selected security method, otherwise unsuppress it.  It must be unsuppressed if it is
             // not the selected security method for the following reason:  if the user starts
@@ -1366,4 +1363,8 @@
         Message msg = mHandler.obtainMessage(LAUNCH_CAMERA);
         mHandler.sendMessage(msg);
     }
+
+    public void onBootCompleted() {
+        mUpdateMonitor.dispatchBootCompleted();
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index 704af6e..e07e0d0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -40,7 +40,6 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextClock;
-
 import com.android.internal.widget.LockPatternUtils;
 
 import java.util.ArrayList;
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
index 23488d4..9d237dc 100644
--- a/packages/Keyguard/src/com/android/keyguard/PagedView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -1427,7 +1427,10 @@
                 if (Math.abs(deltaX) >= 1.0f) {
                     mTouchX += deltaX;
                     mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                    if (!mDeferScrollUpdate) {
+                    if (isWarping()) {
+                        KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
+                        v.setTranslationX(v.getTranslationX() - deltaX);
+                    } else if (!mDeferScrollUpdate) {
                         scrollBy((int) deltaX, 0);
                         if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
                     } else {
@@ -1553,6 +1556,9 @@
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 final int activePointerId = mActivePointerId;
                 final int pointerIndex = ev.findPointerIndex(activePointerId);
+
+                if (pointerIndex == -1) return true;
+
                 final float x = ev.getX(pointerIndex);
                 final VelocityTracker velocityTracker = mVelocityTracker;
                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
@@ -1806,10 +1812,11 @@
     }
 
     protected void snapToDestination() {
+        final int newPage = getPageNearestToCenterOfScreen();
         if (isWarping()) {
-            cancelWarpAnimation("snapToDestination");
+            cancelWarpAnimation("snapToDestination", mCurrentPage != newPage);
         }
-        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+        snapToPage(newPage, getPageSnapDuration());
     }
 
     private int getPageSnapDuration() {
@@ -1841,7 +1848,7 @@
         int halfScreenSize = getViewportWidth() / 2;
 
         if (isWarping()) {
-            cancelWarpAnimation("snapToPageWithVelocity");
+            cancelWarpAnimation("snapToPageWithVelocity", mCurrentPage != whichPage);
         }
 
         if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
@@ -2699,13 +2706,19 @@
         }
     };
 
-    private void cancelWarpAnimation(String msg) {
+    private void cancelWarpAnimation(String msg, boolean abortAnimation) {
         if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ")");
-        // We're done with the animation, let the scroller take over the positioning
-        KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
-        v.animate().cancel();
-        v.setTranslationX(0f);
-        scrollBy((int) Math.round(v.getTranslationX() - mWarpPeekAmount), 0);
+        if (abortAnimation) {
+            // We're done with the animation and moving to a new page.  Let the scroller
+            // take over the animation.
+            KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
+            v.animate().cancel();
+            // Make the scroll amount match the current warp position.
+            scrollBy(Math.round(-v.getTranslationX()), 0);
+            v.setTranslationX(0);
+        } else {
+            animateWarpPageOffScreen("canceled", true);
+        }
     }
 
     private boolean isAnimatingWarpPage() {
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 2749aa6..1a61b99 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -15,13 +15,13 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="16dip"
-        android:paddingEnd="16dip"
-        android:minHeight="?android:attr/listPreferredItemHeightSmall"
-        android:orientation="horizontal"
-        android:gravity="start|center_vertical">
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:paddingStart="16dip"
+      android:paddingEnd="16dip"
+      android:minHeight="?android:attr/listPreferredItemHeightSmall"
+      android:orientation="horizontal"
+      android:gravity="start|center_vertical">
 
     <ImageView
         android:id="@+id/icon"
@@ -31,7 +31,7 @@
         android:layout_marginEnd="8dip"
         android:duplicateParentState="true"
         android:contentDescription="@null"
-        android:visibility="gone">
+        android:visibility="invisible">
     </ImageView>
 
     <LinearLayout
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
new file mode 100644
index 0000000..47eb0b5
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:orientation="horizontal"
+        android:gravity="start|center_vertical">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="32dip"
+        android:layout_height="32dip"
+        android:layout_gravity="center_vertical"
+        android:layout_marginEnd="8dip"
+        android:duplicateParentState="true"
+        android:contentDescription="@null"
+        android:visibility="gone">
+    </ImageView>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:duplicateParentState="true">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textIsSelectable="false"
+            android:gravity="top|start"
+            android:textColor="@color/item_text_color"
+            android:duplicateParentState="true">
+        </TextView>
+
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textIsSelectable="false"
+            android:visibility="gone"
+            android:textColor="@color/print_option_title"
+            android:duplicateParentState="true">
+        </TextView>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/select_printer_fragment.xml b/packages/PrintSpooler/res/layout/select_printer_fragment.xml
new file mode 100644
index 0000000..bbd012e
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/select_printer_fragment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/list"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:paddingStart="@dimen/printer_list_view_padding_start"
+    android:paddingEnd="@dimen/printer_list_view_padding_end"
+    android:scrollbarStyle="outsideOverlay"
+    android:cacheColorHint="@android:color/transparent"
+    android:scrollbarAlwaysDrawVerticalTrack="true" >
+</ListView>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 76666fc..a6ad391 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -52,8 +52,8 @@
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Drukkerfout by <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drukker het <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> geblokkeer"</string>
   <plurals name="composite_notification_title_template">
-    <item quantity="one" msgid="5866624638054847057">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>-druktaak"</item>
-    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>-druktake"</item>
+    <item quantity="one" msgid="5866624638054847057">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>-uitdruktaak"</item>
+    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>-uitdruktake"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Kanselleer"</string>
     <string name="restart" msgid="2472034227037808749">"Herbegin"</string>
diff --git a/packages/PrintSpooler/res/values-be/arrays.xml b/packages/PrintSpooler/res/values-be/arrays.xml
new file mode 100644
index 0000000..d40278c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-be/arrays.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>NA_LETTER</item>
+        <item>NA_GOVT_LETTER</item>
+        <item>NA_LEGAL</item>
+        <item>NA_JUNIOR_LEGAL</item>
+        <item>NA_LEDGER</item>
+        <item>NA_TABLOID</item>
+        <item>NA_INDEX_3X5</item>
+        <item>NA_INDEX_4X6</item>
+        <item>NA_INDEX_5X8</item>
+        <item>NA_MONARCH</item>
+        <item>NA_QUARTO</item>
+        <item>NA_FOOLSCAP</item>
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-ca/arrays.xml b/packages/PrintSpooler/res/values-ca/arrays.xml
new file mode 100644
index 0000000..d40278c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-ca/arrays.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>NA_LETTER</item>
+        <item>NA_GOVT_LETTER</item>
+        <item>NA_LEGAL</item>
+        <item>NA_JUNIOR_LEGAL</item>
+        <item>NA_LEDGER</item>
+        <item>NA_TABLOID</item>
+        <item>NA_INDEX_3X5</item>
+        <item>NA_INDEX_4X6</item>
+        <item>NA_INDEX_5X8</item>
+        <item>NA_MONARCH</item>
+        <item>NA_QUARTO</item>
+        <item>NA_FOOLSCAP</item>
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 4071249..3ad5892 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -25,8 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientació"</string>
     <string name="label_pages" msgid="6300874667546617333">"Pàgines (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <!-- no translation found for pages_range_example (8558694453556945172) -->
-    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Visualització prèvia impressió"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Instal·la un lector de PDF per a visualitz. prèvia"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"L\'aplicació d\'impressió ha fallat"</string>
@@ -61,8 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"desconegut"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
-    <!-- no translation found for print_error_default_message (8568506918983980567) -->
-    <skip />
+    <string name="print_error_default_message" msgid="8568506918983980567">"No s\'ha pogut generar la tasca d\'impressió"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Blanc i negre"</item>
     <item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-es-rUS/arrays.xml b/packages/PrintSpooler/res/values-es-rUS/arrays.xml
new file mode 100644
index 0000000..d40278c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-es-rUS/arrays.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>NA_LETTER</item>
+        <item>NA_GOVT_LETTER</item>
+        <item>NA_LEGAL</item>
+        <item>NA_JUNIOR_LEGAL</item>
+        <item>NA_LEDGER</item>
+        <item>NA_TABLOID</item>
+        <item>NA_INDEX_3X5</item>
+        <item>NA_INDEX_4X6</item>
+        <item>NA_INDEX_5X8</item>
+        <item>NA_MONARCH</item>
+        <item>NA_QUARTO</item>
+        <item>NA_FOOLSCAP</item>
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 549077b..891ccf2 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
     <string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"p. ej.: 1–5, 8, 11–13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"Ej.: 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Vista previa de impresión"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Instalar visualizador de PDF para vista previa"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"La aplicación de impresión falló"</string>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index b5dfb0b..052e06c 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
     <string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"p.ej.: 1-5, 8, 11-13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"p. ej.: 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Vista previa de impresión"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Instalar visor PDF para obtener vista previa"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Error de aplicación de impresión"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index e07ba07..581f2a6 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Couleur"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
     <string name="label_pages" msgid="6300874667546617333">"Pages (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"p.ex. 1-5, 8, 11-13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Aperçu avant impression"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Installer un lecteur PDF pour voir l\'aperçu"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"L\'application à l\'origine de l\'impression a planté"</string>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 90048c9..247a21b 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -52,8 +52,8 @@
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression pour \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" bloquée"</string>
   <plurals name="composite_notification_title_template">
-    <item quantity="one" msgid="5866624638054847057">"Tâche d\'impression <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
-    <item quantity="other" msgid="8746611264734222865">"Tâches d\'impression <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
+    <item quantity="one" msgid="5866624638054847057">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> tâche d\'impression"</item>
+    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> tâches d\'impression"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuler"</string>
     <string name="restart" msgid="2472034227037808749">"Redémarrer"</string>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index ae29fa4..a142aa3 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -25,8 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Warna"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
     <string name="label_pages" msgid="6300874667546617333">"(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>) halaman"</string>
-    <!-- no translation found for pages_range_example (8558694453556945172) -->
-    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"misalnya 1—5,8,11—13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Pratinjau cetak"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Pasang penampil PDF untuk pratinjau"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Aplikasi pencetakan mogok"</string>
@@ -61,8 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"tak diketahui"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
-    <!-- no translation found for print_error_default_message (8568506918983980567) -->
-    <skip />
+    <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat membuat tugas cetak"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Hitam &amp; Putih"</item>
     <item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-ja/arrays.xml b/packages/PrintSpooler/res/values-ja/arrays.xml
new file mode 100644
index 0000000..57088c8
--- /dev/null
+++ b/packages/PrintSpooler/res/values-ja/arrays.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>JIS_B10</item>
+        <item>JIS_B9</item>
+        <item>JIS_B8</item>
+        <item>JIS_B7</item>
+        <item>JIS_b6</item>
+        <item>JIS_b5</item>
+        <item>JIS_b4</item>
+        <item>JIS_b3</item>
+        <item>JIS_b2</item>
+        <item>JIS_b1</item>
+        <item>JIS_b0</item>
+        <item>JIS_EXEC</item>
+        <item>JPN_CHOU4</item>
+        <item>JPN_CHOU3</item>
+        <item>JPN_CHOU2</item>
+        <item>JPN_HAGAKI</item>
+        <item>JPN_OUFUKU</item>
+        <item>JPN_KAHU</item>
+        <item>JPN_KAKU2</item>
+        <item>JPN_YOU4</item>
+
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
new file mode 100644
index 0000000..d68b77e
--- /dev/null
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<resources>
+
+    <dimen name="printer_list_view_padding_start">48dip</dimen>
+    <dimen name="printer_list_view_padding_end">48dip</dimen>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 44ec159..62af20b 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -52,8 +52,8 @@
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printera kļūda ar darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeris bloķēja darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
   <plurals name="composite_notification_title_template">
-    <item quantity="one" msgid="5866624638054847057">"Drukas darbs: <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
-    <item quantity="other" msgid="8746611264734222865">"Drukas darbi: <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
+    <item quantity="one" msgid="5866624638054847057">"Drukas darbs <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
+    <item quantity="other" msgid="8746611264734222865">"Drukas darbi <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Atcelt"</string>
     <string name="restart" msgid="2472034227037808749">"Restartēt"</string>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index 5d19cf3..0a64954 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Өнгө"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Чиглэл"</string>
     <string name="label_pages" msgid="6300874667546617333">"(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>) хуудас"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"жнь. 1–5, 8, 11–13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"ж.нь. 1–5, 8, 11–13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Хэвлэхээр урьдчилан харах"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Урьдчилан харахын тулд PDF харагчийг суулгах"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Хэвлэгч апп гацсан"</string>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index bd205d5..8bf0083 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -52,15 +52,15 @@
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ralat pencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Pencetak disekat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
   <plurals name="composite_notification_title_template">
-    <item quantity="one" msgid="5866624638054847057">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> kerja cetakan"</item>
-    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> kerja cetakan"</item>
+    <item quantity="one" msgid="5866624638054847057">"Kerja cetakan <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
+    <item quantity="other" msgid="8746611264734222865">"Kerja cetakan <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Batal"</string>
     <string name="restart" msgid="2472034227037808749">"Mulakan semula"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"tidak diketahui"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
-    <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat menjaga kerja cetakan"</string>
+    <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat menjana kerja cetakan"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Hitam &amp; Putih"</item>
     <item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index ae0dee2..577d316 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Cor"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
     <string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1—5, 8, 11—13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Pré-visualização de impressão"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Instalar o leitor de PDF para pré-visualização"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"A aplicação de impressão bloqueou"</string>
@@ -52,8 +52,8 @@
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
   <plurals name="composite_notification_title_template">
-    <item quantity="one" msgid="5866624638054847057">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> tarefa de impressão"</item>
-    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> tarefas de impressão"</item>
+    <item quantity="one" msgid="5866624638054847057">"Tarefa de impressão: <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
+    <item quantity="other" msgid="8746611264734222865">"Tarefas de impressão: <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 699de3c..82a8c8c 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -25,7 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientare"</string>
     <string name="label_pages" msgid="6300874667546617333">"Pagini (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <string name="pages_range_example" msgid="8558694453556945172">"de ex.: 1-5, 8, 11-13"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"de ex. 1-5, 8, 11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Previzualizați printarea"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Instalați PDF viewer pentru previzualizare"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Aplicația de printare s-a blocat"</string>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index dd5ad3b..3d4cf6a 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -37,12 +37,12 @@
     <string name="search" msgid="5421724265322228497">"Поиск"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Все принтеры"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Добавить службу печати"</string>
-    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Открыто окно поиска"</string>
+    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Окно поиска показано"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Окно поиска скрыто"</string>
     <string name="print_add_printer" msgid="1088656468360653455">"Добавить принтер"</string>
   <plurals name="print_search_result_count_utterance">
     <item quantity="one" msgid="4484953260685964252">"Найден <xliff:g id="COUNT">%1$s</xliff:g> принтер"</item>
-    <item quantity="other" msgid="6533817036607128241">"Найдено несколько принтеров (<xliff:g id="COUNT">%1$s</xliff:g>)"</item>
+    <item quantity="other" msgid="6533817036607128241">"Найдено принтеров: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
   </plurals>
     <string name="choose_print_service" msgid="3740309762324459694">"Выберите службу печати"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Поиск принтеров…"</string>
@@ -60,7 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"неизвестно"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступен"</string>
-    <string name="print_error_default_message" msgid="8568506918983980567">"Не удалось создать задание печати."</string>
+    <string name="print_error_default_message" msgid="8568506918983980567">"Не удалось отправить документ на печать."</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Черно-белое"</item>
     <item msgid="2762241247228983754">"Цветное"</item>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 3176358..6135a19 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -25,8 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Farba"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Orientácia"</string>
     <string name="label_pages" msgid="6300874667546617333">"STRÁNKY (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <!-- no translation found for pages_range_example (8558694453556945172) -->
-    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"napr. 1–5, 8, 11–13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Ukážka pred tlačou"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Inštalovať zobrazovač PDF na zobrazenie ukážky"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Aplikácia pre tlač zlyhala"</string>
@@ -61,8 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"neznáme"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie je k dispozícii"</string>
-    <!-- no translation found for print_error_default_message (8568506918983980567) -->
-    <skip />
+    <string name="print_error_default_message" msgid="8568506918983980567">"Tlačovú úlohu nie je možné vytvoriť"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Čiernobiele"</item>
     <item msgid="2762241247228983754">"Farba"</item>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index ee6a47f..a09fa4c 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -53,7 +53,7 @@
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Штампач је блокирао <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
   <plurals name="composite_notification_title_template">
     <item quantity="one" msgid="5866624638054847057">"Задатак штампања <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
-    <item quantity="other" msgid="8746611264734222865">"<xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g> задат(а)ка штампања"</item>
+    <item quantity="other" msgid="8746611264734222865">"Задаци штампања <xliff:g id="PRINT_JOB_NAME">%1$d</xliff:g>"</item>
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"Откажи"</string>
     <string name="restart" msgid="2472034227037808749">"Поново покрени"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 62509db..b8842e7 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -60,7 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"haijulikani"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - haipatikani"</string>
-    <string name="print_error_default_message" msgid="8568506918983980567">"Haikuweza kuzalisha kazi ya kuchapisha"</string>
+    <string name="print_error_default_message" msgid="8568506918983980567">"Haikuweza kuunda kazi ya kuchapisha"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Nyeusi na Nyeupe"</item>
     <item msgid="2762241247228983754">"Rangi"</item>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 5ef6530..ab82666 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -25,8 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"Renkli"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Sayfa yönü"</string>
     <string name="label_pages" msgid="6300874667546617333">"Sayfa (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <!-- no translation found for pages_range_example (8558694453556945172) -->
-    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"ör. 1-5,8,11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Yazdırmayı önizle"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"Önizlemek için PDF görüntüleyici yükleyin"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Yazdırma uygulaması kilitlendi"</string>
@@ -61,8 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"bilinmiyor"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – kullanılamıyor"</string>
-    <!-- no translation found for print_error_default_message (8568506918983980567) -->
-    <skip />
+    <string name="print_error_default_message" msgid="8568506918983980567">"Yazdırma işi oluşturulamadı"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"Siyah Beyaz"</item>
     <item msgid="2762241247228983754">"Renkli"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/arrays.xml b/packages/PrintSpooler/res/values-zh-rCN/arrays.xml
new file mode 100644
index 0000000..4fc75db
--- /dev/null
+++ b/packages/PrintSpooler/res/values-zh-rCN/arrays.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>ROC_8K</item>
+        <item>ROC_16K</item>
+        <item>PRC_1</item>
+        <item>PRC_2</item>
+        <item>PRC_3</item>
+        <item>PRC_4</item>
+        <item>PRC_5</item>
+        <item>PRC_6</item>
+        <item>PRC_7</item>
+        <item>PRC_8</item>
+        <item>PRC_9</item>
+        <item>PRC_10</item>
+        <item>PRC_16K</item>
+        <item>OM_PA_KAI</item>
+        <item>OM_DAI_PA_KAI</item>
+        <item>OM_JUURO_KU_KAI</item>
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 5eb640f..606baea 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -25,8 +25,7 @@
     <string name="label_color" msgid="1108690305218188969">"颜色"</string>
     <string name="label_orientation" msgid="2853142581990496477">"方向"</string>
     <string name="label_pages" msgid="6300874667546617333">"页数 (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
-    <!-- no translation found for pages_range_example (8558694453556945172) -->
-    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"例如:1-5、8、11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"打印预览"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"安装 PDF 查看器以便预览"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"打印应用崩溃了"</string>
@@ -61,8 +60,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"未知"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - 无法使用"</string>
-    <!-- no translation found for print_error_default_message (8568506918983980567) -->
-    <skip />
+    <string name="print_error_default_message" msgid="8568506918983980567">"无法生成打印作业"</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"黑白"</item>
     <item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index e5a9d5d..e9c925c 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -26,4 +26,7 @@
 
     <dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
 
-</resources>
\ No newline at end of file
+    <dimen name="printer_list_view_padding_start">16dip</dimen>
+    <dimen name="printer_list_view_padding_end">16dip</dimen>
+
+</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 3a23b3e..ee5ff16 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -2175,10 +2175,17 @@
                 mIsPosted = false;
                 if (mDestinationSpinner.getSelectedItemPosition() >= 0) {
                     View itemView = mDestinationSpinner.getSelectedView();
-                    TextView titleView = (TextView) itemView.findViewById(R.id.title);
-                    String title = getString(R.string.printer_unavailable,
-                            mCurrentPrinter.getName());
-                    titleView.setText(title);
+                    TextView titleView = (TextView) itemView.findViewById(R.id.subtitle);
+                    try {
+                        PackageInfo packageInfo = getPackageManager().getPackageInfo(
+                                mCurrentPrinter.getId().getServiceName().getPackageName(), 0);
+                        CharSequence service = packageInfo.applicationInfo.loadLabel(
+                                getPackageManager());
+                        String subtitle = getString(R.string.printer_unavailable, service.toString());
+                        titleView.setText(subtitle);
+                    } catch (NameNotFoundException nnfe) {
+                        /* ignore */
+                    }
                 }
             }
         }
@@ -2296,8 +2303,6 @@
                             R.layout.printer_dropdown_item, parent, false);
                 }
 
-                convertView.getLayoutParams().width = mDestinationSpinner.getWidth();
-
                 CharSequence title = null;
                 CharSequence subtitle = null;
                 Drawable icon = null;
@@ -2346,7 +2351,7 @@
                     iconView.setImageDrawable(icon);
                     iconView.setVisibility(View.VISIBLE);
                 } else {
-                    iconView.setVisibility(View.GONE);
+                    iconView.setVisibility(View.INVISIBLE);
                 }
 
                 return convertView;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
index be94ba4..204c152 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -22,7 +22,6 @@
 import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.FragmentTransaction;
-import android.app.ListFragment;
 import android.app.LoaderManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -47,12 +46,14 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.BaseAdapter;
 import android.widget.Filter;
@@ -68,7 +69,7 @@
 /**
  * This is a fragment for selecting a printer.
  */
-public final class SelectPrinterFragment extends ListFragment {
+public final class SelectPrinterFragment extends Fragment {
 
     private static final String LOG_TAG = "SelectPrinterFragment";
 
@@ -83,6 +84,8 @@
     private final ArrayList<PrintServiceInfo> mAddPrinterServices =
             new ArrayList<PrintServiceInfo>();
 
+    private ListView mListView;
+
     private AnnounceFilterResult mAnnounceFilterResult;
 
     public static interface OnPrinterSelectedListener {
@@ -97,8 +100,12 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View content = inflater.inflate(R.layout.select_printer_fragment, container, false);
+
+        // Hook up the list view.
+        mListView = (ListView) content.findViewById(android.R.id.list);
         final DestinationAdapter adapter = new DestinationAdapter();
         adapter.registerDataSetObserver(new DataSetObserver() {
             @Override
@@ -115,7 +122,23 @@
                 }
             }
         });
-        setListAdapter(adapter);
+        mListView.setAdapter(adapter);
+
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+                Activity activity = getActivity();
+                if (activity instanceof OnPrinterSelectedListener) {
+                    ((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
+                } else {
+                    throw new IllegalStateException("the host activity must implement"
+                            + " OnPrinterSelectedListener");
+                }
+            }
+        });
+
+        return content;
     }
 
     @Override
@@ -133,7 +156,7 @@
 
             @Override
             public boolean onQueryTextChange(String searchString) {
-                ((DestinationAdapter) getListAdapter()).getFilter().filter(searchString);
+                ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString);
                 return true;
             }
         });
@@ -177,18 +200,6 @@
     }
 
     @Override
-    public void onListItemClick(ListView list, View view, int position, long id) {
-        PrinterInfo printer = (PrinterInfo) list.getAdapter().getItem(position);
-        Activity activity = getActivity();
-        if (activity instanceof OnPrinterSelectedListener) {
-            ((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
-        } else {
-            throw new IllegalStateException("the host activity must implement"
-                    + " OnPrinterSelectedListener");
-        }
-    }
-
-    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         if (item.getItemId() == R.id.action_add_printer) {
             showAddPrinterSelectionDialog();
@@ -260,9 +271,9 @@
     }
 
     public void updateEmptyView(DestinationAdapter adapter) {
-        if (getListView().getEmptyView() == null) {
+        if (mListView.getEmptyView() == null) {
             View emptyView = getActivity().findViewById(R.id.empty_print_state);
-            getListView().setEmptyView(emptyView);
+            mListView.setEmptyView(emptyView);
         }
         TextView titleView = (TextView) getActivity().findViewById(R.id.title);
         View progressBar = getActivity().findViewById(R.id.progress_bar);
@@ -450,7 +461,7 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             if (convertView == null) {
                 convertView = getActivity().getLayoutInflater().inflate(
-                        R.layout.printer_dropdown_item, parent, false);
+                        R.layout.printer_list_item, parent, false);
             }
 
             convertView.setEnabled(isEnabled(position));
@@ -539,16 +550,16 @@
 
         public void post() {
             remove();
-            getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+            mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
         }
 
         public void remove() {
-            getListView().removeCallbacks(this);
+            mListView.removeCallbacks(this);
         }
 
         @Override
         public void run() {
-            final int count = getListView().getAdapter().getCount();
+            final int count = mListView.getAdapter().getCount();
             final String text;
             if (count <= 0) {
                 text = getString(R.string.print_no_printers);
@@ -556,7 +567,7 @@
                 text = getActivity().getResources().getQuantityString(
                     R.plurals.print_search_result_count_utterance, count, count);
             }
-            getListView().announceForAccessibility(text);
+            mListView.announceForAccessibility(text);
         }
     }
 }
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index ab2feb9..783aa03 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,7 +8,7 @@
                  android:process="system"
                  android:backupAgent="SettingsBackupAgent"
                  android:killAfterRestore="false"
-                 android:icon="@drawable/ic_launcher_settings">
+                 android:icon="@mipmap/ic_launcher_settings">
                  
     <!-- todo add: android:neverEncrypt="true" -->
 
diff --git a/packages/SettingsProvider/res/drawable-hdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/drawable-hdpi/ic_launcher_settings.png
deleted file mode 100644
index 8a5a2f7..0000000
--- a/packages/SettingsProvider/res/drawable-hdpi/ic_launcher_settings.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsProvider/res/drawable-mdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/drawable-mdpi/ic_launcher_settings.png
deleted file mode 100644
index 803439f..0000000
--- a/packages/SettingsProvider/res/drawable-mdpi/ic_launcher_settings.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsProvider/res/drawable-xhdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/drawable-xhdpi/ic_launcher_settings.png
deleted file mode 100644
index ec3c8ea..0000000
--- a/packages/SettingsProvider/res/drawable-xhdpi/ic_launcher_settings.png
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsProvider/res/mipmap-hdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/mipmap-hdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..a8ccc89
--- /dev/null
+++ b/packages/SettingsProvider/res/mipmap-hdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SettingsProvider/res/mipmap-mdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/mipmap-mdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..69709a8
--- /dev/null
+++ b/packages/SettingsProvider/res/mipmap-mdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SettingsProvider/res/mipmap-xhdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/mipmap-xhdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..c3adce61
--- /dev/null
+++ b/packages/SettingsProvider/res/mipmap-xhdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SettingsProvider/res/mipmap-xxhdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/mipmap-xxhdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..52fe978
--- /dev/null
+++ b/packages/SettingsProvider/res/mipmap-xxhdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SettingsProvider/res/mipmap-xxxhdpi/ic_launcher_settings.png b/packages/SettingsProvider/res/mipmap-xxxhdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..6b92795
--- /dev/null
+++ b/packages/SettingsProvider/res/mipmap-xxxhdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 260a3be..09ac2da 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -200,7 +200,8 @@
             android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
             android:hardwareAccelerated="true"
             android:launchMode="singleInstance"
-            android:configChanges="orientation|screenSize"
+            android:screenOrientation="locked"
+            android:process=":sweetsweetdesserts"
             android:excludeFromRecents="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -215,6 +216,7 @@
             android:exported="true"
             android:label="@string/dessert_case"
             android:enabled="false"
+            android:process=":sweetsweetdesserts"
             >
             <intent-filter>
                 <action android:name="android.service.dreams.DreamService" />
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_1.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_1.png
index e20fdf7..2e3e486 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_2.png
index 79c9099..04e8220 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_3.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_3.png
index 8cb1cec..f8ed8f0 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_3.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_signal_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_0.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_0.png
index 6aa522b..c416fc3 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_0.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_1.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_1.png
index 2afa621..93b45b4 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_2.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_2.png
index c873c55..21b2c61 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_2.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_3.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_3.png
index e3e0e8d..cd96ae0 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_3.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_4.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_4.png
index 2762df5..43bfe3a 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_4.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_full_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_not_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_not_connected.png
index c9e0a7c..c416fc3 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_not_connected.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_wifi_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_0.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_0.png
index d924756..49f9d8d 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_0.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_0.png
index 8177ef9..49f9d8d 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_2.png
index ec4b0a6..050dde5 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_0.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_0.png
index cdf76e2..ad699e2 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_0.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_0.png
index d41a2c7..ad699e2 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_2.png
index 2f21063..55e7125 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_0.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_0.png
index 10c6905..e50be70 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_0.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_0.png
index ef8c677..e50be70 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_2.png
index 8d3dede..5a69da2 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_0.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_0.png
index 5c378ef..5298d41 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_0.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_0.png
index 6db5d0d..a9c0849 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_0.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_2.png
index 4faff9b..3a1678f 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_1.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_1.png
index 52faded..1d063d7 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_2.png
index bcf825d..b8551ac 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_3.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_3.png
index f9de3ef..25e5586 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_signal_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_0.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_0.png
index 42210a6..b177999 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_1.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_1.png
index 092ddbb..b3318bc 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_2.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_2.png
index e10a1da..18f5307 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_3.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_3.png
index bd235ae..6d08001c 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_4.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_4.png
index 5873b37..ef3023e 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_full_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_not_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_not_connected.png
index fdf34bf..b177999 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_not_connected.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_wifi_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_1.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_1.png
index 58317e3..d6af953d 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_1.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_2.png
index 57b8039..a702239 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_3.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_3.png
index 0cb099c..33e0310 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_3.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_signal_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_0.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_0.png
index 5599069..2afe504 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_0.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_1.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_1.png
index 0702c31..6df8484 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_1.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_2.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_2.png
index 6693090..d7915c6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_2.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_3.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_3.png
index ded4c67..c3773d2 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_3.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_4.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_4.png
index c2e0da9..263d697 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_4.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_full_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_not_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_not_connected.png
index 2344349..2afe504 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_not_connected.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_wifi_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_1.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_1.png
index edc44ab..d8556b2 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_1.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_2.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_2.png
index 5bd9b76..b913f6e 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_2.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_3.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_3.png
index 16196a03..7a1de13 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_3.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_signal_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_0.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_0.png
index f1d9f21..1951654 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_0.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_1.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_1.png
index dbae28c..f60d8a5 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_1.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_2.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_2.png
index 06e8d5b..ac88239 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_2.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_3.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_3.png
index bff132b..4a3c770 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_3.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_4.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_4.png
index 636bd702..b7e7d6f 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_4.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_full_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_not_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_not_connected.png
index 9c5a207..89c8fc1 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_not_connected.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_wifi_not_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9bf8de8..9d537c5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -61,7 +61,7 @@
     <string name="usb_debugging_always" msgid="303335496705863070">"Dóna sempre permís des d\'aquest equip"</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Zoom per omplir pantalla"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Estira per omplir pant."</string>
-    <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Desant captura de pantalla..."</string>
+    <string name="screenshot_saving_ticker" msgid="7403652894056693515">"S\'està desant captura de pantalla..."</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"S\'està desant la captura de pantalla..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"La captura de pantalla s\'ha desat."</string>
     <string name="screenshot_saved_title" msgid="6461865960961414961">"S\'ha fet una captura de pantalla."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index da958bf..8ec75fc 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -22,7 +22,7 @@
     <string name="app_label" msgid="7164937344850004466">"सिस्‍टम UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"साफ़ करें"</string>
     <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूची से निकालें"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"एप्‍स जानकारी"</string>
+    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ऐप्स जानकारी"</string>
     <string name="status_bar_no_recent_apps" msgid="6576392951053994640">"कोई हाल ही के एप्स नहीं"</string>
     <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"हाल ही के एप्स खारिज करें"</string>
   <plurals name="status_bar_accessibility_recent_apps">
@@ -75,7 +75,7 @@
     <string name="accessibility_back" msgid="567011538994429120">"वापस जाएं"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"होम"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"मेनू"</string>
-    <string name="accessibility_recent" msgid="8571350598987952883">"हाल ही के एप्‍स"</string>
+    <string name="accessibility_recent" msgid="8571350598987952883">"हाल ही के ऐप्स"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"खोजें"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"कैमरा"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"इनपुट पद्धति‍ बटन स्विच करें."</string>
@@ -146,7 +146,7 @@
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारिज की गई."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना शेड."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"त्वरित सेटिंग."</string>
-    <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"हाल ही के एप्‍स."</string>
+    <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"हाल ही के ऐप्स."</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"उपयोगकर्ता <xliff:g id="USER">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"मोबाइल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
@@ -166,7 +166,7 @@
     <string name="gps_notification_found_text" msgid="4619274244146446464">"GPS द्वारा सेट किया गया स्‍थान"</string>
     <string name="accessibility_location_active" msgid="2427290146138169014">"स्थान अनुरोध सक्रिय"</string>
     <string name="accessibility_clear_all" msgid="5235938559247164925">"सभी सूचनाएं साफ़ करें."</string>
-    <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"एप्‍स जानकारी"</string>
+    <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"ऐप्स जानकारी"</string>
     <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्‍क्रीन स्‍वचालित रूप से घूमेगी."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"स्‍क्रीन लैंडस्केप अभिविन्यास में लॉक है."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"स्‍क्रीन पोर्ट्रेट अभिविन्‍यास में लॉक है."</string>
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
index 99c59d5..90de65e 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
@@ -96,6 +96,13 @@
             1f,  0f,  0f,  0f, 0f
     };
 
+    private static final float[] ALPHA_MASK = {
+            0f,  0f,  0f,  0f, 255f,
+            0f,  0f,  0f,  0f, 255f,
+            0f,  0f,  0f,  0f, 255f,
+            0f,  0f,  0f,  1f, 0f
+    };
+
     private static final float[] WHITE_MASK = {
             0f,  0f,  0f,  0f, 255f,
             0f,  0f,  0f,  0f, 255f,
@@ -162,8 +169,8 @@
         for (int[] list : new int[][] { PASTRIES, RARE_PASTRIES, XRARE_PASTRIES, XXRARE_PASTRIES }) {
             for (int resid : list) {
                 final BitmapDrawable d = new BitmapDrawable(res,
-                        BitmapFactory.decodeResource(res, resid, opts));
-                d.setColorFilter(new ColorMatrixColorFilter(MASK));
+                        convertToAlphaMask(BitmapFactory.decodeResource(res, resid, opts)));
+                d.setColorFilter(new ColorMatrixColorFilter(ALPHA_MASK));
                 d.setBounds(0, 0, mCellSize, mCellSize);
                 mDrawables.append(resid, d);
             }
@@ -171,6 +178,15 @@
         if (DEBUG) setWillNotDraw(false);
     }
 
+    private static Bitmap convertToAlphaMask(Bitmap b) {
+        Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ALPHA_8);
+        Canvas c = new Canvas(a);
+        Paint pt = new Paint();
+        pt.setColorFilter(new ColorMatrixColorFilter(MASK));
+        c.drawBitmap(b, 0.0f, 0.0f, pt);
+        return a;
+    }
+
     public void start() {
         if (!mStarted) {
             mStarted = true;
@@ -273,9 +289,9 @@
 
             final float which = frand();
             final Drawable d;
-            if (which < 0.001f) {
+            if (which < 0.0005f) {
                 d = mDrawables.get(pick(XXRARE_PASTRIES));
-            } else if (which < 0.01f) {
+            } else if (which < 0.005f) {
                 d = mDrawables.get(pick(XRARE_PASTRIES));
             } else if (which < 0.5f) {
                 d = mDrawables.get(pick(RARE_PASTRIES));
@@ -288,8 +304,7 @@
                 v.getOverlay().add(d);
             }
 
-            final Paint paint = new Paint();
-            v.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
+            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
 
             lp.width = lp.height = mCellSize;
             addView(v, lp);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 6fa863d..4b0c2cb 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -326,6 +326,7 @@
                             ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
                             dw + ", " + dh);
                 }
+                mWallpaperManager.forgetLoadedWallpaper();
                 updateWallpaperLocked();
                 if (mBackground == null) {
                     if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index e6823ac..2026102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -310,6 +310,7 @@
         refreshBrightnessTile();
         refreshRotationLockTile();
         refreshRssiTile();
+        refreshLocationTile();
     }
 
     // Settings
@@ -582,6 +583,12 @@
         mLocationCallback.refreshView(mLocationTile, mLocationState);
     }
 
+    void refreshLocationTile() {
+        if (mLocationTile != null) {
+            onLocationSettingsChanged(mLocationState.enabled);
+        }
+    }
+
     @Override
     public void onLocationSettingsChanged(boolean locationEnabled) {
         int textResId = locationEnabled ? R.string.quick_settings_location_label
diff --git a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java
index dfdcdad5..3e57a77 100644
--- a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java
+++ b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java
@@ -201,13 +201,20 @@
         private ValueAnimator mColorAnim;
         private ViewGroup mClingLayout;
 
+        private Runnable mUpdateLayoutRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mClingLayout != null && mClingLayout.getParent() != null) {
+                    mClingLayout.setLayoutParams(getBubbleLayoutParams());
+                }
+            }
+        };
+
         private BroadcastReceiver mReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
-                    if (mClingLayout != null && mClingLayout.getParent() != null) {
-                        mClingLayout.setLayoutParams(getBubbleLayoutParams());
-                    }
+                    post(mUpdateLayoutRunnable);
                 }
             }
         };
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1c43014..816672f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4668,6 +4668,9 @@
 
     /** {@inheritDoc} */
     public void systemBooted() {
+        if (mKeyguardDelegate != null) {
+            mKeyguardDelegate.onBootCompleted();
+        }
         synchronized (mLock) {
             mSystemBooted = true;
         }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index 56a282b..bf22e2f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -52,6 +52,7 @@
         public int offReason;
         public int currentUser;
         public boolean screenIsOn;
+        public boolean bootCompleted;
     };
 
     public interface ShowListener {
@@ -117,6 +118,9 @@
                 // This is used to hide the scrim once keyguard displays.
                 mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(null));
             }
+            if (mKeyguardState.bootCompleted) {
+                mKeyguardService.onBootCompleted();
+            }
         }
 
         @Override
@@ -305,4 +309,11 @@
         });
     }
 
+    public void onBootCompleted() {
+        if (mKeyguardService != null) {
+            mKeyguardService.onBootCompleted();
+        }
+        mKeyguardState.bootCompleted = true;
+    }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index 83be1a8..9fb2a50 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -180,6 +180,14 @@
         }
     }
 
+    public void onBootCompleted() {
+        try {
+            mService.onBootCompleted();
+        } catch (RemoteException e) {
+            Slog.w(TAG , "Remote Exception", e);
+        }
+    }
+
     public void showAssistant() {
         // Not used by PhoneWindowManager
     }
diff --git a/preloaded-classes b/preloaded-classes
index d161bc1..467da25 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1202,29 +1202,12 @@
 android.view.inputmethod.InputMethodManager$PendingEvent
 android.view.textservice.SpellCheckerSubtype
 android.view.textservice.SpellCheckerSubtype$1
-android.webkit.BrowserFrame
-android.webkit.BrowserFrame$ConfigCallback
-android.webkit.CallbackProxy
 android.webkit.CookieManager
 android.webkit.CookieSyncManager
-android.webkit.DeviceMotionAndOrientationManager
 android.webkit.GeolocationPermissions
-android.webkit.HTML5Audio
-android.webkit.HTML5VideoViewProxy
-android.webkit.JWebCoreJavaBridge
 android.webkit.JavascriptInterface
-android.webkit.L10nUtils
-android.webkit.MockGeolocation
-android.webkit.OverScrollGlow
-android.webkit.PluginManager
-android.webkit.QuadF
 android.webkit.URLUtil
-android.webkit.ViewManager
-android.webkit.ViewManager$2
-android.webkit.ViewManager$3
-android.webkit.ViewStateSerializer
 android.webkit.WebBackForwardList
-android.webkit.WebCoreThreadWatchdog
 android.webkit.WebHistoryItem
 android.webkit.WebIconDatabase
 android.webkit.WebSettings
@@ -1238,35 +1221,14 @@
 android.webkit.WebView
 android.webkit.WebView$PrivateAccess
 android.webkit.WebViewClient
-android.webkit.WebViewCore$AutoFillData
-android.webkit.WebViewCore$DrawData
-android.webkit.WebViewCore$EventHub
-android.webkit.WebViewCore$EventHub$1
-android.webkit.WebViewCore$GetUrlData
-android.webkit.WebViewCore$TextFieldInitData
-android.webkit.WebViewCore$ViewState
-android.webkit.WebViewCore$WebCoreThread
-android.webkit.WebViewCore$WebCoreThread$1
 android.webkit.WebViewDatabase
 android.webkit.WebViewFactory
 android.webkit.WebViewFactory$Preloader
 android.webkit.WebViewFactoryProvider
 android.webkit.WebViewFactoryProvider$Statics
-android.webkit.WebViewInputDispatcher
-android.webkit.WebViewInputDispatcher$DispatchEventQueue
-android.webkit.WebViewInputDispatcher$TouchStream
-android.webkit.WebViewInputDispatcher$UiCallbacks
-android.webkit.WebViewInputDispatcher$UiHandler
-android.webkit.WebViewInputDispatcher$WebKitCallbacks
-android.webkit.WebViewInputDispatcher$WebKitHandler
 android.webkit.WebViewProvider
 android.webkit.WebViewProvider$ScrollDelegate
 android.webkit.WebViewProvider$ViewDelegate
-android.webkit.ZoomControlBase
-android.webkit.ZoomControlExternal
-android.webkit.ZoomManager
-android.webkit.ZoomManager$FocusMovementQueue
-android.webkit.ZoomManager$PostScale
 android.widget.AbsListView
 android.widget.AbsListView$1
 android.widget.AbsListView$AdapterDataSetObserver
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 70418e8..7c61c44 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -4247,27 +4247,35 @@
                         log("isMobileOk: linkHasIpv4=" + linkHasIpv4
                                 + " linkHasIpv6=" + linkHasIpv6);
 
-                        // Loop through at most 3 valid addresses or all of the address or until
-                        // we run out of time
-                        int loops = Math.min(3, addresses.length);
-                        for(int validAddr=0, addrTried=0;
-                                    (validAddr < loops) && (addrTried < addresses.length)
-                                      && (SystemClock.elapsedRealtime() < endTime);
-                                addrTried ++) {
+                        final ArrayList<InetAddress> validAddresses =
+                                new ArrayList<InetAddress>(addresses.length);
 
-                            // Choose the address at random but make sure its type is supported
-                            // TODO: This doesn't work 100% of the time, because we may end up
-                            // trying the same invalid address more than once and ignoring one
-                            // of the valid addresses.
-                            InetAddress hostAddr = addresses[rand.nextInt(addresses.length)];
-                            if (((hostAddr instanceof Inet4Address) && linkHasIpv4)
-                                    || ((hostAddr instanceof Inet6Address) && linkHasIpv6)) {
-                                // Valid address, so use it
-                                validAddr += 1;
-                            } else {
-                                // Invalid address so try next address
-                                continue;
+                        for (InetAddress addr : addresses) {
+                            if (((addr instanceof Inet4Address) && linkHasIpv4) ||
+                                    ((addr instanceof Inet6Address) && linkHasIpv6)) {
+                                validAddresses.add(addr);
                             }
+                        }
+
+                        if (validAddresses.size() == 0) {
+                            return CMP_RESULT_CODE_NO_CONNECTION;
+                        }
+
+                        int addrTried = 0;
+                        while (true) {
+                            // Loop through at most 3 valid addresses or until
+                            // we run out of time
+                            if (addrTried++ >= 3) {
+                                log("too many loops tried - giving up");
+                                break;
+                            }
+                            if (SystemClock.elapsedRealtime() >= endTime) {
+                                log("spend too much time - giving up");
+                                break;
+                            }
+
+                            InetAddress hostAddr = validAddresses.get(rand.nextInt(
+                                    validAddresses.size()));
 
                             // Make a route to host so we check the specific interface.
                             if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
@@ -4283,8 +4291,10 @@
                             }
 
                             // Rewrite the url to have numeric address to use the specific route.
+                            // Add a pointless random query param to fool proxies into not caching.
                             URL newUrl = new URL(orgUri.getScheme(),
-                                    hostAddr.getHostAddress(), orgUri.getPath());
+                                    hostAddr.getHostAddress(),
+                                    orgUri.getPath() + "?q=" + rand.nextInt(Integer.MAX_VALUE));
                             log("isMobileOk: newUrl=" + newUrl);
 
                             HttpURLConnection urlConn = null;
@@ -4321,6 +4331,9 @@
                                     // occasions where a server returned 200 even though
                                     // the device didn't have a "warm" sim.
                                     log("isMobileOk: not expected responseCode=" + responseCode);
+                                    // TODO - it would be nice in the single-address case to do
+                                    // another DNS resolve here, but flushing the cache is a bit
+                                    // heavy-handed.
                                     result = CMP_RESULT_CODE_REDIRECTED;
                                 }
                             } catch (Exception e) {
@@ -4454,8 +4467,9 @@
             mdst.enableMobileProvisioning(url);
         } else {
             if (DBG) log("handleMobileProvisioningAction: on default network");
-            Intent newIntent =
-                    new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+            Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+                    Intent.CATEGORY_APP_BROWSER);
+            newIntent.setData(Uri.parse(url));
             newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
                     Intent.FLAG_ACTIVITY_NEW_TASK);
             try {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c7ca1ea..e60231a 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -62,6 +62,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.NativeDaemonConnector.Command;
@@ -173,6 +174,8 @@
          * 600 series - Unsolicited broadcasts.
          */
         public static final int VolumeStateChange              = 605;
+        public static final int VolumeUuidChange               = 613;
+        public static final int VolumeUserLabelChange          = 614;
         public static final int VolumeDiskInserted             = 630;
         public static final int VolumeDiskRemoved              = 631;
         public static final int VolumeBadRemoval               = 632;
@@ -661,6 +664,7 @@
         final String oldState;
         synchronized (mVolumesLock) {
             oldState = mVolumeStates.put(path, state);
+            volume.setState(state);
         }
 
         if (state.equals(oldState)) {
@@ -801,6 +805,26 @@
             notifyVolumeStateChange(
                     cooked[2], cooked[3], Integer.parseInt(cooked[7]),
                             Integer.parseInt(cooked[10]));
+        } else if (code == VoldResponseCode.VolumeUuidChange) {
+            // Format: nnn <label> <path> <uuid>
+            final String path = cooked[2];
+            final String uuid = (cooked.length > 3) ? cooked[3] : null;
+
+            final StorageVolume vol = mVolumesByPath.get(path);
+            if (vol != null) {
+                vol.setUuid(uuid);
+            }
+
+        } else if (code == VoldResponseCode.VolumeUserLabelChange) {
+            // Format: nnn <label> <path> <label>
+            final String path = cooked[2];
+            final String userLabel = (cooked.length > 3) ? cooked[3] : null;
+
+            final StorageVolume vol = mVolumesByPath.get(path);
+            if (vol != null) {
+                vol.setUserLabel(userLabel);
+            }
+
         } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
                    (code == VoldResponseCode.VolumeDiskRemoved) ||
                    (code == VoldResponseCode.VolumeBadRemoval)) {
@@ -1230,6 +1254,7 @@
 
                             // Until we hear otherwise, treat as unmounted
                             mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
+                            volume.setState(Environment.MEDIA_UNMOUNTED);
                         }
                     }
 
@@ -1273,6 +1298,7 @@
         } else {
             // Place stub status for early callers to find
             mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
+            volume.setState(Environment.MEDIA_MOUNTED);
         }
     }
 
@@ -2743,54 +2769,59 @@
     }
 
     @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump ActivityManager from from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " without permission " + android.Manifest.permission.DUMP);
-            return;
-        }
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 160);
 
         synchronized (mObbMounts) {
-            pw.println("  mObbMounts:");
-
-            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
+            pw.println("mObbMounts:");
+            pw.increaseIndent();
+            final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet()
+                    .iterator();
             while (binders.hasNext()) {
                 Entry<IBinder, List<ObbState>> e = binders.next();
-                pw.print("    Key="); pw.println(e.getKey().toString());
+                pw.println(e.getKey() + ":");
+                pw.increaseIndent();
                 final List<ObbState> obbStates = e.getValue();
                 for (final ObbState obbState : obbStates) {
-                    pw.print("      "); pw.println(obbState.toString());
+                    pw.println(obbState);
                 }
+                pw.decreaseIndent();
             }
+            pw.decreaseIndent();
 
-            pw.println("");
-            pw.println("  mObbPathToStateMap:");
+            pw.println();
+            pw.println("mObbPathToStateMap:");
+            pw.increaseIndent();
             final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
             while (maps.hasNext()) {
                 final Entry<String, ObbState> e = maps.next();
-                pw.print("    "); pw.print(e.getKey());
-                pw.print(" -> "); pw.println(e.getValue().toString());
+                pw.print(e.getKey());
+                pw.print(" -> ");
+                pw.println(e.getValue());
             }
+            pw.decreaseIndent();
         }
 
-        pw.println("");
-
         synchronized (mVolumesLock) {
-            pw.println("  mVolumes:");
-
-            final int N = mVolumes.size();
-            for (int i = 0; i < N; i++) {
-                final StorageVolume v = mVolumes.get(i);
-                pw.print("    ");
-                pw.println(v.toString());
-                pw.println("      state=" + mVolumeStates.get(v.getPath()));
+            pw.println();
+            pw.println("mVolumes:");
+            pw.increaseIndent();
+            for (StorageVolume volume : mVolumes) {
+                pw.println(volume);
+                pw.increaseIndent();
+                pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
+                pw.decreaseIndent();
             }
+            pw.decreaseIndent();
         }
 
         pw.println();
-        pw.println("  mConnection:");
+        pw.println("mConnection:");
+        pw.increaseIndent();
         mConnector.dump(fd, pw, args);
+        pw.decreaseIndent();
     }
 
     /** {@inheritDoc} */
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index cc43a9c..f972f70 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -2167,15 +2167,20 @@
                  * this can be very bad when those apps are in the system like
                  * the System Settings.
                  */
-                PackageManager pm = mContext.getPackageManager();
-                ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
-                int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
                 int authenticatorUid = Binder.getCallingUid();
-                if (PackageManager.SIGNATURE_MATCH !=
-                        pm.checkSignatures(authenticatorUid, targetUid)) {
-                    throw new SecurityException(
-                            "Activity to be started with KEY_INTENT must " +
-                            "share Authenticator's signatures");
+                long bid = Binder.clearCallingIdentity();
+                try {
+                    PackageManager pm = mContext.getPackageManager();
+                    ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+                    int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
+                    if (PackageManager.SIGNATURE_MATCH !=
+                            pm.checkSignatures(authenticatorUid, targetUid)) {
+                        throw new SecurityException(
+                                "Activity to be started with KEY_INTENT must " +
+                               "share Authenticator's signatures");
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(bid);
                 }
             }
             if (result != null
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index b69a0c8..a64940c 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -1494,7 +1494,7 @@
                     } catch (Exception e) {
                         Slog.w(TAG, "Exception when unbinding service "
                                 + r.shortName, e);
-                        serviceDoneExecutingLocked(r, true, true);
+                        serviceProcessGoneLocked(r);
                     }
                 }
             }
@@ -1544,7 +1544,7 @@
                 } catch (Exception e) {
                     Slog.w(TAG, "Exception when destroying service "
                             + r.shortName, e);
-                    serviceDoneExecutingLocked(r, true, true);
+                    serviceProcessGoneLocked(r);
                 }
                 updateServiceForegroundLocked(r.app, false);
             } else {
@@ -1570,7 +1570,7 @@
             r.tracker.setStarted(false, memFactor, now);
             r.tracker.setBound(false, memFactor, now);
             if (r.executeNesting == 0) {
-                r.tracker.clearCurrentOwner(r);
+                r.tracker.clearCurrentOwner(r, false);
                 r.tracker = null;
             }
         }
@@ -1629,7 +1629,7 @@
                     s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
                 } catch (Exception e) {
                     Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
-                    serviceDoneExecutingLocked(s, true, true);
+                    serviceProcessGoneLocked(s);
                 }
             }
 
@@ -1708,6 +1708,16 @@
         }
     }
 
+    private void serviceProcessGoneLocked(ServiceRecord r) {
+        if (r.tracker != null) {
+            int memFactor = mAm.mProcessStats.getMemFactorLocked();
+            long now = SystemClock.uptimeMillis();
+            r.tracker.setExecuting(false, memFactor, now);
+            r.tracker.setBound(false, memFactor, now);
+        }
+        serviceDoneExecutingLocked(r, true, true);
+    }
+
     private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
             boolean finishing) {
         if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
@@ -1747,7 +1757,7 @@
                 r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
                         SystemClock.uptimeMillis());
                 if (finishing) {
-                    r.tracker.clearCurrentOwner(r);
+                    r.tracker.clearCurrentOwner(r, false);
                     r.tracker = null;
                 }
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f1c2025..3ba9098 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -226,7 +226,7 @@
     static final boolean DEBUG_RESULTS = localLOGV || false;
     static final boolean DEBUG_SERVICE = localLOGV || false;
     static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
-    static final boolean DEBUG_STACK = localLOGV || true;
+    static final boolean DEBUG_STACK = localLOGV || false;
     static final boolean DEBUG_SWITCH = localLOGV || false;
     static final boolean DEBUG_TASKS = localLOGV || false;
     static final boolean DEBUG_THUMBNAILS = localLOGV || false;
@@ -236,7 +236,7 @@
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PSS = localLOGV || false;
     static final boolean DEBUG_LOCKSCREEN = localLOGV || false;
-    static final boolean VALIDATE_TOKENS = true;
+    static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
 
     // Control over CPU and battery monitoring.
@@ -13143,8 +13143,8 @@
                             boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(
                                     intent.getAction());
                             if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
-                                forceStopPackageLocked(ssp,
-                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true,
+                                forceStopPackageLocked(ssp, UserHandle.getAppId(
+                                        intent.getIntExtra(Intent.EXTRA_UID, -1)), false, true, true,
                                         false, userId, removed ? "pkg removed" : "pkg changed");
                             }
                             if (removed) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 596e18a..0397fd5 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -478,37 +478,45 @@
         }
         final int userId = UserHandle.getUserId(info.applicationInfo.uid);
 
+        if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.userId != userId) {
                 // Looking for a different task.
+                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
                 continue;
             }
             final ActivityRecord r = task.getTopActivity();
             if (r == null || r.finishing || r.userId != userId ||
                     r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": mismatch root " + r);
                 continue;
             }
 
-            //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
-            //        + "/aff=" + r.task.affinity + " to new cls="
-            //        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
+            if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
+                    + r.task.intent.getComponent().flattenToShortString()
+                    + "/aff=" + r.task.affinity + " to new cls="
+                    + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
             if (task.affinity != null) {
                 if (task.affinity.equals(info.taskAffinity)) {
-                    //Slog.i(TAG, "Found matching affinity!");
+                    if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
                     return r;
                 }
             } else if (task.intent != null && task.intent.getComponent().equals(cls)) {
-                //Slog.i(TAG, "Found matching class!");
+                if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
                 //dump();
-                //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+                if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+                        + r.intent);
                 return r;
             } else if (task.affinityIntent != null
                     && task.affinityIntent.getComponent().equals(cls)) {
-                //Slog.i(TAG, "Found matching class!");
+                if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
                 //dump();
-                //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+                if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+                        + r.intent);
                 return r;
+            } else if (DEBUG_TASKS) {
+                Slog.d(TAG, "Not a match: " + task);
             }
         }
 
@@ -551,9 +559,6 @@
      * Move the activities around in the stack to bring a user to the foreground.
      */
     final void switchUserLocked(int userId) {
-        if (VALIDATE_TOKENS) {
-            validateAppTokensLocked();
-        }
         if (mCurrentUser == userId) {
             return;
         }
@@ -564,11 +569,16 @@
         for (int i = 0; i < index; ++i) {
             TaskRecord task = mTaskHistory.get(i);
             if (task.userId == userId) {
+                if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
+                        " moving " + task + " to top");
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
                 --index;
             }
         }
+        if (VALIDATE_TOKENS) {
+            validateAppTokensLocked();
+        }
     }
 
     void minimalResumeActivityLocked(ActivityRecord r) {
@@ -581,6 +591,7 @@
         mService.addRecentTaskLocked(r.task);
         completeResumeLocked(r);
         mStackSupervisor.checkReadyForSleepLocked();
+        setLaunchTime(r);
         if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
     }
 
@@ -985,7 +996,7 @@
      */
     final boolean ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting,
             String onlyThisProcess, int configChanges, boolean forceHomeShown) {
-        if (true || DEBUG_VISBILITY) Slog.v(
+        if (DEBUG_VISBILITY) Slog.v(
                 TAG, "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
 
@@ -1041,7 +1052,7 @@
                                 r.startFreezingScreenLocked(r.app, configChanges);
                             }
                             if (!r.visible) {
-                                if (true || DEBUG_VISBILITY) Slog.v(
+                                if (DEBUG_VISBILITY) Slog.v(
                                         TAG, "Starting and making visible: " + r);
                                 mWindowManager.setAppVisibility(r.appToken, true);
                             }
@@ -1063,7 +1074,7 @@
                         if (r.state != ActivityState.RESUMED && r != starting) {
                             // If this activity is paused, tell it
                             // to now show its window.
-                            if (true || DEBUG_VISBILITY) Slog.v(
+                            if (DEBUG_VISBILITY) Slog.v(
                                     TAG, "Making visible and scheduling visibility: " + r);
                             try {
                                 if (mTranslucentActivityWaiting != null) {
@@ -1117,7 +1128,7 @@
                     // Now for any activities that aren't visible to the user, make
                     // sure they no longer are keeping the screen frozen.
                     if (r.visible) {
-                        if (true || DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
+                        if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
                         r.visible = false;
                         try {
                             mWindowManager.setAppVisibility(r.appToken, false);
@@ -2950,6 +2961,7 @@
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.isHomeTask()) {
+                if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
                 mWindowManager.moveTaskToTop(task.taskId);
@@ -3571,6 +3583,7 @@
 
     @Override
     public String toString() {
-        return "stackId=" + mStackId + " tasks=" + mTaskHistory;
+        return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+                + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
     }
 }
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 040f0ac..523015d 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -68,9 +68,8 @@
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
-import android.util.SparseArray;
+import android.util.SparseIntArray;
 
-import android.util.SparseBooleanArray;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.os.TransferPipe;
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -89,7 +88,7 @@
     static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
     static final boolean DEBUG_APP = DEBUG || false;
     static final boolean DEBUG_SAVED_STATE = DEBUG || false;
-    static final boolean DEBUG_STATES = DEBUG || true;
+    static final boolean DEBUG_STATES = DEBUG || false;
     static final boolean DEBUG_IDLE = DEBUG || false;
 
     public static final int HOME_STACK_ID = 0;
@@ -204,8 +203,8 @@
      */
     final PowerManager.WakeLock mGoingToSleep;
 
-    /** State of the stacks when user switched, indexed by userId. */
-    SparseBooleanArray mUserHomeInFront = new SparseBooleanArray(2);
+    /** Stack id of the front stack when user switched, indexed by userId. */
+    SparseIntArray mUserStackInFront = new SparseIntArray(2);
 
     public ActivityStackSupervisor(ActivityManagerService service, Context context,
             Looper looper) {
@@ -876,7 +875,7 @@
             throws RemoteException {
 
         r.startFreezingScreenLocked(app, 0);
-        if (true) Slog.d(TAG, "realStartActivity: setting app visibility true");
+        if (false) Slog.d(TAG, "realStartActivity: setting app visibility true");
         mWindowManager.setAppVisibility(r.appToken, true);
 
         // schedule launch ticks to collect information about slow apps.
@@ -1378,12 +1377,23 @@
         }
 
         final ActivityStack sourceStack;
-        TaskRecord sourceTask;
         if (sourceRecord != null) {
-            sourceTask = sourceRecord.task;
-            sourceStack = sourceTask.stack;
+            if (sourceRecord.finishing) {
+                // If the source is finishing, we can't further count it as our source.  This
+                // is because the task it is associated with may now be empty and on its way out,
+                // so we don't want to blindly throw it in to that task.  Instead we will take
+                // the NEW_TASK flow and try to find a task for it.
+                if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+                }
+                sourceRecord = null;
+                sourceStack = null;
+            } else {
+                sourceStack = sourceRecord.task.stack;
+            }
         } else {
-            sourceTask = null;
             sourceStack = null;
         }
 
@@ -1425,6 +1435,8 @@
                     }
                     targetStack = intentActivity.task.stack;
                     targetStack.mLastPausedActivity = null;
+                    if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
+                            + " from " + intentActivity);
                     moveHomeStack(targetStack.isHomeStack());
                     if (intentActivity.task.intent == null) {
                         // This task was started because of movement of
@@ -1664,7 +1676,7 @@
                 }
             }
         } else if (sourceRecord != null) {
-            sourceTask = sourceRecord.task;
+            TaskRecord sourceTask = sourceRecord.task;
             targetStack = sourceTask.stack;
             moveHomeStack(targetStack.isHomeStack());
             if (!addingToTask &&
@@ -1684,7 +1696,7 @@
                         targetStack.resumeTopActivityLocked(null);
                     }
                     ActivityOptions.abort(options);
-                    if (r.task == null)  Slog.v(TAG,
+                    if (r.task == null)  Slog.w(TAG,
                         "startActivityUncheckedLocked: task left null",
                         new RuntimeException("here").fillInStackTrace());
                     return ActivityManager.START_DELIVERED_TO_TOP;
@@ -1713,7 +1725,7 @@
             // it.
             r.setTask(sourceTask, sourceRecord.thumbHolder, false);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
-                    + " in existing task " + r.task);
+                    + " in existing task " + r.task + " from source " + sourceRecord);
 
         } else {
             // This not being started from an existing activity, and not part
@@ -1927,7 +1939,7 @@
     }
 
     void removeUserLocked(int userId) {
-        mUserHomeInFront.delete(userId);
+        mUserStackInFront.delete(userId);
     }
 
     /**
@@ -2061,9 +2073,11 @@
     }
 
     ActivityRecord findTaskLocked(ActivityRecord r) {
+        if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + r);
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             if (!r.isApplicationActivity() && !stack.isHomeStack()) {
+                if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
                 continue;
             }
             final ActivityRecord ar = stack.findTaskLocked(r);
@@ -2071,6 +2085,7 @@
                 return ar;
             }
         }
+        if (DEBUG_TASKS) Slog.d(TAG, "No task found");
         return null;
     }
 
@@ -2248,8 +2263,8 @@
     }
 
     boolean switchUserLocked(int userId, UserStartedState uss) {
-        mUserHomeInFront.put(mCurrentUser, isFrontStack(mHomeStack));
-        final boolean homeInFront = mUserHomeInFront.get(userId, true);
+        mUserStackInFront.put(mCurrentUser, getFocusedStack().getStackId());
+        final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID);
         mCurrentUser = userId;
 
         mStartingUsers.add(uss);
@@ -2257,7 +2272,13 @@
             mStacks.get(stackNdx).switchUserLocked(userId);
         }
 
+        ActivityStack stack = getStack(restoreStackId);
+        if (stack == null) {
+            stack = mHomeStack;
+        }
+        final boolean homeInFront = stack.isHomeStack();
         moveHomeStack(homeInFront);
+        mWindowManager.moveTaskToTop(stack.topTask().taskId);
         return homeInFront;
     }
 
@@ -2270,7 +2291,7 @@
         final boolean nowVisible = allResumedActivitiesVisible();
         for (int i=0; i<N; i++) {
             ActivityRecord s = mStoppingActivities.get(i);
-            if (true || localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+            if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
                     + nowVisible + " waitingVisible=" + s.waitingVisible
                     + " finishing=" + s.finishing);
             if (s.waitingVisible && nowVisible) {
@@ -2351,7 +2372,7 @@
         pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
         pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
         pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
-        pw.print(prefix); pw.println("mUserHomeInFront: " + mUserHomeInFront);
+        pw.print(prefix); pw.println("mUserStackInFront: " + mUserStackInFront);
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 1d6970f..5e80135 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -54,9 +54,9 @@
     static final boolean DEBUG_BROADCAST_LIGHT = ActivityManagerService.DEBUG_BROADCAST_LIGHT;
     static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
 
-    static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 25;
+    static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
     static final int MAX_BROADCAST_SUMMARY_HISTORY
-            = ActivityManager.isLowRamDeviceStatic() ? 25 : 100;
+            = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
 
     final ActivityManagerService mService;
 
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index c47c1ac..cc1172a 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -335,12 +335,7 @@
 
     public void forceClearTracker() {
         if (tracker != null) {
-            int memFactor = ams.mProcessStats.getMemFactorLocked();
-            long now = SystemClock.uptimeMillis();
-            tracker.setStarted(false, memFactor, now);
-            tracker.setBound(false, memFactor, now);
-            tracker.setExecuting(false, memFactor, now);
-            tracker.clearCurrentOwner(this);
+            tracker.clearCurrentOwner(this, true);
             tracker = null;
         }
     }
diff --git a/services/java/com/android/server/connectivity/PacManager.java b/services/java/com/android/server/connectivity/PacManager.java
index 837fb05..7786fe6 100644
--- a/services/java/com/android/server/connectivity/PacManager.java
+++ b/services/java/com/android/server/connectivity/PacManager.java
@@ -162,7 +162,7 @@
      */
     public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
         if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
-            if (proxy.getPacFileUrl().equals(mPacUrl)) {
+            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
                 return false;
             }
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 377c390..92fc6a4 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -2473,7 +2473,8 @@
                 } else if (tagName.equals("signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
                     packageSetting.keySetData.addSigningKeySet(id);
-                    Slog.d(TAG, "Adding signing keyset " + Long.toString(id) + " to " + name);
+                    if (false) Slog.d(TAG, "Adding signing keyset " + Long.toString(id)
+                            + " to " + name);
                 } else if (tagName.equals("defined-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
                     String alias = parser.getAttributeValue(null, "alias");
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index edd6b25..8a3997a 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -361,12 +361,17 @@
         }
 
         synchronized (mLock) {
-            pw.println("PRINT MANAGER STATE (dumpsys print)");
-            final int userStateCount = mUserStates.size();
-            for (int i = 0; i < userStateCount; i++) {
-                UserState userState = mUserStates.get(i);
-                userState.dump(fd, pw, "");
-                pw.println();
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                pw.println("PRINT MANAGER STATE (dumpsys print)");
+                final int userStateCount = mUserStates.size();
+                for (int i = 0; i < userStateCount; i++) {
+                    UserState userState = mUserStates.get(i);
+                    userState.dump(fd, pw, "");
+                    pw.println();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 00a653b..cfb10a0 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -9735,7 +9735,7 @@
                 newFocus = computeFocusedWindowLocked();
             }
 
-            if (true || DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG, "Changing focus from " +
+            if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG, "Changing focus from " +
                     mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
             final WindowState oldFocus = mCurrentFocus;
             mCurrentFocus = newFocus;
diff --git a/tests/BrowserTestPlugin/Android.mk b/tests/BrowserTestPlugin/Android.mk
deleted file mode 100644
index 968d9e6..0000000
--- a/tests/BrowserTestPlugin/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2009 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.
-#
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build application
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BrowserTestPlugin
-
-LOCAL_JNI_SHARED_LIBRARIES := libtestplugin
-
-include $(BUILD_PACKAGE)
-
-# ============================================================
-
-# Also build all of the sub-targets under this one: the shared library.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/BrowserTestPlugin/AndroidManifest.xml b/tests/BrowserTestPlugin/AndroidManifest.xml
deleted file mode 100644
index f071ab6..0000000
--- a/tests/BrowserTestPlugin/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="com.android.testplugin"
-      android:versionCode="1"
-      android:versionName="1.0">
-
-    <uses-permission android:name="android.webkit.permission.PLUGIN"/>
-
-    <uses-sdk android:minSdkVersion="3" />
-
-    <application android:icon="@drawable/browser_test_plugin"
-                android:label="Browser Test Plugin">
-        <service android:name="TestPlugin">
-            <intent-filter>
-                <action android:name="android.webkit.PLUGIN" />
-            </intent-filter>
-        </service>
-    </application>
-
-</manifest>
diff --git a/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2 b/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/tests/BrowserTestPlugin/NOTICE b/tests/BrowserTestPlugin/NOTICE
deleted file mode 100644
index 9df2554..0000000
--- a/tests/BrowserTestPlugin/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2009, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/tests/BrowserTestPlugin/jni/Android.mk b/tests/BrowserTestPlugin/jni/Android.mk
deleted file mode 100644
index d641dad..0000000
--- a/tests/BrowserTestPlugin/jni/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-##
-##
-## Copyright 2009, The Android Open Source Project
-##
-## Redistribution and use in source and binary forms, with or without
-## modification, are permitted provided that the following conditions
-## are met:
-##  * Redistributions of source code must retain the above copyright
-##    notice, this list of conditions and the following disclaimer.
-##  * Redistributions in binary form must reproduce the above copyright
-##    notice, this list of conditions and the following disclaimer in the
-##    documentation and/or other materials provided with the distribution.
-##
-## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
-## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
-## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-##
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	main.cpp \
-	PluginObject.cpp \
-	event/EventPlugin.cpp \
-
-WEBCORE_PATH := external/webkit/Source/WebCore
-
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH) \
-	$(LOCAL_PATH)/event \
-	$(WEBCORE_PATH)/bridge \
-	$(WEBCORE_PATH)/plugins \
-	$(WEBCORE_PATH)/platform/android/JavaVM \
-	external/webkit/Source/WebKit/android/plugins
-
-LOCAL_CFLAGS += -fvisibility=hidden 
-
-
-LOCAL_MODULE := libtestplugin
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.cpp b/tests/BrowserTestPlugin/jni/PluginObject.cpp
deleted file mode 100644
index 68fca60..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms.  If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software.  Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-          OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include "main.h"
-#include "PluginObject.h"
-
-static void pluginInvalidate(NPObject *obj);
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
-static void pluginDeallocate(NPObject *obj);
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
-
-
-
-static NPClass pluginClass = {
-    NP_CLASS_STRUCT_VERSION,
-    pluginAllocate,
-    pluginDeallocate,
-    pluginInvalidate,
-    pluginHasMethod,
-    pluginInvoke,
-    pluginInvokeDefault,
-    pluginHasProperty,
-    pluginGetProperty,
-    pluginSetProperty,
-    pluginRemoveProperty,
-    pluginEnumerate
-};
-
-NPClass *getPluginClass(void)
-{
-    return &pluginClass;
-}
-
-static bool identifiersInitialized = false;
-
-#define ID_TESTFILE_PROPERTY            0
-#define NUM_PROPERTY_IDENTIFIERS        1
-
-static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
-static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
-    "testfile"
-};
-
-#define ID_GETTESTFILE_METHOD                   0
-#define NUM_METHOD_IDENTIFIERS                  1
-
-static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
-static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
-    "getTestFile"
-};
-
-static void initializeIdentifiers(void)
-{
-    browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
-    browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
-}
-
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
-{
-    int i;
-    for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
-        if (name == pluginPropertyIdentifiers[i])
-            return true;
-    return false;
-}
-
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
-{
-    int i;
-    for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
-        if (name == pluginMethodIdentifiers[i])
-            return true;
-    return false;
-}
-
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
-{
-    PluginObject *plugin = (PluginObject *)obj;
-    if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
-        BOOLEAN_TO_NPVARIANT(true, *variant);
-        return true;
-    }
-    return false;
-}
-
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
-{
-    return false;
-}
-
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
-    PluginObject *plugin = (PluginObject *)obj;
-    if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
-        return true;
-    }
-    return false;
-}
-
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
-    return false;
-}
-
-static void pluginInvalidate(NPObject *obj)
-{
-    // Release any remaining references to JavaScript objects.
-}
-
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
-{
-    PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
-    newInstance->header._class = theClass;
-    newInstance->header.referenceCount = 1;
-
-    if (!identifiersInitialized) {
-        identifiersInitialized = true;
-        initializeIdentifiers();
-    }
-
-    newInstance->npp = npp;
-
-    return &newInstance->header;
-}
-
-static void pluginDeallocate(NPObject *obj)
-{
-    free(obj);
-}
-
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
-{
-    return false;
-}
-
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
-{
-    return false;
-}
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
deleted file mode 100644
index 037367e..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms.  If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software.  Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-          OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef PluginObject__DEFINED
-#define PluginObject__DEFINED
-
-#include "main.h"
-
-class SubPlugin {
-public:
-    SubPlugin(NPP inst) : m_inst(inst) {}
-    virtual ~SubPlugin() {}
-    virtual int16_t handleEvent(const ANPEvent* evt) = 0;
-
-    NPP inst() const { return m_inst; }
-
-private:
-    NPP m_inst;
-};
-
-typedef struct PluginObject {
-    NPObject header;
-    NPP npp;
-    NPWindow* window;
-
-    SubPlugin* subPlugin;
-
-} PluginObject;
-
-NPClass *getPluginClass(void);
-
-#endif // PluginObject__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
deleted file mode 100644
index 91f1b3d..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "EventPlugin.h"
-#include "android_npapi.h"
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <math.h>
-#include <string.h>
-
-extern NPNetscapeFuncs*        browser;
-extern ANPCanvasInterfaceV0    gCanvasI;
-extern ANPLogInterfaceV0       gLogI;
-extern ANPPaintInterfaceV0     gPaintI;
-extern ANPTypefaceInterfaceV0  gTypefaceI;
-
-///////////////////////////////////////////////////////////////////////////////
-
-EventPlugin::EventPlugin(NPP inst) : SubPlugin(inst) { }
-
-EventPlugin::~EventPlugin() { }
-
-void EventPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
-
-    gLogI.log(kDebug_ANPLogType, " ------ %p drawing the plugin (%d,%d)",
-              inst(), bitmap.width, bitmap.height);
-
-    // get the plugin's dimensions according to the DOM
-    PluginObject *obj = (PluginObject*) inst()->pdata;
-    const int W = obj->window->width;
-    const int H = obj->window->height;
-
-    // compute the current zoom level
-    const float zoomFactorW = static_cast<float>(bitmap.width) / W;
-    const float zoomFactorH = static_cast<float>(bitmap.height) / H;
-
-    // check to make sure the zoom level is uniform
-    if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
-        gLogI.log(kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
-                  inst(), zoomFactorW, zoomFactorH);
-
-    // scale the variables based on the zoom level
-    const int fontSize = (int)(zoomFactorW * 16);
-    const int leftMargin = (int)(zoomFactorW * 10);
-
-    // create and clip a canvas
-    ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
-
-    ANPRectF clipR;
-    clipR.left = clip.left;
-    clipR.top = clip.top;
-    clipR.right = clip.right;
-    clipR.bottom = clip.bottom;
-    gCanvasI.clipRect(canvas, &clipR);
-
-    gCanvasI.drawColor(canvas, 0xFFFFFFFF);
-
-    // configure the paint
-    ANPPaint* paint = gPaintI.newPaint();
-    gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
-    gPaintI.setColor(paint, 0xFF0000FF);
-    gPaintI.setTextSize(paint, fontSize);
-
-    // configure the font
-    ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
-    gPaintI.setTypeface(paint, tf);
-    gTypefaceI.unref(tf);
-
-    // retrieve the font metrics
-    ANPFontMetrics fm;
-    gPaintI.getFontMetrics(paint, &fm);
-
-    // write text on the canvas
-    const char c[] = "Browser Test Plugin";
-    gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
-
-    // clean up variables
-    gPaintI.deletePaint(paint);
-    gCanvasI.deleteCanvas(canvas);
-}
-
-void EventPlugin::printToDiv(const char* text, int length) {
-    // Get the plugin's DOM object
-    NPObject* windowObject = NULL;
-    browser->getvalue(inst(), NPNVWindowNPObject, &windowObject);
-
-    if (!windowObject)
-        gLogI.log(kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", inst());
-
-    // create a string (JS code) that is stored in memory allocated by the browser
-    const char* jsBegin = "var outputDiv = document.getElementById('eventOutput'); outputDiv.innerHTML += ' ";
-    const char* jsEnd = "';";
-
-    // allocate memory and configure pointers
-    int totalLength = strlen(jsBegin) + length + strlen(jsEnd);
-    char* beginMem = (char*)browser->memalloc(totalLength);
-    char* middleMem = beginMem + strlen(jsBegin);
-    char* endMem = middleMem + length;
-
-    // copy into the allocated memory
-    memcpy(beginMem, jsBegin, strlen(jsBegin));
-    memcpy(middleMem, text, length);
-    memcpy(endMem, jsEnd, strlen(jsEnd));
-
-    gLogI.log(kDebug_ANPLogType, "text: %.*s\n", totalLength, (char*)beginMem);
-
-    // execute the javascript in the plugin's DOM object
-    NPString script = { (char*)beginMem, totalLength };
-    NPVariant scriptVariant;
-    if (!browser->evaluate(inst(), windowObject, &script, &scriptVariant))
-        gLogI.log(kError_ANPLogType, " ------ %p Unable to eval the JS.", inst());
-
-    // free the memory allocated within the browser
-    browser->memfree(beginMem);
-}
-
-int16_t EventPlugin::handleEvent(const ANPEvent* evt) {
-    switch (evt->eventType) {
-
-        case kDraw_ANPEventType: {
-            switch (evt->data.draw.model) {
-                case kBitmap_ANPDrawingModel:
-                    drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
-                    return 1;
-                default:
-                    break;   // unknown drawing model
-            }
-        }
-        case kLifecycle_ANPEventType:
-            switch (evt->data.lifecycle.action) {
-                case kOnLoad_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-onLoad";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-                case kGainFocus_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-gainFocus";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-                case kLoseFocus_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-loseFocus";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-            }
-            return 1;
-        case kTouch_ANPEventType:
-            gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
-            break;
-        case kKey_ANPEventType:
-            gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
-            break;
-        default:
-            break;
-    }
-    return 0;   // unknown or unhandled event
-}
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
deleted file mode 100644
index 043be85..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "PluginObject.h"
-
-#ifndef eventPlugin__DEFINED
-#define eventPlugin__DEFINED
-
-class EventPlugin : public SubPlugin {
-public:
-    EventPlugin(NPP inst);
-    virtual ~EventPlugin();
-    virtual int16_t handleEvent(const ANPEvent* evt);
-
-private:
-    void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
-    void printToDiv(const char* text, int length);
-};
-
-#endif // eventPlugin__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
deleted file mode 100644
index 511180c..0000000
--- a/tests/BrowserTestPlugin/jni/main.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "android_npapi.h"
-#include "main.h"
-#include "PluginObject.h"
-#include "EventPlugin.h"
-
-NPNetscapeFuncs* browser;
-#define EXPORT __attribute__((visibility("default")))
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
-        char* argn[], char* argv[], NPSavedData* saved);
-NPError NPP_Destroy(NPP instance, NPSavedData** save);
-NPError NPP_SetWindow(NPP instance, NPWindow* window);
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
-        NPBool seekable, uint16_t* stype);
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-int32_t   NPP_WriteReady(NPP instance, NPStream* stream);
-int32_t   NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
-        void* buffer);
-void    NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
-void    NPP_Print(NPP instance, NPPrint* platformPrint);
-int16_t   NPP_HandleEvent(NPP instance, void* event);
-void    NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
-        void* notifyData);
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
-
-extern "C" {
-EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
-EXPORT const char* NP_GetMIMEDescription(void);
-EXPORT void NP_Shutdown(void);
-};
-
-ANPAudioTrackInterfaceV0    gSoundI;
-ANPBitmapInterfaceV0        gBitmapI;
-ANPCanvasInterfaceV0        gCanvasI;
-ANPLogInterfaceV0           gLogI;
-ANPPaintInterfaceV0         gPaintI;
-ANPPathInterfaceV0          gPathI;
-ANPTypefaceInterfaceV0      gTypefaceI;
-ANPWindowInterfaceV0        gWindowI;
-
-#define ARRAY_COUNT(array)      (sizeof(array) / sizeof(array[0]))
-
-NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env)
-{
-    // Make sure we have a function table equal or larger than we are built against.
-    if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
-        return NPERR_GENERIC_ERROR;
-    }
-
-    // Copy the function table (structure)
-    browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
-    memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
-
-    // Build the plugin function table
-    pluginFuncs->version = 11;
-    pluginFuncs->size = sizeof(pluginFuncs);
-    pluginFuncs->newp = NPP_New;
-    pluginFuncs->destroy = NPP_Destroy;
-    pluginFuncs->setwindow = NPP_SetWindow;
-    pluginFuncs->newstream = NPP_NewStream;
-    pluginFuncs->destroystream = NPP_DestroyStream;
-    pluginFuncs->asfile = NPP_StreamAsFile;
-    pluginFuncs->writeready = NPP_WriteReady;
-    pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
-    pluginFuncs->print = NPP_Print;
-    pluginFuncs->event = NPP_HandleEvent;
-    pluginFuncs->urlnotify = NPP_URLNotify;
-    pluginFuncs->getvalue = NPP_GetValue;
-    pluginFuncs->setvalue = NPP_SetValue;
-
-    static const struct {
-        NPNVariable     v;
-        uint32_t        size;
-        ANPInterface*   i;
-    } gPairs[] = {
-        { kCanvasInterfaceV0_ANPGetValue,       sizeof(gCanvasI),   &gCanvasI },
-        { kLogInterfaceV0_ANPGetValue,          sizeof(gLogI),      &gLogI },
-        { kPaintInterfaceV0_ANPGetValue,        sizeof(gPaintI),    &gPaintI },
-        { kTypefaceInterfaceV0_ANPGetValue,     sizeof(gTypefaceI), &gTypefaceI },
-    };
-    for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
-        gPairs[i].i->inSize = gPairs[i].size;
-        NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
-        if (err) {
-            return err;
-        }
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-void NP_Shutdown(void)
-{
-
-}
-
-const char *NP_GetMIMEDescription(void)
-{
-    return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
-}
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
-                char* argn[], char* argv[], NPSavedData* saved)
-{
-
-
-    gLogI.log(kDebug_ANPLogType, "creating plugin");
-
-    PluginObject *obj = NULL;
-
-    // Scripting functions appeared in NPAPI version 14
-    if (browser->version >= 14) {
-    instance->pdata = browser->createobject (instance, getPluginClass());
-    obj = static_cast<PluginObject*>(instance->pdata);
-    memset(obj, 0, sizeof(*obj));
-    } else {
-        return NPERR_GENERIC_ERROR;
-    }
-
-    // select the drawing model
-    ANPDrawingModel model = kBitmap_ANPDrawingModel;
-
-    // notify the plugin API of the drawing model we wish to use. This must be
-    // done prior to creating certain subPlugin objects (e.g. surfaceViews)
-    NPError err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
-                            reinterpret_cast<void*>(model));
-    if (err) {
-        gLogI.log(kError_ANPLogType, "request model %d err %d", model, err);
-        return err;
-    }
-
-    // create the sub-plugin
-    obj->subPlugin = new EventPlugin(instance);
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_Destroy(NPP instance, NPSavedData** save)
-{
-    PluginObject *obj = (PluginObject*) instance->pdata;
-    if (obj) {
-        delete obj->subPlugin;
-        browser->releaseobject(&obj->header);
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_SetWindow(NPP instance, NPWindow* window)
-{
-    PluginObject *obj = (PluginObject*) instance->pdata;
-
-    // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
-    if (obj != NULL) {
-        obj->window = window;
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
-{
-    *stype = NP_ASFILEONLY;
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
-{
-    return NPERR_NO_ERROR;
-}
-
-int32_t NPP_WriteReady(NPP instance, NPStream* stream)
-{
-    return 0;
-}
-
-int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer)
-{
-    return 0;
-}
-
-void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
-{
-}
-
-void NPP_Print(NPP instance, NPPrint* platformPrint)
-{
-}
-
-int16_t NPP_HandleEvent(NPP instance, void* event)
-{
-    PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
-    const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
-
-    if(!obj->subPlugin) {
-        gLogI.log(kError_ANPLogType, "the sub-plugin is null.");
-        return 0; // unknown or unhandled event
-    }
-    else {
-        return obj->subPlugin->handleEvent(evt);
-    }
-}
-
-void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
-{
-}
-
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
-
-    if (variable == NPPVpluginNameString) {
-        const char **str = (const char **)value;
-        *str = "Browser Test Plugin";
-        return NPERR_NO_ERROR;
-    }
-
-    if (variable == NPPVpluginDescriptionString) {
-        const char **str = (const char **)value;
-        *str = "Description of Browser Test Plugin";
-        return NPERR_NO_ERROR;
-    }
-
-    return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
-{
-    if (variable == NPPVpluginScriptableNPObject) {
-        void **v = (void **)value;
-        PluginObject *obj = (PluginObject*) instance->pdata;
-
-        if (obj)
-            browser->retainobject((NPObject*)obj);
-
-        *v = obj;
-        return NPERR_NO_ERROR;
-    }
-
-    return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
-{
-    return NPERR_GENERIC_ERROR;
-}
-
diff --git a/tests/BrowserTestPlugin/jni/main.h b/tests/BrowserTestPlugin/jni/main.h
deleted file mode 100644
index e6e8c73..0000000
--- a/tests/BrowserTestPlugin/jni/main.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <npapi.h>
-#include <npfunctions.h>
-#include <npruntime.h>
-#include "android_npapi.h"
-
-extern NPNetscapeFuncs* browser;
diff --git a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png b/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
deleted file mode 100644
index 47c79d1..0000000
--- a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
+++ /dev/null
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/Android.mk b/tests/Camera2Tests/SmartCamera/Android.mk
new file mode 100644
index 0000000..3fa8f54
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/Android.mk
@@ -0,0 +1,14 @@
+# Copyright 2013 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.
+include $(call all-subdir-makefiles)
diff --git a/tests/Camera2Tests/SmartCamera/README.txt b/tests/Camera2Tests/SmartCamera/README.txt
new file mode 100644
index 0000000..1fff3ab8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/README.txt
@@ -0,0 +1,60 @@
+Copyright 2013 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.
+
+
+Smart Camera / Auto Snapshot (formerly named SimpleCamera) ReadMe
+
+Created by: Benjamin W Hendricks
+
+How to build the application:
+From root: make SmartCamera will build the apk for generic
+Otherwise, to build the application for a specific device, lunch to that device
+and then run mm while in the SimpleCamera directory.
+Then take the given Install path (out/target/.../SmartCamera.apk)
+and run adb install out/target/.../SmartCamera.apk. The application should
+then appear in the launcher of your device.
+You might also need to run adb sync after building to sync the
+libsmartcamera_jni library
+Summarized:
+    make SmartCamera
+    adb remount
+    adb sync
+    adb install -r $ANDROID_PRODUCT_OUT/data/app/SmartCamera.apk
+
+How to run the application:
+On a Nexus 7, open up the application from the launcher, and the camera preview
+should appear. From there, you can go to the gallery with the gallery button or
+press start to start capturing images. You can also change the number of images
+to be captured by changing the number on the spinner (between 1-10).
+
+What does it do:
+The application tries to take good pictures for you automatically when in the
+start mode. On stop, the application will capture whatever images are in the
+bottom preview and save them to the Gallery. It does this by looking at the
+following image features:
+    - Sharpness
+    - Brightness
+    - Motion of the device
+    - Colorfulness
+    - Contrast
+    - Exposure (over/under)
+
+By comparing each of these features frame by frame, a score is calculated to
+determine whether an image is better or worse than the previous few frames,
+and from that score I can determine the great images from the bad ones.
+
+What libraries does it use:
+- Mobile Filter Framework (MFF)
+- Camera2 API
+- Renderscript
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/.classpath b/tests/Camera2Tests/SmartCamera/SimpleCamera/.classpath
new file mode 100644
index 0000000..3f9691c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/.project b/tests/Camera2Tests/SmartCamera/SimpleCamera/.project
new file mode 100644
index 0000000..2517e2d
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>CameraShoot</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
new file mode 100644
index 0000000..801c81c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2013 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.
+
+ifneq ($(TARGET_BUILD_JAVA_SUPPORT_LEVEL),)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+# comment it out for now since we need use some hidden APIs
+# LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := SmartCamera
+LOCAL_JNI_SHARED_LIBRARIES := libsmartcamera_jni
+
+include $(BUILD_PACKAGE)
+
+# Include packages in subdirectories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+endif
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/AndroidManifest.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/AndroidManifest.xml
new file mode 100644
index 0000000..0681868
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      android:versionCode="1"
+      android:versionName="1.0"
+      package="androidx.media.filterfw.samples.simplecamera">
+    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19"/>
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <application android:label="Smart Camera"
+                  android:debuggable="true">
+    <uses-library android:name="com.google.android.media.effects"
+                  android:required="false" />
+
+        <activity android:name=".SmartCamera"
+                  android:label="Smart Camera"
+                  android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/0002_000390.jpg b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/0002_000390.jpg
new file mode 100644
index 0000000..9b4bce4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/0002_000390.jpg
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLeyesclosed_100.emd b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLeyesclosed_100.emd
new file mode 100644
index 0000000..8c3d811
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLeyesclosed_100.emd
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLjoy_100.emd b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLjoy_100.emd
new file mode 100644
index 0000000..4ae3fbd
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/assets/frsdk_expression_modules/BCLjoy_100.emd
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/ic_launcher-web.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/ic_launcher-web.png
new file mode 100644
index 0000000..f142216
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/ic_launcher-web.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk
new file mode 100644
index 0000000..616a11b
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2013 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.
+#
+
+FILTERFW_NATIVE_PATH := $(call my-dir)
+
+
+#
+# Build module libfilterframework
+#
+LOCAL_PATH := $(FILTERFW_NATIVE_PATH)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := 14
+
+LOCAL_MODULE := libsmartcamera_jni
+
+LOCAL_SRC_FILES := contrast.cpp \
+                brightness.cpp \
+                exposure.cpp \
+                colorspace.cpp \
+                histogram.cpp \
+                frametovalues.cpp \
+                pixelutils.cpp \
+                sobeloperator.cpp \
+                stats_scorer.cpp
+
+LOCAL_STATIC_LIBRARIES += \
+    libcutils
+
+LOCAL_C_INCLUDES += \
+    system/core/include \
+
+LOCAL_NDK_STL_VARIANT := stlport_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Application.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Application.mk
new file mode 100644
index 0000000..2b93b3c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Application.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2013 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.
+#
+
+APP_STL := stlport_static
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.cpp
new file mode 100644
index 0000000..998fd4c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Native function to extract brightness from image (handed down as ByteBuffer).
+
+#include "brightness.h"
+
+#include <math.h>
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+jfloat
+Java_androidx_media_filterfw_samples_simplecamera_AvgBrightnessFilter_brightnessOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer) {
+
+    if (imageBuffer == 0) {
+        return 0.0f;
+    }
+    float pixelTotals[] = { 0.0f, 0.0f, 0.0f };
+    const int numPixels = width * height;
+    unsigned char* srcPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    for (int i = 0; i < numPixels; i++) {
+        pixelTotals[0] += *(srcPtr + 4 * i);
+        pixelTotals[1] += *(srcPtr + 4 * i + 1);
+        pixelTotals[2] += *(srcPtr + 4 * i + 2);
+    }
+    float avgPixels[] = { 0.0f, 0.0f, 0.0f };
+
+    avgPixels[0] = pixelTotals[0] / numPixels;
+    avgPixels[1] = pixelTotals[1] / numPixels;
+    avgPixels[2] = pixelTotals[2] / numPixels;
+    float returnValue = sqrt(0.241f * avgPixels[0] * avgPixels[0] +
+                            0.691f * avgPixels[1] * avgPixels[1] +
+                            0.068f * avgPixels[2] * avgPixels[2]);
+
+    return returnValue / 255;
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.h
new file mode 100644
index 0000000..c09e3b5
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/brightness.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2013 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.
+ */
+
+// Native function to extract brightness from image (handed down as ByteBuffer).
+
+#ifndef ANDROID_FILTERFW_JNI_BRIGHTNESS_H
+#define ANDROID_FILTERFW_JNI_BRIGHTNESS_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    JNIEXPORT jfloat JNICALL
+    Java_androidx_media_filterfw_samples_simplecamera_AvgBrightnessFilter_brightnessOperator(
+        JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_BRIGHTNESS_H
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.cpp
new file mode 100644
index 0000000..63e2ebf
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "colorspace.h"
+
+#include <jni.h>
+#include <stdint.h>
+
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+typedef int32_t int32;
+
+// RGBA helper struct allows access as int and individual channels
+// WARNING: int value depends on endianness and should not be used to analyze individual channels.
+union Rgba {
+  uint32 color;
+  uint8 channel[4];
+};
+
+// Channel index constants
+static const uint8 kRed = 0;
+static const uint8 kGreen = 1;
+static const uint8 kBlue = 2;
+static const uint8 kAlpha = 3;
+
+// Clamp to range 0-255
+static inline uint32 clamp(int32 x) {
+  return x > 255 ? 255 : (x < 0 ? 0 : x);
+}
+
+// Convert YUV to RGBA
+// This uses the ITU-R BT.601 coefficients.
+static inline Rgba convertYuvToRgba(int32 y, int32 u, int32 v) {
+  Rgba color;
+  color.channel[kRed] = clamp(y + static_cast<int>(1.402 * v));
+  color.channel[kGreen] = clamp(y - static_cast<int>(0.344 * u + 0.714 * v));
+  color.channel[kBlue] = clamp(y + static_cast<int>(1.772 * u));
+  color.channel[kAlpha] = 0xFF;
+  return color;
+}
+
+// Colorspace conversion functions /////////////////////////////////////////////////////////////////
+void JNI_COLORSPACE_METHOD(nativeYuv420pToRgba8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
+  uint8* const pInput = static_cast<uint8*>(env->GetDirectBufferAddress(input));
+  Rgba* const pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
+
+  const int size = width * height;
+
+  uint8* pInY = pInput;
+  uint8* pInU = pInput + size;
+  uint8* pInV = pInput + size + size / 4;
+  Rgba* pOutColor = pOutput;
+
+  const int u_offset = size;
+  const int v_offset = u_offset + size / 4;
+
+  for (int y = 0; y < height; y += 2) {
+    for (int x = 0; x < width; x += 2) {
+      int u, v, y1, y2, y3, y4;
+
+      y1 = pInY[0];
+      y2 = pInY[1];
+      y3 = pInY[width];
+      y4 = pInY[width + 1];
+
+      u = *pInU - 128;
+      v = *pInV - 128;
+
+      pOutColor[0] = convertYuvToRgba(y1, u, v);
+      pOutColor[1] = convertYuvToRgba(y2, u, v);
+      pOutColor[width] = convertYuvToRgba(y3, u, v);
+      pOutColor[width + 1] = convertYuvToRgba(y4, u, v);
+
+      pInY += 2;
+      pInU++;
+      pInV++;
+      pOutColor += 2;
+    }
+    pInY += width;
+    pOutColor += width;
+  }
+}
+
+void JNI_COLORSPACE_METHOD(nativeArgb8888ToRgba8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
+  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
+  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
+
+  for (int i = 0; i < width * height; ++i) {
+    Rgba color_in = *pInput++;
+    Rgba& color_out = *pOutput++;
+    color_out.channel[kRed] = color_in.channel[kGreen];
+    color_out.channel[kGreen] = color_in.channel[kBlue];
+    color_out.channel[kBlue] = color_in.channel[kAlpha];
+    color_out.channel[kAlpha] = color_in.channel[kRed];
+  }
+}
+
+void JNI_COLORSPACE_METHOD(nativeRgba8888ToHsva8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
+  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
+  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
+
+  int r, g, b, a, h, s, v, c_max, c_min;
+  float delta;
+  for (int i = 0; i < width * height; ++i) {
+    Rgba color_in = *pInput++;
+    Rgba& color_out = *pOutput++;
+    r = color_in.channel[kRed];
+    g = color_in.channel[kGreen];
+    b = color_in.channel[kBlue];
+    a = color_in.channel[kAlpha];
+
+    if (r > g) {
+      c_min = (g > b) ? b : g;
+      c_max = (r > b) ? r : b;
+    } else {
+      c_min = (r > b) ? b : r;
+      c_max = (g > b) ? g : b;
+    }
+    delta = c_max -c_min;
+
+    float scaler = 255 * 60 / 360.0f;
+    if (c_max == r) {
+      h = (g > b) ? static_cast<int>(scaler * (g - b) / delta) :
+          static_cast<int>(scaler * ((g - b) / delta + 6));
+    } else if (c_max == g) {
+      h = static_cast<int>(scaler * ((b - r) / delta + 2));
+    } else {  // Cmax == b
+      h = static_cast<int>(scaler * ((r - g) / delta + 4));
+    }
+    s = (delta == 0.0f) ? 0 : static_cast<unsigned char>(delta / c_max * 255);
+    v = c_max;
+
+    color_out.channel[kRed] = h;
+    color_out.channel[kGreen] = s;
+    color_out.channel[kBlue] = v;
+    color_out.channel[kAlpha] = a;
+  }
+}
+
+void JNI_COLORSPACE_METHOD(nativeRgba8888ToYcbcra8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height) {
+  Rgba* pInput = static_cast<Rgba*>(env->GetDirectBufferAddress(input));
+  Rgba* pOutput = static_cast<Rgba*>(env->GetDirectBufferAddress(output));
+
+  int r, g, b;
+  for (int i = 0; i < width * height; ++i) {
+    Rgba color_in = *pInput++;
+    Rgba& color_out = *pOutput++;
+    r = color_in.channel[kRed];
+    g = color_in.channel[kGreen];
+    b = color_in.channel[kBlue];
+
+    color_out.channel[kRed] =
+        static_cast<unsigned char>((65.738 * r + 129.057 * g + 25.064 * b) / 256 + 16);
+    color_out.channel[kGreen] =
+        static_cast<unsigned char>((-37.945 * r - 74.494 * g + 112.439 * b) / 256 + 128);
+    color_out.channel[kBlue] =
+        static_cast<unsigned char>((112.439 * r - 94.154 * g - 18.285 * b) / 256 + 128);
+    color_out.channel[kAlpha] = color_in.channel[kAlpha];
+  }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.h
new file mode 100644
index 0000000..c332749
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/colorspace.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_COLORSPACE_H
+#define ANDROID_FILTERFW_JNI_COLORSPACE_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JNI_COLORSPACE_METHOD(METHOD_NAME) \
+  Java_androidx_media_filterfw_ColorSpace_ ## METHOD_NAME
+
+JNIEXPORT void JNICALL
+JNI_COLORSPACE_METHOD(nativeYuv420pToRgba8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height);
+
+JNIEXPORT void JNICALL
+JNI_COLORSPACE_METHOD(nativeArgb8888ToRgba8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height);
+
+JNIEXPORT void JNICALL
+JNI_COLORSPACE_METHOD(nativeRgba8888ToHsva8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height);
+
+JNIEXPORT void JNICALL
+JNI_COLORSPACE_METHOD(nativeRgba8888ToYcbcra8888)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_COLORSPACE_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.cpp
new file mode 100644
index 0000000..222f738
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Native function to extract contrast ratio from image (handed down as ByteBuffer).
+
+#include "contrast.h"
+
+#include <math.h>
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+jfloat
+Java_androidx_media_filterfw_samples_simplecamera_ContrastRatioFilter_contrastOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer) {
+
+    if (imageBuffer == 0) {
+      return 0.0f;
+    }
+    float total = 0;
+    const int numPixels = width * height;
+    unsigned char* srcPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    float* lumArray = new float[numPixels];
+    for (int i = 0; i < numPixels; i++) {
+        lumArray[i] = (0.2126f * *(srcPtr + 4 * i) + 0.7152f *
+            *(srcPtr + 4 * i + 1) + 0.0722f * *(srcPtr + 4 * i + 2)) / 255;
+        total += lumArray[i];
+    }
+    const float avg = total / numPixels;
+    float sum = 0;
+
+    for (int i = 0; i < numPixels; i++) {
+        sum += (lumArray[i] - avg) * (lumArray[i] - avg);
+    }
+    delete[] lumArray;
+    return ((float) sqrt(sum / numPixels));
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.h
new file mode 100644
index 0000000..ddcd3d4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/contrast.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2013 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.
+ */
+
+// Native function to extract contrast from image (handed down as ByteBuffer).
+
+#ifndef ANDROID_FILTERFW_JNI_CONTRAST_H
+#define ANDROID_FILTERFW_JNI_CONTRAST_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    JNIEXPORT jfloat JNICALL
+    Java_androidx_media_filterfw_samples_simplecamera_ContrastRatioFilter_contrastOperator(
+        JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_CONTRAST_H
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.cpp
new file mode 100644
index 0000000..b2853f7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Native function to extract exposure from image (handed down as ByteBuffer).
+
+#include "exposure.h"
+
+#include <math.h>
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+
+jfloat
+Java_androidx_media_filterfw_samples_simplecamera_ExposureFilter_overExposureOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer) {
+    if (imageBuffer == 0) {
+        return 0.0f;
+    }
+    const int numPixels = width * height;
+    unsigned char* srcPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    int output = 0;
+    float tempLuminance = 0.0f;
+
+    for (int i = 0; i < numPixels; i++) {
+        tempLuminance = (0.2126f * *(srcPtr + 4 * i) +
+                        0.7152f * *(srcPtr + 4 * i + 1) +
+                        0.0722f * *(srcPtr + 4 * i + 2));
+        if (tempLuminance + 5 >= 255) {
+            output++;
+        }
+    }
+    return (static_cast<float>(output)) / numPixels;
+}
+
+jfloat
+Java_androidx_media_filterfw_samples_simplecamera_ExposureFilter_underExposureOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer) {
+    if (imageBuffer == 0) {
+        return 0.0f;
+    }
+    const int numPixels = width * height;
+    unsigned char* srcPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    int output = 0;
+    float tempLuminance = 0.0f;
+
+    for (int i = 0; i < numPixels; i++) {
+        tempLuminance = (0.2126f * *(srcPtr + 4 * i) +
+                        0.7152f * *(srcPtr + 4 * i + 1) +
+                        0.0722f * *(srcPtr + 4 * i + 2));
+        if (tempLuminance - 5 <= 0) {
+            output++;
+        }
+    }
+    return (static_cast<float>(output)) / numPixels;
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.h
new file mode 100644
index 0000000..bc6e3b1
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/exposure.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2013 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.
+ */
+
+// Native function to extract exposure from image (handed down as ByteBuffer).
+
+#ifndef ANDROID_FILTERFW_JNI_EXPOSURE_H
+#define ANDROID_FILTERFW_JNI_EXPOSURE_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    JNIEXPORT jfloat JNICALL
+    Java_androidx_media_filterfw_samples_simplecamera_ExposureFilter_underExposureOperator(
+        JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer);
+
+    JNIEXPORT jfloat JNICALL
+    Java_androidx_media_filterfw_samples_simplecamera_ExposureFilter_overExposureOperator(
+        JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_EXPOSURE_H
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.cpp
new file mode 100644
index 0000000..2e3a0ec
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+// Native function to extract histogram from image (handed down as ByteBuffer).
+
+#include "frametovalues.h"
+
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+#include "imgprocutil.h"
+
+jboolean Java_androidx_media_filterpacks_image_ToGrayValuesFilter_toGrayValues(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject grayBuffer )
+{
+    unsigned char* pixelPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    unsigned char* grayPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(grayBuffer));
+
+    if (pixelPtr == 0 || grayPtr == 0) {
+      return JNI_FALSE;
+    }
+
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;
+
+    // TODO: the current implementation is focused on the correctness not performance.
+    // If performance becomes an issue, it is better to increment pixelPtr directly.
+    int disp = 0;
+    for(int idx = 0; idx < numPixels; idx++, disp+=4) {
+      int R = *(pixelPtr + disp);
+      int G = *(pixelPtr + disp + 1);
+      int B = *(pixelPtr + disp + 2);
+      int gray = getIntensityFast(R, G, B);
+      *(grayPtr+idx) = static_cast<unsigned char>(gray);
+    }
+
+    return JNI_TRUE;
+}
+
+jboolean Java_androidx_media_filterpacks_image_ToRgbValuesFilter_toRgbValues(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject rgbBuffer )
+{
+    unsigned char* pixelPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    unsigned char* rgbPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(rgbBuffer));
+
+    if (pixelPtr == 0 || rgbPtr == 0) {
+      return JNI_FALSE;
+    }
+
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;
+
+    // TODO: this code could be revised to improve the performance as the TODO above.
+    int pixelDisp = 0;
+    int rgbDisp = 0;
+    for(int idx = 0; idx < numPixels; idx++, pixelDisp += 4, rgbDisp += 3) {
+      for (int c = 0; c < 3; ++c) {
+        *(rgbPtr + rgbDisp + c) = *(pixelPtr + pixelDisp + c);
+      }
+    }
+    return JNI_TRUE;
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.h
new file mode 100644
index 0000000..4abb848
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/frametovalues.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+// Native functions to pack a RGBA frame into either a one channel grayscale buffer
+// or a three channel RGB buffer.
+
+#ifndef ANDROID_FILTERFW_JNI_TOGRAYVALUES_H
+#define ANDROID_FILTERFW_JNI_TOGRAYVALUES_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_androidx_media_filterpacks_image_ToGrayValuesFilter_toGrayValues(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject grayBuffer );
+
+JNIEXPORT jboolean JNICALL
+Java_androidx_media_filterpacks_image_ToRgbValuesFilter_toRgbValues(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject rgbBuffer );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_TOGRAYVALUES_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.cpp
new file mode 100644
index 0000000..ba060d4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+// Native function to extract histogram from image (handed down as ByteBuffer).
+
+#include "histogram.h"
+
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+#include "imgprocutil.h"
+
+inline void addPixelToHistogram(unsigned char*& pImg, int* pHist, int numBins) {
+    int R = *(pImg++);
+    int G = *(pImg++);
+    int B = *(pImg++);
+    ++pImg;
+    int i = getIntensityFast(R, G, B);
+    int bin = clamp(0, static_cast<int>(static_cast<float>(i * numBins) / 255.0f), numBins - 1);
+    ++pHist[bin];
+}
+
+void Java_androidx_media_filterpacks_histogram_GrayHistogramFilter_extractHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject maskBuffer, jobject histogramBuffer )
+{
+    unsigned char* pImg = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    int* pHist = static_cast<int*>(env->GetDirectBufferAddress(histogramBuffer));
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
+    int numBins    = env->GetDirectBufferCapacity(histogramBuffer);
+
+    unsigned char* pMask = NULL;
+    if(maskBuffer != NULL) {
+        pMask = static_cast<unsigned char*>(env->GetDirectBufferAddress(maskBuffer));
+    }
+
+    for(int i = 0; i < numBins; ++i) pHist[i] = 0;
+
+    if(pMask == NULL) {
+        for( ; numPixels > 0; --numPixels) {
+            addPixelToHistogram(pImg, pHist, numBins);
+        }
+    } else {
+        for( ; numPixels > 0; --numPixels) {
+            if(*pMask == 0){
+                pMask += 4;
+                pImg  += 4;  // Note that otherwise addPixelToHistogram advances pImg by 4
+                continue;
+            }
+            pMask += 4;
+            addPixelToHistogram(pImg, pHist, numBins);
+        }
+    }
+}
+
+void Java_androidx_media_filterpacks_histogram_ChromaHistogramFilter_extractChromaHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer, jint hBins, jint sBins)
+{
+    unsigned char* pixelIn = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    float* histOut = static_cast<float*>(env->GetDirectBufferAddress(histogramBuffer));
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
+
+    for (int i = 0; i < hBins * sBins; ++i) histOut[i] = 0.0f;
+
+    int h, s, v;
+    float hScaler = hBins / 256.0f;
+    float sScaler = sBins / 256.0f;
+    for( ; numPixels > 0; --numPixels) {
+      h = *(pixelIn++);
+      s = *(pixelIn++);
+      v = *(pixelIn++);
+      pixelIn++;
+
+      int index = static_cast<int>(s * sScaler) * hBins + static_cast<int>(h * hScaler);
+      histOut[index] += 1.0f;
+    }
+}
+
+void Java_androidx_media_filterpacks_histogram_NewChromaHistogramFilter_extractChromaHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer,
+    jint hueBins, jint saturationBins, jint valueBins,
+    jint saturationThreshold, jint valueThreshold) {
+    unsigned char* pixelIn = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    float* histOut = static_cast<float*>(env->GetDirectBufferAddress(histogramBuffer));
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer) / 4;  // 4 bytes per pixel
+
+    // TODO: add check on the size of histOut
+    for (int i = 0; i < (hueBins * saturationBins + valueBins); ++i) {
+      histOut[i] = 0.0f;
+    }
+
+    for( ; numPixels > 0; --numPixels) {
+      int h = *(pixelIn++);
+      int s = *(pixelIn++);
+      int v = *(pixelIn++);
+
+      pixelIn++;
+      // If a pixel that is either too dark (less than valueThreshold) or colorless
+      // (less than saturationThreshold), if will be put in a 1-D value histogram instead.
+
+      int index;
+      if (s > saturationThreshold && v > valueThreshold) {
+        int sIndex = s * saturationBins / 256;
+
+        // Shifting hue index by 0.5 such that peaks of red, yellow, green, cyan, blue, pink
+        // will be at the center of some bins.
+        int hIndex = ((h * hueBins + 128) / 256) % hueBins;
+        index = sIndex * hueBins + hIndex;
+      } else {
+        index =  hueBins * saturationBins + (v * valueBins / 256);
+      }
+      histOut[index] += 1.0f;
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.h
new file mode 100644
index 0000000..b5e88aa
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/histogram.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+// Native function to extract histogram from image (handed down as ByteBuffer).
+
+#ifndef ANDROID_FILTERFW_JNI_HISTOGRAM_H
+#define ANDROID_FILTERFW_JNI_HISTOGRAM_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL
+Java_androidx_media_filterpacks_histogram_GrayHistogramFilter_extractHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject maskBuffer, jobject histogramBuffer );
+
+JNIEXPORT void JNICALL
+Java_androidx_media_filterpacks_histogram_ChromaHistogramFilter_extractChromaHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer, jint hBins, jint sBins);
+
+JNIEXPORT void JNICALL
+Java_androidx_media_filterpacks_histogram_NewChromaHistogramFilter_extractChromaHistogram(
+    JNIEnv* env, jclass clazz, jobject imageBuffer, jobject histogramBuffer,
+    jint hueBins, jint saturationBins, jint valueBins,
+    jint saturationThreshold, jint valueThreshold);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_HISTOGRAM_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/imgprocutil.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/imgprocutil.h
new file mode 100644
index 0000000..aef67a5
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/imgprocutil.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+// Some native low-level image processing functions.
+
+
+#ifndef ANDROID_FILTERFW_JNI_IMGPROCUTIL_H
+#define ANDROID_FILTERFW_JNI_IMGPROCUTIL_H
+
+inline int getIntensityFast(int R, int G, int B) {
+    return (R + R + R + B + G + G + G + G) >> 3;  // see http://stackoverflow.com/a/596241
+}
+
+inline int clamp(int min, int val, int max) {
+    return val < min ? min : (val > max ? max : val);
+        // Note that for performance reasons, this function does *not* check if min < max!
+}
+
+#endif // ANDROID_FILTERFW_JNI_IMGPROCUTIL_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.cpp
new file mode 100644
index 0000000..596c7c0
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "pixelutils.h"
+
+#include <stdint.h>
+
+typedef uint32_t uint32;
+
+void JNI_PIXELUTILS_METHOD(nativeCopyPixels)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height, jint offset,
+    jint pixStride, jint rowStride) {
+  uint32* pInPix = static_cast<uint32*>(env->GetDirectBufferAddress(input));
+  uint32* pOutput = static_cast<uint32*>(env->GetDirectBufferAddress(output));
+  uint32* pOutRow = pOutput + offset;
+  for (int y = 0; y < height; ++y) {
+    uint32* pOutPix = pOutRow;
+    for (int x = 0; x < width; ++x) {
+      *pOutPix = *(pInPix++);
+      pOutPix += pixStride;
+    }
+    pOutRow += rowStride;
+  }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.h
new file mode 100644
index 0000000..be69009
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/pixelutils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_PIXELUTILS_H
+#define ANDROID_FILTERFW_JNI_PIXELUTILS_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JNI_PIXELUTILS_METHOD(METHOD_NAME) \
+  Java_androidx_media_filterfw_PixelUtils_ ## METHOD_NAME
+
+JNIEXPORT void JNICALL
+JNI_PIXELUTILS_METHOD(nativeCopyPixels)(
+    JNIEnv* env, jclass clazz, jobject input, jobject output, jint width, jint height, jint offset,
+    jint pixStride, jint rowStride);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_PIXELUTILS_H
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.cpp
new file mode 100644
index 0000000..dc5c305
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+// Native function to extract histogram from image (handed down as ByteBuffer).
+
+#include "sobeloperator.h"
+
+#include <math.h>
+#include <string.h>
+#include <jni.h>
+#include <unistd.h>
+#include <android/log.h>
+
+#include "imgprocutil.h"
+
+/*
+ * Perform 1d convolution on 3 channel image either horizontally or vertically.
+ * Parameters:
+ *  inputHead: pointer to input image
+ *  length: the length of image in the chosen axis.
+ *  fragments: number of lines of the image in the chosen axis.
+ *  step: the 1d pixel distance between adjacent pixels in the chosen axis.
+ *  shift: the 1d pixel distance between adjacent lines in the chosen axis.
+ *  filter: pointer to 1d filter
+ *  halfSize: the length of filter is supposed to be (2 * halfSize + 1)
+ *  outputHead: pointer to output image
+ */
+
+void computeGradient(unsigned char* dataPtr, int width, int height, short* gxPtr, short* gyPtr) {
+  for (int i = 0; i < height; i++) {
+    for (int j = 0; j < width; j++) {
+      const int left = (j > 0)? -4 : 0;
+      const int right = (j < width - 1) ? 4 : 0;
+      const int curr = (i * width + j) * 4;
+      const int above = (i > 0) ? curr - 4 * width : curr;
+      const int below = (i < height - 1) ? curr + 4 * width : curr;
+      const int offset = (i * width + j) * 3;
+      for (int c = 0; c < 3; c++) {
+        *(gxPtr + offset + c) =
+            (*(dataPtr + curr + c + right) - *(dataPtr + curr + c + left)) * 2 +
+            *(dataPtr + above + c + right) - *(dataPtr + above + c + left) +
+            *(dataPtr + below + c + right) - *(dataPtr + below + c + left);
+        *(gyPtr + offset + c) =
+            (*(dataPtr + c + below) - *(dataPtr + c + above)) * 2 +
+            *(dataPtr + left + c + below) - *(dataPtr + left + c + above) +
+            *(dataPtr + right + c + below) - *(dataPtr + right + c + above);
+      }
+    }
+  }
+}
+
+jboolean Java_androidx_media_filterpacks_image_SobelFilter_sobelOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height, jobject imageBuffer,
+    jobject magBuffer, jobject dirBuffer) {
+
+  if (imageBuffer == 0) {
+    return JNI_FALSE;
+  }
+  unsigned char* srcPtr = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+  unsigned char* magPtr = (magBuffer == 0) ?
+      0 : static_cast<unsigned char*>(env->GetDirectBufferAddress(magBuffer));
+  unsigned char* dirPtr = (dirBuffer == 0) ?
+      0 : static_cast<unsigned char*>(env->GetDirectBufferAddress(dirBuffer));
+
+  int numPixels = width * height;
+  // TODO: avoid creating and deleting these buffers within this native function.
+  short* gxPtr = new short[3 * numPixels];
+  short* gyPtr = new short[3 * numPixels];
+  computeGradient(srcPtr, width, height, gxPtr, gyPtr);
+
+  unsigned char* mag = magPtr;
+  unsigned char* dir = dirPtr;
+  for (int i = 0; i < numPixels; ++i) {
+    for (int c = 0; c < 3; c++) {
+      int gx = static_cast<int>(*(gxPtr + 3 * i + c) / 8 + 127.5);
+      int gy = static_cast<int>(*(gyPtr + 3 * i + c) / 8 + 127.5);
+
+      // emulate arithmetic in GPU.
+      gx = 2 * gx - 255;
+      gy = 2 * gy - 255;
+      if (magPtr != 0) {
+        double value = sqrt(gx * gx + gy * gy);
+        *(magPtr + 4 * i + c) = static_cast<unsigned char>(value);
+      }
+      if (dirPtr != 0) {
+        *(dirPtr + 4 * i + c) = static_cast<unsigned char>(
+            (atan(static_cast<double>(gy)/static_cast<double>(gx)) + 3.14) / 6.28);
+      }
+    }
+    //setting alpha change to 1.0 (255)
+    if (magPtr != 0) {
+      *(magPtr + 4 * i + 3) = 255;
+    }
+    if (dirPtr != 0) {
+      *(dirPtr + 4 * i + 3) = 255;
+    }
+  }
+
+  delete[] gxPtr;
+  delete[] gyPtr;
+
+  return JNI_TRUE;
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.h
new file mode 100644
index 0000000..c7639d2
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/sobeloperator.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+// Native function to extract histogram from image (handed down as ByteBuffer).
+
+#ifndef ANDROID_FILTERFW_JNI_SOBELOPERATOR_H
+#define ANDROID_FILTERFW_JNI_SOBELOPERATOR_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_androidx_media_filterpacks_image_SobelFilter_sobelOperator(
+    JNIEnv* env, jclass clazz, jint width, jint height,
+    jobject imageBuffer, jobject magBuffer, jobject dirBuffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_SOBELOPERATOR_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.cpp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.cpp
new file mode 100644
index 0000000..f282675
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+// Stats (mean and stdev) scoring in the native.
+
+#include "stats_scorer.h"
+
+#include <jni.h>
+#include <math.h>
+
+void Java_androidx_media_filterpacks_numeric_StatsFilter_score(
+    JNIEnv* env, jobject thiz, jobject imageBuffer, jfloatArray statsArray)
+{
+    unsigned char* pImg = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    int numPixels  = env->GetDirectBufferCapacity(imageBuffer);  // 1 byte per pixel
+    float sum = 0.0;
+    float sumSquares = 0.0;
+
+    for (int i = 0; i < numPixels; ++i) {
+        float val = static_cast<float>(pImg[i]);
+        sum += val;
+        sumSquares += val * val;
+    }
+    jfloat result[2];
+    result[0] = sum / numPixels;  // mean
+    result[1] = sqrt((sumSquares - numPixels * result[0] * result[0]) / (numPixels - 1));  // stdev.
+    env->SetFloatArrayRegion(statsArray, 0, 2, result);
+}
+
+void Java_androidx_media_filterpacks_numeric_StatsFilter_regionscore(
+    JNIEnv* env, jobject thiz, jobject imageBuffer, jint width, jint height,
+    jfloat left, jfloat top, jfloat right, jfloat bottom, jfloatArray statsArray)
+{
+    unsigned char* pImg = static_cast<unsigned char*>(env->GetDirectBufferAddress(imageBuffer));
+    int xStart = static_cast<int>(width * left);
+    int xEnd = static_cast<int>(width * right);
+    int yStart = static_cast<int>(height * top);
+    int yEnd = static_cast<int>(height * bottom);
+    int numPixels  = (xEnd - xStart) * (yEnd - yStart);
+    float sum = 0.0;
+    float sumSquares = 0.0;
+
+    for (int y = yStart; y < yEnd; y++) {
+      int disp = width * y;
+      for (int x = xStart; x < xEnd; ++x) {
+        float val = static_cast<float>(*(pImg + disp + x));
+        sum += val;
+        sumSquares += val * val;
+      }
+    }
+    jfloat result[2];
+    result[0] = sum / numPixels;  // mean
+    result[1] = (numPixels == 1) ?
+        0 : sqrt((sumSquares - numPixels * result[0] * result[0]) / (numPixels - 1));  // stdev.
+    env->SetFloatArrayRegion(statsArray, 0, 2, result);
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.h b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.h
new file mode 100644
index 0000000..a951ec9
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/stats_scorer.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+// Stats (mean and stdev) scoring in the native.
+
+#ifndef ANDROID_FILTERFW_JNI_STATS_SCORER_H
+#define ANDROID_FILTERFW_JNI_STATS_SCORER_H
+
+#include <jni.h>
+
+#define JNI_FES_FUNCTION(name) Java_androidx_media_filterpacks_numeric_StatsFilter_ ## name
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL
+JNI_FES_FUNCTION(score)(
+    JNIEnv* env, jobject thiz, jobject imageBuffer, jfloatArray statsArray);
+
+JNIEXPORT void JNICALL
+JNI_FES_FUNCTION(regionscore)(
+   JNIEnv* env, jobject thiz, jobject imageBuffer, jint width, jint height,
+   jfloat lefp, jfloat top, jfloat right, jfloat bottom, jfloatArray statsArray);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif  // ANDROID_FILTERFW_JNI_STATS_SCORER_H
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/proguard-project.txt b/tests/Camera2Tests/SmartCamera/SimpleCamera/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/project.properties b/tests/Camera2Tests/SmartCamera/SimpleCamera/project.properties
new file mode 100644
index 0000000..10149cb
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-16
+android.library.reference.1=../../filterfw
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/black_screen.jpg b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/black_screen.jpg
new file mode 100644
index 0000000..702d9fa
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/black_screen.jpg
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_launcher.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..1c7b44a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_gallery.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_gallery.png
new file mode 100644
index 0000000..f61bbd8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_gallery.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_quill.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_quill.png
new file mode 100644
index 0000000..7ea01b7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_quill.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_save.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_save.png
new file mode 100644
index 0000000..62d0b9a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-hdpi/ic_menu_save.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-ldpi/ic_launcher.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..b42e903
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-mdpi/ic_launcher.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..d4b4d6b
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/android_figure.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/android_figure.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/android_figure.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/oldframe.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/oldframe.png
new file mode 100644
index 0000000..8b7ae63
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/oldframe.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/polaroid.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/polaroid.png
new file mode 100644
index 0000000..5504c57
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-nodpi/polaroid.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-xhdpi/ic_launcher.png b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..3bb5454
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/imageview.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/imageview.xml
new file mode 100644
index 0000000..4e20c3f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/imageview.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 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.
+-->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="180px"
+    android:layout_height="240px"
+    android:src="@drawable/black_screen"
+    android:adjustViewBounds="true"
+/>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/simplecamera.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/simplecamera.xml
new file mode 100644
index 0000000..8d8ff51
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/layout/simplecamera.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <RelativeLayout android:id="@+id/surfaceViewLayout"
+        android:layout_width="wrap_content"
+        android:layout_height="1240px"
+        android:layout_alignParentTop="true" >
+        <SurfaceView android:id="@+id/cameraView"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1.0"
+        />
+        <Button android:id="@+id/startButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/startButton"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true"
+        />
+        <Button android:id="@+id/galleryOpenButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/galleryOpenButton"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        />
+        <Spinner android:id="@+id/spinner"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:entries="@array/number_array"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentRight="true"
+        />
+        <TextView android:id="@+id/imagesSavedTextView"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="16dip"
+        android:text="@string/imagesSavedTextView"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentBottom="true"
+        android:textColor="#FF0000"
+        android:textSize="20sp"
+        />
+    </RelativeLayout>
+    <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/scrollView"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" >
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:id="@+id/scrollViewLinearLayout"
+      android:orientation="horizontal"
+      android:layout_width="fill_parent"
+      android:layout_height="320px">
+    </LinearLayout>
+    </HorizontalScrollView>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:orientation="horizontal"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content">
+        <TextView android:id="@+id/goodOrBadTextView"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:padding="16dip"
+        android:text="@string/goodOrBadTextView"
+        />
+        <TextView android:id="@+id/fpsTextView"
+        android:layout_height="fill_parent"
+        android:layout_width="wrap_content"
+        android:padding="16dip"
+        android:text="@string/fpsTextView"
+        />
+        <TextView android:id="@+id/scoreTextView"
+        android:layout_height="fill_parent"
+        android:layout_width="wrap_content"
+        android:padding="16dip"
+        android:text="@string/scoreTextView"
+        />
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/raw/camera_graph.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/raw/camera_graph.xml
new file mode 100644
index 0000000..6661fd7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/raw/camera_graph.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2013 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.
+-->
+
+<graph>
+    <!-- Packages -->
+    <import package="androidx.media.filterpacks.base"/>
+    <import package="androidx.media.filterpacks.image"/>
+    <import package="androidx.media.filterpacks.video"/>
+    <import package="androidx.media.filterpacks.text" />
+    <import package="androidx.media.filterpacks.numeric" />
+    <import package="androidx.media.filterpacks.face" />
+    <import package="androidx.media.filterpacks.transform" />
+    <import package="androidx.media.filterpacks.performance" />
+    <import package="androidx.media.filterfw.samples.simplecamera" />
+    <import package="androidx.media.filterpacks.histogram" />
+    <import package="androidx.media.filterpacks.colorspace" />
+    <import package="androidx.media.filterpacks.sensors" />
+
+    <!-- Filters -->
+    <filter class="ResizeFilter" name="resize" >
+        <input name="outputWidth" intValue="480" />
+        <input name="outputHeight" intValue="640" />
+    </filter>
+
+    <filter class="Camera2Source" name="camera"/>
+
+    <filter class="BranchFilter" name="mainBranch" />
+    <filter class="BranchFilter" name="preMainBranch" />
+    <filter class="BranchFilter" name="featureBranch" />
+
+    <filter class="SurfaceHolderTarget" name="camViewTarget"/>
+
+    <filter class="ScaleFilter" name="scale" >
+        <input name="scale" floatValue="0.50"/>
+    </filter>
+
+    <filter class="SobelFilter" name="sobel" />
+    <filter class="StatsFilter" name="statsFilter" />
+    <filter class="NormFilter" name="normFilter" />
+    <filter class="TextViewTarget" name="goodOrBadTextView" />
+    <filter class="ToGrayValuesFilter" name="sobelConverter" />
+    <filter class="AverageFilter" name="avgFilter" />
+
+    <var name="startCapture" />
+    <filter class="ImageGoodnessFilter" name="goodnessFilter" >
+        <input name="capturing" varValue="startCapture" />
+    </filter>
+
+    <filter class="ToStringFilter" name="scoreToString" />
+    <filter class="TextViewTarget" name="scoreTextView" />
+
+    <filter class="ExposureFilter" name="exposure" />
+
+    <filter class="TextViewTarget" name="fpsTextView" />
+    <filter class="ToStringFilter" name="throughputToString" />
+
+
+    <filter class="ContrastRatioFilter" name="contrast" />
+
+    <filter class="ScaleFilter" name="secondaryScale" >
+        <input name="scale" floatValue="0.50"/>
+    </filter>
+
+    <filter class="ThroughputFilter" name="throughput" />
+
+    <filter class="NewChromaHistogramFilter" name="histogram" />
+    <filter class="ColorfulnessFilter" name="colorfulness" />
+
+    <filter class="MotionSensorWTime" name="motion" />
+
+    <filter class="AvgBrightnessFilter" name="brightness" />
+
+    <filter class="RotateFilter" name="rotate" />
+
+    <filter class="BrightnessFilter" name="snapBrightness" />
+    <filter class="WaveTriggerFilter" name="snapEffect" />
+    <!-- Connections -->
+    <connect sourceFilter="camera" sourcePort="video"
+        targetFilter="rotate" targetPort="image" />
+
+    <connect sourceFilter="camera" sourcePort="orientation"
+        targetFilter="rotate" targetPort="rotateAngle" />
+
+    <connect sourceFilter="rotate" sourcePort="image"
+        targetFilter="resize" targetPort="image" />
+    <connect sourceFilter="resize" sourcePort="image"
+        targetFilter="preMainBranch" targetPort="input" />
+    <connect sourceFilter="preMainBranch" sourcePort="toMainBranch"
+        targetFilter="scale" targetPort="image" />
+    <connect sourceFilter="scale" sourcePort="image"
+         targetFilter="mainBranch" targetPort="input" />
+
+    <connect sourceFilter="preMainBranch" sourcePort="toGoodnessFilter"
+        targetFilter="goodnessFilter" targetPort="image" />
+    <connect sourceFilter="mainBranch" sourcePort="toFeatureBranch"
+        targetFilter="secondaryScale" targetPort="image" />
+    <connect sourceFilter="secondaryScale" sourcePort="image"
+        targetFilter="featureBranch" targetPort="input" />
+
+    <connect sourceFilter="featureBranch" sourcePort="toSobel"
+         targetFilter="sobel" targetPort="image" />
+
+    <connect sourceFilter="sobel" sourcePort="magnitude"
+         targetFilter="sobelConverter" targetPort="image" />
+
+    <connect sourceFilter="sobelConverter" sourcePort="image"
+         targetFilter="statsFilter" targetPort="buffer" />
+
+    <connect sourceFilter="statsFilter" sourcePort="mean"
+         targetFilter="normFilter" targetPort="x" />
+
+    <connect sourceFilter="statsFilter" sourcePort="stdev"
+         targetFilter="normFilter" targetPort="y" />
+
+    <connect sourceFilter="normFilter" sourcePort="norm"
+         targetFilter="avgFilter" targetPort="sharpness" />
+
+    <connect sourceFilter="avgFilter" sourcePort="avg"
+         targetFilter="goodnessFilter" targetPort="sharpness" />
+
+    <connect sourceFilter="goodnessFilter" sourcePort="goodOrBadPic"
+         targetFilter="goodOrBadTextView" targetPort="text" />
+
+    <connect sourceFilter="featureBranch" sourcePort="toExposure"
+        targetFilter="exposure" targetPort="image" />
+    <connect sourceFilter="exposure" sourcePort="underExposureRating"
+        targetFilter="goodnessFilter" targetPort="underExposure" />
+    <connect sourceFilter="exposure" sourcePort="overExposureRating"
+        targetFilter="goodnessFilter" targetPort="overExposure" />
+
+    <connect sourceFilter="goodnessFilter" sourcePort="score"
+        targetFilter="scoreToString" targetPort="object" />
+    <connect sourceFilter="scoreToString" sourcePort="string"
+        targetFilter="scoreTextView" targetPort="text" />
+
+    <connect sourceFilter="mainBranch" sourcePort="camView"
+        targetFilter="throughput" targetPort="frame" />
+    <connect sourceFilter="throughput" sourcePort="frame"
+        targetFilter="snapBrightness" targetPort="image" />
+    <connect sourceFilter="snapEffect" sourcePort="value"
+        targetFilter="snapBrightness" targetPort="brightness" />
+    <connect sourceFilter="snapBrightness" sourcePort="image"
+        targetFilter="camViewTarget" targetPort="image" />
+    <connect sourceFilter="throughput" sourcePort="throughput"
+        targetFilter="throughputToString" targetPort="object" />
+    <connect sourceFilter="throughputToString" sourcePort="string"
+        targetFilter="fpsTextView" targetPort="text" />
+
+    <connect sourceFilter="featureBranch" sourcePort="contrastRatio"
+        targetFilter="contrast" targetPort="image" />
+    <connect sourceFilter="contrast" sourcePort="contrastRatingToGoodness"
+        targetFilter="goodnessFilter" targetPort="contrastRating" />
+
+    <connect sourceFilter="mainBranch" sourcePort="colorfulness"
+        targetFilter="histogram" targetPort="image" />
+    <connect sourceFilter="histogram" sourcePort="histogram"
+        targetFilter="colorfulness" targetPort="histogram" />
+    <connect sourceFilter="colorfulness" sourcePort="score"
+        targetFilter="goodnessFilter" targetPort="colorfulness" />
+
+    <connect sourceFilter="motion" sourcePort="values"
+        targetFilter="goodnessFilter" targetPort="motionValues" />
+
+    <connect sourceFilter="featureBranch" sourcePort="brightness"
+        targetFilter="brightness" targetPort="image" />
+    <connect sourceFilter="brightness" sourcePort="brightnessRating"
+        targetFilter="goodnessFilter" targetPort="brightness" />
+</graph>
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v11/styles.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v11/styles.xml
new file mode 100644
index 0000000..d408cbc
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v11/styles.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <style name="AppTheme" parent="android:Theme.Holo.Light" />
+
+</resources>
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v14/styles.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v14/styles.xml
new file mode 100644
index 0000000..1c089a7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values-v14/styles.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
+
+</resources>
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/strings.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/strings.xml
new file mode 100644
index 0000000..5e6b8ab
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2013 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.
+-->
+
+<resources>
+     <string name="goodOrBadTextView"> Good/Bad Picture </string>
+     <string name="fpsTextView"> FPS </string>
+     <string name="scoreTextView"> Score</string>
+     <string name="gallery"> Go To Gallery </string>
+     <string name="camera"> Go To Camera </string>
+     <string name="startButton" > Start </string>
+     <string name="imagesSavedTextView" > Images Saved </string>
+     <string name="galleryOpenButton" > Gallery </string>
+     <string-array name="number_array">
+        <item> 1 </item>
+        <item> 2 </item>
+        <item> 3 </item>
+        <item> 4 </item>
+        <item> 5 </item>
+        <item> 6 </item>
+        <item> 7 </item>
+        <item> 8 </item>
+        <item> 9 </item>
+        <item> 10 </item>
+     </string-array>
+</resources>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/styles.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/styles.xml
new file mode 100644
index 0000000..bd5027f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/res/values/styles.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <style name="AppTheme" parent="android:Theme.Light" />
+
+</resources>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java
new file mode 100644
index 0000000..216e743
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java
@@ -0,0 +1,929 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.Vector;
+
+final class BackingStore {
+
+    /** Access mode None: Frame data will not be accessed at all. */
+    static final int ACCESS_NONE = 0x00;
+    /** Access mode Bytes: Frame data will be accessed as a ByteBuffer. */
+    static final int ACCESS_BYTES = 0x01;
+    /** Access mode Texture: Frame data will be accessed as a TextureSource. */
+    static final int ACCESS_TEXTURE = 0x02;
+    /** Access mode RenderTarget: Frame data will be accessed as a RenderTarget. */
+    static final int ACCESS_RENDERTARGET = 0x04;
+    /** Access mode Object: Frame data will be accessed as a generic Object. */
+    static final int ACCESS_OBJECT = 0x08;
+    /** Access mode Bitmap: Frame data will be accessed as a Bitmap. */
+    static final int ACCESS_BITMAP = 0x10;
+    /** Access mode Allocation: Frame data will be accessed as a RenderScript Allocation. */
+    static final int ACCESS_ALLOCATION = 0x20;
+
+    private static final int BACKING_BYTEBUFFER = 1;
+    private static final int BACKING_TEXTURE = 2;
+    private static final int BACKING_OBJECT = 3;
+    private static final int BACKING_BITMAP = 4;
+    private static final int BACKING_ALLOCATION = 5;
+
+    private final FrameType mType;
+    private int[] mDimensions;
+    private long mTimestamp = Frame.TIMESTAMP_NOT_SET;
+
+    private final FrameManager mFrameManager;
+
+    private Vector<Backing> mBackings = new Vector<Backing>();
+
+    private boolean mWriteLocked = false;
+    private int mReadLocks = 0;
+
+    private int mRefCount = 1;
+
+    /** The most up-to-date data backing */
+    private Backing mCurrentBacking = null;
+
+    /** The currently locked backing */
+    private Backing mLockedBacking = null;
+
+    // Public Methods //////////////////////////////////////////////////////////////////////////////
+    public BackingStore(FrameType type, int[] dimensions, FrameManager frameManager) {
+        mType = type;
+        mDimensions = dimensions != null ? Arrays.copyOf(dimensions, dimensions.length) : null;
+        mFrameManager = frameManager;
+    }
+
+    public FrameType getFrameType() {
+        return mType;
+    }
+
+    public Object lockData(int mode, int accessFormat) {
+        return lockBacking(mode, accessFormat).lock(accessFormat);
+    }
+
+    public Backing lockBacking(int mode, int access) {
+        Backing backing = fetchBacking(mode, access);
+        if (backing == null) {
+            throw new RuntimeException("Could not fetch frame data!");
+        }
+        lock(backing, mode);
+        return backing;
+    }
+
+    public boolean unlock() {
+        if (mWriteLocked) {
+            mWriteLocked = false;
+        } else if (mReadLocks > 0) {
+            --mReadLocks;
+        } else {
+            return false;
+        }
+        mLockedBacking.unlock();
+        mLockedBacking = null;
+        return true;
+    }
+
+    public BackingStore retain() {
+        if (mRefCount >= 10) {
+            Log.w("BackingStore", "High ref-count of " + mRefCount + " on " + this + "!");
+        }
+        if (mRefCount <= 0) {
+            throw new RuntimeException("RETAINING RELEASED");
+        }
+        ++mRefCount;
+        return this;
+    }
+
+    public BackingStore release() {
+        if (mRefCount <= 0) {
+            throw new RuntimeException("DOUBLE-RELEASE");
+        }
+        --mRefCount;
+        if (mRefCount == 0) {
+            releaseBackings();
+            return null;
+        }
+        return this;
+    }
+
+    /**
+     * Resizes the backing store. This invalidates all data in the store.
+     */
+    public void resize(int[] newDimensions) {
+        Vector<Backing> resized = new Vector<Backing>();
+        for (Backing backing : mBackings) {
+            if (backing.resize(newDimensions)) {
+                resized.add(backing);
+            } else {
+                releaseBacking(backing);
+            }
+        }
+        mBackings = resized;
+        mDimensions = newDimensions;
+    }
+
+    public int[] getDimensions() {
+        return mDimensions;
+    }
+
+    public int getElementCount() {
+        int result = 1;
+        if (mDimensions != null) {
+            for (int dim : mDimensions) {
+                result *= dim;
+            }
+        }
+        return result;
+    }
+
+    public void importStore(BackingStore store) {
+        // TODO: Better backing selection?
+        if (store.mBackings.size() > 0) {
+            importBacking(store.mBackings.firstElement());
+        }
+        mTimestamp = store.mTimestamp;
+    }
+
+    /**
+     * @return the timestamp
+     */
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
+    /**
+     * @param timestamp the timestamp to set
+     */
+    public void setTimestamp(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
+    // Internal Methods ////////////////////////////////////////////////////////////////////////////
+    private Backing fetchBacking(int mode, int access) {
+        Backing backing = getBacking(mode, access);
+        if (backing == null) {
+            backing = attachNewBacking(mode, access);
+        }
+        syncBacking(backing);
+        return backing;
+    }
+
+    private void syncBacking(Backing backing) {
+        if (backing != null && backing.isDirty() && mCurrentBacking != null) {
+            backing.syncTo(mCurrentBacking);
+        }
+    }
+
+    private Backing getBacking(int mode, int access) {
+        // [Non-iterator looping]
+        for (int i = 0; i < mBackings.size(); ++i) {
+            final Backing backing = mBackings.get(i);
+
+            int backingAccess =
+                    (mode == Frame.MODE_WRITE) ? backing.writeAccess() : backing.readAccess();
+            if ((backingAccess & access) == access) {
+                return backing;
+            }
+        }
+        return null;
+    }
+
+    private Backing attachNewBacking(int mode, int access) {
+        Backing backing = createBacking(mode, access);
+        if (mBackings.size() > 0) {
+            backing.markDirty();
+        }
+        mBackings.add(backing);
+        return backing;
+    }
+
+    private Backing createBacking(int mode, int access) {
+        // TODO: If the read/write access flags indicate, make/fetch a GraphicBuffer backing.
+        Backing backing = null;
+        int elemSize = mType.getElementSize();
+        if (shouldFetchCached(access)) {
+            backing = mFrameManager.fetchBacking(mode, access, mDimensions, elemSize);
+        }
+        if (backing == null) {
+            switch (access) {
+                case ACCESS_BYTES:
+                    backing = new ByteBufferBacking();
+                    break;
+                case ACCESS_TEXTURE:
+                case ACCESS_RENDERTARGET:
+                    backing = new TextureBacking();
+                    break;
+                case ACCESS_OBJECT:
+                    backing = new ObjectBacking();
+                    break;
+                case ACCESS_BITMAP:
+                    backing = new BitmapBacking();
+                    break;
+                case ACCESS_ALLOCATION:
+                    if (!AllocationBacking.isSupported()) {
+                        throw new RuntimeException(
+                                "Attempted to create an AllocationBacking in context that does " +
+                                "not support RenderScript!");
+                    }
+                    backing = new AllocationBacking(mFrameManager.getContext().getRenderScript());
+                    break;
+            }
+            if (backing == null) {
+                throw new RuntimeException(
+                        "Could not create backing for access type " + access + "!");
+            }
+            if (backing.requiresGpu() && !mFrameManager.getRunner().isOpenGLSupported()) {
+                throw new RuntimeException(
+                        "Cannot create backing that requires GPU in a runner that does not " +
+                        "support OpenGL!");
+            }
+            backing.setDimensions(mDimensions);
+            backing.setElementSize(elemSize);
+            backing.setElementId(mType.getElementId());
+            backing.allocate(mType);
+            mFrameManager.onBackingCreated(backing);
+        }
+        return backing;
+    }
+
+    private void importBacking(Backing backing) {
+        // TODO: This actually needs synchronization between the two BackingStore threads for the
+        // general case
+        int access = backing.requiresGpu() ? ACCESS_BYTES : backing.readAccess();
+        Backing newBacking = createBacking(Frame.MODE_READ, access);
+        newBacking.syncTo(backing);
+        mBackings.add(newBacking);
+        mCurrentBacking = newBacking;
+    }
+
+    private void releaseBackings() {
+        // [Non-iterator looping]
+        for (int i = 0; i < mBackings.size(); ++i) {
+            releaseBacking(mBackings.get(i));
+        }
+        mBackings.clear();
+        mCurrentBacking = null;
+    }
+
+    private void releaseBacking(Backing backing) {
+        mFrameManager.onBackingAvailable(backing);
+    }
+
+    private void lock(Backing backingToLock, int mode) {
+        if (mode == Frame.MODE_WRITE) {
+            // Make sure frame is not read-locked
+            if (mReadLocks > 0) {
+                throw new RuntimeException(
+                        "Attempting to write-lock the read-locked frame " + this + "!");
+            } else if (mWriteLocked) {
+                throw new RuntimeException(
+                        "Attempting to write-lock the write-locked frame " + this + "!");
+            }
+            // Mark all other backings dirty
+            // [Non-iterator looping]
+            for (int i = 0; i < mBackings.size(); ++i) {
+                final Backing backing = mBackings.get(i);
+                if (backing != backingToLock) {
+                    backing.markDirty();
+                }
+            }
+            mWriteLocked = true;
+            mCurrentBacking = backingToLock;
+        } else {
+            if (mWriteLocked) {
+                throw new RuntimeException("Attempting to read-lock locked frame " + this + "!");
+            }
+            ++mReadLocks;
+        }
+        mLockedBacking = backingToLock;
+    }
+
+    private static boolean shouldFetchCached(int access) {
+        return access != ACCESS_OBJECT;
+    }
+
+
+    // Backings ////////////////////////////////////////////////////////////////////////////////////
+    static abstract class Backing {
+        protected int[] mDimensions = null;
+        private int mElementSize;
+        private int mElementID;
+        protected boolean mIsDirty = false;
+
+        int cachePriority = 0;
+
+        public abstract void allocate(FrameType frameType);
+
+        public abstract int readAccess();
+
+        public abstract int writeAccess();
+
+        public abstract void syncTo(Backing backing);
+
+        public abstract Object lock(int accessType);
+
+        public abstract int getType();
+
+        public abstract boolean shouldCache();
+
+        public abstract boolean requiresGpu();
+
+        public abstract void destroy();
+
+        public abstract int getSize();
+
+        public void unlock() {
+            // Default implementation does nothing.
+        }
+
+        public void setData(Object data) {
+            throw new RuntimeException("Internal error: Setting data on frame backing " + this
+                    + ", which does not support setting data directly!");
+        }
+
+        public void setDimensions(int[] dimensions) {
+            mDimensions = dimensions;
+        }
+
+        public void setElementSize(int elemSize) {
+            mElementSize = elemSize;
+        }
+
+        public void setElementId(int elemId) {
+            mElementID = elemId;
+        }
+
+        public int[] getDimensions() {
+            return mDimensions;
+        }
+
+        public int getElementSize() {
+            return mElementSize;
+        }
+
+        public int getElementId() {
+            return mElementID;
+        }
+
+        public boolean resize(int[] newDimensions) {
+            return false;
+        }
+
+        public void markDirty() {
+            mIsDirty = true;
+        }
+
+        public boolean isDirty() {
+            return mIsDirty;
+        }
+
+        protected void assertImageCompatible(FrameType type) {
+            if (type.getElementId() != FrameType.ELEMENT_RGBA8888) {
+                throw new RuntimeException("Cannot allocate texture with non-RGBA data type!");
+            } else if (mDimensions == null || mDimensions.length != 2) {
+                throw new RuntimeException("Cannot allocate non 2-dimensional texture!");
+            }
+        }
+
+    }
+
+    static class ObjectBacking extends Backing {
+
+        private Object mObject = null;
+
+        @Override
+        public void allocate(FrameType frameType) {
+            mObject = null;
+        }
+
+        @Override
+        public int readAccess() {
+            return ACCESS_OBJECT;
+        }
+
+        @Override
+        public int writeAccess() {
+            return ACCESS_OBJECT;
+        }
+
+        @Override
+        public void syncTo(Backing backing) {
+            switch (backing.getType()) {
+                case BACKING_OBJECT:
+                    mObject = backing.lock(ACCESS_OBJECT);
+                    backing.unlock();
+                    break;
+                case BACKING_BITMAP:
+                    mObject = backing.lock(ACCESS_BITMAP);
+                    backing.unlock();
+                    break;
+                default:
+                    mObject = null;
+            }
+            mIsDirty = false;
+        }
+
+        @Override
+        public Object lock(int accessType) {
+            return mObject;
+        }
+
+        @Override
+        public int getType() {
+            return BACKING_OBJECT;
+        }
+
+        @Override
+        public boolean shouldCache() {
+            return false;
+        }
+
+        @Override
+        public boolean requiresGpu() {
+            return false;
+        }
+
+        @Override
+        public void destroy() {
+            mObject = null;
+        }
+
+        @Override
+        public int getSize() {
+            return 0;
+        }
+
+        @Override
+        public void setData(Object data) {
+            mObject = data;
+        }
+
+    }
+
+    static class BitmapBacking extends Backing {
+
+        private Bitmap mBitmap = null;
+
+        @Override
+        public void allocate(FrameType frameType) {
+            assertImageCompatible(frameType);
+        }
+
+        @Override
+        public int readAccess() {
+            return ACCESS_BITMAP;
+        }
+
+        @Override
+        public int writeAccess() {
+            return ACCESS_BITMAP;
+        }
+
+        @Override
+        public void syncTo(Backing backing) {
+            int access = backing.readAccess();
+            if ((access & ACCESS_BITMAP) != 0) {
+                mBitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
+            } else if ((access & ACCESS_BYTES) != 0) {
+                createBitmap();
+                ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
+                mBitmap.copyPixelsFromBuffer(buffer);
+                buffer.rewind();
+            } else if ((access & ACCESS_TEXTURE) != 0) {
+                createBitmap();
+                RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
+                mBitmap.copyPixelsFromBuffer(
+                        renderTarget.getPixelData(mDimensions[0], mDimensions[1]));
+            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
+                createBitmap();
+                syncToAllocationBacking(backing);
+            } else {
+                throw new RuntimeException("Cannot sync bytebuffer backing!");
+            }
+            backing.unlock();
+            mIsDirty = false;
+        }
+
+        @TargetApi(11)
+        private void syncToAllocationBacking(Backing backing) {
+            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
+            allocation.copyTo(mBitmap);
+        }
+
+        @Override
+        public Object lock(int accessType) {
+            return mBitmap;
+        }
+
+        @Override
+        public int getType() {
+            return BACKING_BITMAP;
+        }
+
+        @Override
+        public boolean shouldCache() {
+            return false;
+        }
+
+        @Override
+        public boolean requiresGpu() {
+            return false;
+        }
+
+        @Override
+        public void destroy() {
+            // As we share the bitmap with other backings (such as object backings), we must not
+            // recycle it here.
+            mBitmap = null;
+        }
+
+        @Override
+        public int getSize() {
+            return 4 * mDimensions[0] * mDimensions[1];
+        }
+
+        @Override
+        public void setData(Object data) {
+            // We can assume that data will always be a Bitmap instance.
+            mBitmap = (Bitmap) data;
+        }
+
+        private void createBitmap() {
+            mBitmap = Bitmap.createBitmap(mDimensions[0], mDimensions[1], Bitmap.Config.ARGB_8888);
+        }
+    }
+
+    static class TextureBacking extends Backing {
+
+        private RenderTarget mRenderTarget = null;
+        private TextureSource mTexture = null;
+
+        @Override
+        public void allocate(FrameType frameType) {
+            assertImageCompatible(frameType);
+            mTexture = TextureSource.newTexture();
+        }
+
+        @Override
+        public int readAccess() {
+            return ACCESS_TEXTURE;
+        }
+
+        @Override
+        public int writeAccess() {
+            return ACCESS_RENDERTARGET;
+        }
+
+        @Override
+        public void syncTo(Backing backing) {
+            int access = backing.readAccess();
+            if ((access & ACCESS_BYTES) != 0) {
+                ByteBuffer pixels = (ByteBuffer) backing.lock(ACCESS_BYTES);
+                mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
+            } else if ((access & ACCESS_BITMAP) != 0) {
+                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
+                mTexture.allocateWithBitmapPixels(bitmap);
+            } else if ((access & ACCESS_TEXTURE) != 0) {
+                TextureSource texture = (TextureSource) backing.lock(ACCESS_TEXTURE);
+                int w = mDimensions[0];
+                int h = mDimensions[1];
+                ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h);
+            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
+                syncToAllocationBacking(backing);
+            } else {
+                throw new RuntimeException("Cannot sync bytebuffer backing!");
+            }
+            backing.unlock();
+            mIsDirty = false;
+        }
+
+        @TargetApi(11)
+        private void syncToAllocationBacking(Backing backing) {
+            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
+            ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
+            allocation.copyTo(pixels.array());
+            mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
+        }
+
+        @Override
+        public Object lock(int accessType) {
+            switch (accessType) {
+                case ACCESS_TEXTURE:
+                    return getTexture();
+
+                case ACCESS_RENDERTARGET:
+                    return getRenderTarget();
+
+                default:
+                    throw new RuntimeException("Illegal access to texture!");
+            }
+        }
+
+        @Override
+        public int getType() {
+            return BACKING_TEXTURE;
+        }
+
+        @Override
+        public boolean shouldCache() {
+            return true;
+        }
+
+        @Override
+        public boolean requiresGpu() {
+            return true;
+        }
+
+        @Override
+        public void destroy() {
+            if (mRenderTarget != null) {
+                mRenderTarget.release();
+            }
+            if (mTexture.isAllocated()) {
+                mTexture.release();
+            }
+        }
+
+        @Override
+        public int getSize() {
+            return 4 * mDimensions[0] * mDimensions[1];
+        }
+
+        private TextureSource getTexture() {
+            if (!mTexture.isAllocated()) {
+                mTexture.allocate(mDimensions[0], mDimensions[1]);
+            }
+            return mTexture;
+        }
+
+        private RenderTarget getRenderTarget() {
+            if (mRenderTarget == null) {
+                int w = mDimensions[0];
+                int h = mDimensions[1];
+                mRenderTarget = RenderTarget.currentTarget().forTexture(getTexture(), w, h);
+            }
+            return mRenderTarget;
+        }
+
+    }
+
+    static class ByteBufferBacking extends Backing {
+
+        ByteBuffer mBuffer = null;
+
+        @Override
+        public void allocate(FrameType frameType) {
+            int size = frameType.getElementSize();
+            for (int dim : mDimensions) {
+                size *= dim;
+            }
+            mBuffer = ByteBuffer.allocateDirect(size);
+        }
+
+        @Override
+        public int readAccess() {
+            return ACCESS_BYTES;
+        }
+
+        @Override
+        public int writeAccess() {
+            return ACCESS_BYTES;
+        }
+
+        @Override
+        public boolean requiresGpu() {
+            return false;
+        }
+
+        @Override
+        public void syncTo(Backing backing) {
+            int access = backing.readAccess();
+            if ((access & ACCESS_TEXTURE) != 0) {
+                RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
+                GLToolbox.readTarget(target, mBuffer, mDimensions[0], mDimensions[1]);
+            } else if ((access & ACCESS_BITMAP) != 0) {
+                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
+                bitmap.copyPixelsToBuffer(mBuffer);
+                mBuffer.rewind();
+            } else if ((access & ACCESS_BYTES) != 0) {
+                ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
+                mBuffer.put(otherBuffer);
+                otherBuffer.rewind();
+            } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
+                syncToAllocationBacking(backing);
+            } else {
+                throw new RuntimeException("Cannot sync bytebuffer backing!");
+            }
+            backing.unlock();
+            mBuffer.rewind();
+            mIsDirty = false;
+        }
+
+        @TargetApi(11)
+        private void syncToAllocationBacking(Backing backing) {
+            Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
+            if (getElementId() == FrameType.ELEMENT_RGBA8888) {
+                byte[] bytes = mBuffer.array();
+                allocation.copyTo(bytes);
+            } else if (getElementId() == FrameType.ELEMENT_FLOAT32) {
+                float[] floats = new float[getSize() / 4];
+                allocation.copyTo(floats);
+                mBuffer.asFloatBuffer().put(floats);
+            } else {
+                throw new RuntimeException(
+                        "Trying to sync to an allocation with an unsupported element id: "
+                        + getElementId());
+            }
+        }
+
+        @Override
+        public Object lock(int accessType) {
+            return mBuffer.rewind();
+        }
+
+        @Override
+        public void unlock() {
+            mBuffer.rewind();
+        }
+
+        @Override
+        public int getType() {
+            return BACKING_BYTEBUFFER;
+        }
+
+        @Override
+        public boolean shouldCache() {
+            return true;
+        }
+
+        @Override
+        public void destroy() {
+            mBuffer = null;
+        }
+
+        @Override
+        public int getSize() {
+            return mBuffer.remaining();
+        }
+
+    }
+
+    @TargetApi(11)
+    static class AllocationBacking extends Backing {
+
+        private final RenderScript mRenderScript;
+        private Allocation mAllocation = null;
+
+        public AllocationBacking(RenderScript renderScript) {
+            mRenderScript = renderScript;
+        }
+
+        @Override
+        public void allocate(FrameType frameType) {
+            assertCompatible(frameType);
+
+            Element element = null;
+            switch (frameType.getElementId()) {
+                case FrameType.ELEMENT_RGBA8888:
+                    element = Element.RGBA_8888(mRenderScript);
+                    break;
+                case FrameType.ELEMENT_FLOAT32:
+                    element = Element.F32(mRenderScript);
+                    break;
+            }
+            Type.Builder imageTypeBuilder = new Type.Builder(mRenderScript, element);
+            imageTypeBuilder.setX(mDimensions.length >= 1 ? mDimensions[0] : 1);
+            imageTypeBuilder.setY(mDimensions.length == 2 ? mDimensions[1] : 1);
+            Type imageType = imageTypeBuilder.create();
+
+            mAllocation = Allocation.createTyped(mRenderScript, imageType);
+        }
+
+        @Override
+        public int readAccess() {
+            return ACCESS_ALLOCATION;
+        }
+
+        @Override
+        public int writeAccess() {
+            return ACCESS_ALLOCATION;
+        }
+
+        @Override
+        public boolean requiresGpu() {
+            return false;
+        }
+
+        @Override
+        public void syncTo(Backing backing) {
+            int access = backing.readAccess();
+            if ((access & ACCESS_TEXTURE) != 0) {
+                RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
+                ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
+                GLToolbox.readTarget(target, pixels, mDimensions[0], mDimensions[1]);
+                mAllocation.copyFrom(pixels.array());
+            } else if ((access & ACCESS_BITMAP) != 0) {
+                Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
+                mAllocation.copyFrom(bitmap);
+            } else if ((access & ACCESS_BYTES) != 0) {
+                ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
+                if (buffer.order() != ByteOrder.nativeOrder()) {
+                    throw new RuntimeException(
+                            "Trying to sync to the ByteBufferBacking with non-native byte order!");
+                }
+                byte[] bytes;
+                if (buffer.hasArray()) {
+                    bytes = buffer.array();
+                } else {
+                    bytes = new byte[getSize()];
+                    buffer.get(bytes);
+                    buffer.rewind();
+                }
+                mAllocation.copyFromUnchecked(bytes);
+            } else {
+                throw new RuntimeException("Cannot sync allocation backing!");
+            }
+            backing.unlock();
+            mIsDirty = false;
+        }
+
+        @Override
+        public Object lock(int accessType) {
+            return mAllocation;
+        }
+
+        @Override
+        public void unlock() {
+        }
+
+        @Override
+        public int getType() {
+            return BACKING_ALLOCATION;
+        }
+
+        @Override
+        public boolean shouldCache() {
+            return true;
+        }
+
+        @Override
+        public void destroy() {
+            if (mAllocation != null) {
+                mAllocation.destroy();
+                mAllocation = null;
+            }
+        }
+
+        @Override
+        public int getSize() {
+            int elementCount = 1;
+            for (int dim : mDimensions) {
+                elementCount *= dim;
+            }
+            return getElementSize() * elementCount;
+        }
+
+        public static boolean isSupported() {
+            return Build.VERSION.SDK_INT >= 11;
+        }
+
+        private void assertCompatible(FrameType type) {
+            // TODO: consider adding support for other data types.
+            if (type.getElementId() != FrameType.ELEMENT_RGBA8888
+                    && type.getElementId() != FrameType.ELEMENT_FLOAT32) {
+                throw new RuntimeException(
+                        "Cannot allocate allocation with a non-RGBA or non-float data type!");
+            }
+            if (mDimensions == null || mDimensions.length > 2) {
+                throw new RuntimeException(
+                        "Cannot create an allocation with more than 2 dimensions!");
+            }
+        }
+
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BranchFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BranchFilter.java
new file mode 100644
index 0000000..6e7c014
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BranchFilter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public final class BranchFilter extends Filter {
+
+    private boolean mSynchronized = true;
+
+    public BranchFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    public BranchFilter(MffContext context, String name, boolean synced) {
+        super(context, name);
+        mSynchronized = synced;
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("input", Signature.PORT_REQUIRED, FrameType.any())
+            .addInputPort("synchronized", Signature.PORT_OPTIONAL,FrameType.single(boolean.class))
+            .disallowOtherInputs();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("input")) {
+            for (OutputPort outputPort : getConnectedOutputPorts()) {
+                port.attachToOutputPort(outputPort);
+            }
+        } else if (port.getName().equals("synchronized")) {
+            port.bindToFieldNamed("mSynchronized");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onOpen() {
+        updateSynchronization();
+    }
+
+    @Override
+    protected void onProcess() {
+        Frame inputFrame = getConnectedInputPort("input").pullFrame();
+        for (OutputPort outputPort : getConnectedOutputPorts()) {
+            if (outputPort.isAvailable()) {
+                outputPort.pushFrame(inputFrame);
+            }
+        }
+    }
+
+    private void updateSynchronization() {
+        if (mSynchronized) {
+            for (OutputPort port : getConnectedOutputPorts()) {
+                port.setWaitsUntilAvailable(true);
+            }
+        } else {
+            for (OutputPort port : getConnectedOutputPorts()) {
+                port.setWaitsUntilAvailable(false);
+            }
+        }
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BrightnessFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BrightnessFilter.java
new file mode 100644
index 0000000..5a70776
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BrightnessFilter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.image;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public class BrightnessFilter extends Filter {
+
+    private float mBrightness = 1.0f;
+    private ImageShader mShader;
+
+    private static final String mBrightnessShader =
+            "precision mediump float;\n" +
+            "uniform sampler2D tex_sampler_0;\n" +
+            "uniform float brightness;\n" +
+            "varying vec2 v_texcoord;\n" +
+            "void main() {\n" +
+            "  vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+            "  if (brightness < 0.5) {\n" +
+            "    gl_FragColor = color * (2.0 * brightness);\n" +
+            "  } else {\n" +
+            "    vec4 diff = 1.0 - color;\n" +
+            "    gl_FragColor = color + diff * (2.0 * (brightness - 0.5));\n" +
+            "  }\n" +
+            "}\n";
+
+
+    public BrightnessFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("brightness", Signature.PORT_OPTIONAL, FrameType.single(float.class))
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("brightness")) {
+            port.bindToFieldNamed("mBrightness");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onPrepare() {
+        mShader = new ImageShader(mBrightnessShader);
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("image");
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        int[] dim = inputImage.getDimensions();
+        FrameImage2D outputImage = outPort.fetchAvailableFrame(dim).asFrameImage2D();
+        mShader.setUniformValue("brightness", mBrightness);
+        mShader.process(inputImage, outputImage);
+        outPort.pushFrame(outputImage);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CameraStreamer.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CameraStreamer.java
new file mode 100644
index 0000000..d1642fd
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CameraStreamer.java
@@ -0,0 +1,1906 @@
+/*
+ * 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 androidx.media.filterfw;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.PreviewCallback;
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder;
+import android.opengl.GLES20;
+import android.os.Build.VERSION;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceView;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.microedition.khronos.egl.EGLContext;
+
+/**
+ * The CameraStreamer streams Frames from a camera to connected clients.
+ *
+ * There is one centralized CameraStreamer object per MffContext, and only one stream can be
+ * active at any time. The CameraStreamer acts as a Camera "server" that streams frames to any
+ * number of connected clients. Typically, these are CameraSource filters that are part of a
+ * graph, but other clients can be written as well.
+ */
+public class CameraStreamer {
+
+    /** Camera Facing: Don't Care: Picks any available camera. */
+    public static final int FACING_DONTCARE = 0;
+    /** Camera Facing: Front: Use the front facing camera. */
+    public static final int FACING_FRONT = 1;
+    /** Camera Facing: Back: Use the rear facing camera. */
+    public static final int FACING_BACK = 2;
+
+    /** How long the streamer should wait to acquire the camera before giving up. */
+    public static long MAX_CAMERA_WAIT_TIME = 5;
+
+    /**
+     * The global camera lock, that is closed when the camera is acquired by any CameraStreamer,
+     * and opened when a streamer is done using the camera.
+     */
+    static ReentrantLock mCameraLock = new ReentrantLock();
+
+    /** The Camera thread that grabs frames from the camera */
+    private CameraRunnable mCameraRunner = null;
+
+    private abstract class CamFrameHandler {
+        protected int mCameraWidth;
+        protected int mCameraHeight;
+        protected int mOutWidth;
+        protected int mOutHeight;
+        protected CameraRunnable mRunner;
+
+        /** Map of GLSL shaders (one for each target context) */
+        protected HashMap<EGLContext, ImageShader> mTargetShaders
+            = new HashMap<EGLContext, ImageShader>();
+
+        /** Map of target textures (one for each target context) */
+        protected HashMap<EGLContext, TextureSource> mTargetTextures
+            = new HashMap<EGLContext, TextureSource>();
+
+        /** Map of set of clients (one for each target context) */
+        protected HashMap<EGLContext, Set<FrameClient>> mContextClients
+            = new HashMap<EGLContext, Set<FrameClient>>();
+
+        /** List of clients that are consuming camera frames. */
+        protected Vector<FrameClient> mClients = new Vector<FrameClient>();
+
+        public void initWithRunner(CameraRunnable camRunner) {
+            mRunner = camRunner;
+        }
+
+        public void setCameraSize(int width, int height) {
+            mCameraWidth = width;
+            mCameraHeight = height;
+        }
+
+        public void registerClient(FrameClient client) {
+            EGLContext context = RenderTarget.currentContext();
+            Set<FrameClient> clientTargets = clientsForContext(context);
+            clientTargets.add(client);
+            mClients.add(client);
+            onRegisterClient(client, context);
+        }
+
+        public void unregisterClient(FrameClient client) {
+            EGLContext context = RenderTarget.currentContext();
+            Set<FrameClient> clientTargets = clientsForContext(context);
+            clientTargets.remove(client);
+            if (clientTargets.isEmpty()) {
+                onCleanupContext(context);
+            }
+            mClients.remove(client);
+        }
+
+        public abstract void setupServerFrame();
+        public abstract void updateServerFrame();
+        public abstract void grabFrame(FrameImage2D targetFrame);
+        public abstract void release();
+
+        public void onUpdateCameraOrientation(int orientation) {
+            if (orientation % 180 != 0) {
+                mOutWidth = mCameraHeight;
+                mOutHeight = mCameraWidth;
+            } else {
+                mOutWidth = mCameraWidth;
+                mOutHeight = mCameraHeight;
+            }
+        }
+
+        protected Set<FrameClient> clientsForContext(EGLContext context) {
+            Set<FrameClient> clients = mContextClients.get(context);
+            if (clients == null) {
+                clients = new HashSet<FrameClient>();
+                mContextClients.put(context, clients);
+            }
+            return clients;
+        }
+
+        protected void onRegisterClient(FrameClient client, EGLContext context) {
+        }
+
+        protected void onCleanupContext(EGLContext context) {
+            TextureSource texture = mTargetTextures.get(context);
+            ImageShader shader = mTargetShaders.get(context);
+            if (texture != null) {
+                texture.release();
+                mTargetTextures.remove(context);
+            }
+            if (shader != null) {
+                mTargetShaders.remove(context);
+            }
+        }
+
+        protected TextureSource textureForContext(EGLContext context) {
+            TextureSource texture = mTargetTextures.get(context);
+            if (texture == null) {
+                texture = createClientTexture();
+                mTargetTextures.put(context, texture);
+            }
+            return texture;
+        }
+
+        protected ImageShader shaderForContext(EGLContext context) {
+            ImageShader shader = mTargetShaders.get(context);
+            if (shader == null) {
+                shader = createClientShader();
+                mTargetShaders.put(context, shader);
+            }
+            return shader;
+        }
+
+        protected ImageShader createClientShader() {
+            return null;
+        }
+
+        protected TextureSource createClientTexture() {
+            return null;
+        }
+
+        public boolean isFrontMirrored() {
+            return true;
+        }
+    }
+
+    // Jellybean (and later) back-end
+    @TargetApi(16)
+    private class CamFrameHandlerJB extends CamFrameHandlerICS {
+
+        @Override
+        public void setupServerFrame() {
+            setupPreviewTexture(mRunner.mCamera);
+        }
+
+        @Override
+        public synchronized void updateServerFrame() {
+            updateSurfaceTexture();
+            informClients();
+        }
+
+        @Override
+        public synchronized void grabFrame(FrameImage2D targetFrame) {
+            TextureSource targetTex = TextureSource.newExternalTexture();
+            ImageShader copyShader = shaderForContext(RenderTarget.currentContext());
+            if (targetTex == null || copyShader == null) {
+                throw new RuntimeException("Attempting to grab camera frame from unknown "
+                    + "thread: " + Thread.currentThread() + "!");
+            }
+            mPreviewSurfaceTexture.attachToGLContext(targetTex.getTextureId());
+            updateTransform(copyShader);
+            updateShaderTargetRect(copyShader);
+            targetFrame.resize(new int[] { mOutWidth, mOutHeight });
+            copyShader.process(targetTex,
+                               targetFrame.lockRenderTarget(),
+                               mOutWidth,
+                               mOutHeight);
+            targetFrame.setTimestamp(mPreviewSurfaceTexture.getTimestamp());
+            targetFrame.unlock();
+            mPreviewSurfaceTexture.detachFromGLContext();
+            targetTex.release();
+        }
+
+        @Override
+        protected void updateShaderTargetRect(ImageShader shader) {
+            if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
+                shader.setTargetRect(1f, 1f, -1f, -1f);
+            } else {
+                shader.setTargetRect(0f, 1f, 1f, -1f);
+            }
+        }
+
+        @Override
+        protected void setupPreviewTexture(Camera camera) {
+            super.setupPreviewTexture(camera);
+            mPreviewSurfaceTexture.detachFromGLContext();
+        }
+
+        protected void updateSurfaceTexture() {
+            mPreviewSurfaceTexture.attachToGLContext(mPreviewTexture.getTextureId());
+            mPreviewSurfaceTexture.updateTexImage();
+            mPreviewSurfaceTexture.detachFromGLContext();
+        }
+
+        protected void informClients() {
+            synchronized (mClients) {
+                for (FrameClient client : mClients) {
+                    client.onCameraFrameAvailable();
+                }
+            }
+        }
+    }
+
+    // ICS (and later) back-end
+    @TargetApi(15)
+    private class CamFrameHandlerICS extends CamFrameHandler  {
+
+        protected static final String mCopyShaderSource =
+            "#extension GL_OES_EGL_image_external : require\n" +
+            "precision mediump float;\n" +
+            "uniform samplerExternalOES tex_sampler_0;\n" +
+            "varying vec2 v_texcoord;\n" +
+            "void main() {\n" +
+            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+            "}\n";
+
+        /** The camera transform matrix */
+        private float[] mCameraTransform = new float[16];
+
+        /** The texture the camera streams to */
+        protected TextureSource mPreviewTexture = null;
+        protected SurfaceTexture mPreviewSurfaceTexture = null;
+
+        /** Map of target surface textures (one for each target context) */
+        protected HashMap<EGLContext, SurfaceTexture> mTargetSurfaceTextures
+            = new HashMap<EGLContext, SurfaceTexture>();
+
+        /** Map of RenderTargets for client SurfaceTextures */
+        protected HashMap<SurfaceTexture, RenderTarget> mClientRenderTargets
+            = new HashMap<SurfaceTexture, RenderTarget>();
+
+        /** Server side copy shader */
+        protected ImageShader mCopyShader = null;
+
+        @Override
+        public synchronized void setupServerFrame() {
+            setupPreviewTexture(mRunner.mCamera);
+        }
+
+        @Override
+        public synchronized void updateServerFrame() {
+            mPreviewSurfaceTexture.updateTexImage();
+            distributeFrames();
+        }
+
+        @Override
+        public void onUpdateCameraOrientation(int orientation) {
+            super.onUpdateCameraOrientation(orientation);
+            mRunner.mCamera.setDisplayOrientation(orientation);
+            updateSurfaceTextureSizes();
+        }
+
+        @Override
+        public synchronized void onRegisterClient(FrameClient client, EGLContext context) {
+            final Set<FrameClient> clientTargets = clientsForContext(context);
+
+            // Make sure we have texture, shader, and surfacetexture setup for this context.
+            TextureSource clientTex = textureForContext(context);
+            ImageShader copyShader = shaderForContext(context);
+            SurfaceTexture surfTex = surfaceTextureForContext(context);
+
+            // Listen to client-side surface texture updates
+            surfTex.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
+                @Override
+                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+                    for (FrameClient clientTarget : clientTargets) {
+                        clientTarget.onCameraFrameAvailable();
+                    }
+                }
+            });
+        }
+
+        @Override
+        public synchronized void grabFrame(FrameImage2D targetFrame) {
+            // Get the GL objects for the receiver's context
+            EGLContext clientContext = RenderTarget.currentContext();
+            TextureSource clientTex = textureForContext(clientContext);
+            ImageShader copyShader = shaderForContext(clientContext);
+            SurfaceTexture surfTex = surfaceTextureForContext(clientContext);
+            if (clientTex == null || copyShader == null || surfTex == null) {
+                throw new RuntimeException("Attempting to grab camera frame from unknown "
+                    + "thread: " + Thread.currentThread() + "!");
+            }
+
+            // Copy from client ST to client tex
+            surfTex.updateTexImage();
+            targetFrame.resize(new int[] { mOutWidth, mOutHeight });
+            copyShader.process(clientTex,
+                               targetFrame.lockRenderTarget(),
+                               mOutWidth,
+                               mOutHeight);
+
+            targetFrame.setTimestamp(mPreviewSurfaceTexture.getTimestamp());
+            targetFrame.unlock();
+        }
+
+        @Override
+        public synchronized void release() {
+            if (mPreviewTexture != null) {
+                mPreviewTexture.release();
+                mPreviewTexture = null;
+            }
+            if (mPreviewSurfaceTexture != null) {
+                mPreviewSurfaceTexture.release();
+                mPreviewSurfaceTexture = null;
+            }
+        }
+
+        @Override
+        protected ImageShader createClientShader() {
+            return new ImageShader(mCopyShaderSource);
+        }
+
+        @Override
+        protected TextureSource createClientTexture() {
+            return TextureSource.newExternalTexture();
+        }
+
+        protected void distributeFrames() {
+            updateTransform(getCopyShader());
+            updateShaderTargetRect(getCopyShader());
+
+            for (SurfaceTexture clientTexture : mTargetSurfaceTextures.values()) {
+                RenderTarget clientTarget = renderTargetFor(clientTexture);
+                clientTarget.focus();
+                getCopyShader().process(mPreviewTexture,
+                                        clientTarget,
+                                        mOutWidth,
+                                        mOutHeight);
+                GLToolbox.checkGlError("distribute frames");
+                clientTarget.swapBuffers();
+            }
+        }
+
+        protected RenderTarget renderTargetFor(SurfaceTexture surfaceTex) {
+            RenderTarget target = mClientRenderTargets.get(surfaceTex);
+            if (target == null) {
+                target = RenderTarget.currentTarget().forSurfaceTexture(surfaceTex);
+                mClientRenderTargets.put(surfaceTex, target);
+            }
+            return target;
+        }
+
+        protected void setupPreviewTexture(Camera camera) {
+            if (mPreviewTexture == null) {
+                mPreviewTexture = TextureSource.newExternalTexture();
+            }
+            if (mPreviewSurfaceTexture == null) {
+                mPreviewSurfaceTexture = new SurfaceTexture(mPreviewTexture.getTextureId());
+                try {
+                    camera.setPreviewTexture(mPreviewSurfaceTexture);
+                } catch (IOException e) {
+                    throw new RuntimeException("Could not bind camera surface texture: " +
+                                               e.getMessage() + "!");
+                }
+                mPreviewSurfaceTexture.setOnFrameAvailableListener(mOnCameraFrameListener);
+            }
+        }
+
+        protected ImageShader getCopyShader() {
+            if (mCopyShader == null) {
+                mCopyShader = new ImageShader(mCopyShaderSource);
+            }
+            return mCopyShader;
+        }
+
+        protected SurfaceTexture surfaceTextureForContext(EGLContext context) {
+            SurfaceTexture surfTex = mTargetSurfaceTextures.get(context);
+            if (surfTex == null) {
+                TextureSource texture = textureForContext(context);
+                if (texture != null) {
+                    surfTex = new SurfaceTexture(texture.getTextureId());
+                    surfTex.setDefaultBufferSize(mOutWidth, mOutHeight);
+                    mTargetSurfaceTextures.put(context, surfTex);
+                }
+            }
+            return surfTex;
+        }
+
+        protected void updateShaderTargetRect(ImageShader shader) {
+            if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
+                shader.setTargetRect(1f, 0f, -1f, 1f);
+            } else {
+                shader.setTargetRect(0f, 0f, 1f, 1f);
+            }
+        }
+
+        protected synchronized void updateSurfaceTextureSizes() {
+            for (SurfaceTexture clientTexture : mTargetSurfaceTextures.values()) {
+                clientTexture.setDefaultBufferSize(mOutWidth, mOutHeight);
+            }
+        }
+
+        protected void updateTransform(ImageShader shader) {
+            mPreviewSurfaceTexture.getTransformMatrix(mCameraTransform);
+            shader.setSourceTransform(mCameraTransform);
+        }
+
+        @Override
+        protected void onCleanupContext(EGLContext context) {
+            super.onCleanupContext(context);
+            SurfaceTexture surfaceTex = mTargetSurfaceTextures.get(context);
+            if (surfaceTex != null) {
+                surfaceTex.release();
+                mTargetSurfaceTextures.remove(context);
+            }
+        }
+
+        protected SurfaceTexture.OnFrameAvailableListener mOnCameraFrameListener =
+                new SurfaceTexture.OnFrameAvailableListener() {
+            @Override
+            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+                mRunner.signalNewFrame();
+            }
+        };
+    }
+
+    // Gingerbread (and later) back-end
+    @TargetApi(9)
+    private final class CamFrameHandlerGB extends CamFrameHandler  {
+
+        private SurfaceView mSurfaceView;
+        private byte[] mFrameBufferFront;
+        private byte[] mFrameBufferBack;
+        private boolean mWriteToBack = true;
+        private float[] mTargetCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
+        final Object mBufferLock = new Object();
+
+        private String mNV21ToRGBAFragment =
+            "precision mediump float;\n" +
+            "\n" +
+            "uniform sampler2D tex_sampler_0;\n" +
+            "varying vec2 v_y_texcoord;\n" +
+            "varying vec2 v_vu_texcoord;\n" +
+            "varying vec2 v_pixcoord;\n" +
+            "\n" +
+            "vec3 select(vec4 yyyy, vec4 vuvu, int s) {\n" +
+            "  if (s == 0) {\n" +
+            "    return vec3(yyyy.r, vuvu.g, vuvu.r);\n" +
+            "  } else if (s == 1) {\n" +
+            "    return vec3(yyyy.g, vuvu.g, vuvu.r);\n" +
+            " } else if (s == 2) {\n" +
+            "    return vec3(yyyy.b, vuvu.a, vuvu.b);\n" +
+            "  } else  {\n" +
+            "    return vec3(yyyy.a, vuvu.a, vuvu.b);\n" +
+            "  }\n" +
+            "}\n" +
+            "\n" +
+            "vec3 yuv2rgb(vec3 yuv) {\n" +
+            "  mat4 conversion = mat4(1.0,  0.0,    1.402, -0.701,\n" +
+            "                         1.0, -0.344, -0.714,  0.529,\n" +
+            "                         1.0,  1.772,  0.0,   -0.886,\n" +
+            "                         0, 0, 0, 0);" +
+            "  return (vec4(yuv, 1.0) * conversion).rgb;\n" +
+            "}\n" +
+            "\n" +
+            "void main() {\n" +
+            "  vec4 yyyy = texture2D(tex_sampler_0, v_y_texcoord);\n" +
+            "  vec4 vuvu = texture2D(tex_sampler_0, v_vu_texcoord);\n" +
+            "  int s = int(mod(floor(v_pixcoord.x), 4.0));\n" +
+            "  vec3 yuv = select(yyyy, vuvu, s);\n" +
+            "  vec3 rgb = yuv2rgb(yuv);\n" +
+            "  gl_FragColor = vec4(rgb, 1.0);\n" +
+            "}";
+
+        private String mNV21ToRGBAVertex =
+            "attribute vec4 a_position;\n" +
+            "attribute vec2 a_y_texcoord;\n" +
+            "attribute vec2 a_vu_texcoord;\n" +
+            "attribute vec2 a_pixcoord;\n" +
+            "varying vec2 v_y_texcoord;\n" +
+            "varying vec2 v_vu_texcoord;\n" +
+            "varying vec2 v_pixcoord;\n" +
+            "void main() {\n" +
+            "  gl_Position = a_position;\n" +
+            "  v_y_texcoord = a_y_texcoord;\n" +
+            "  v_vu_texcoord = a_vu_texcoord;\n" +
+            "  v_pixcoord = a_pixcoord;\n" +
+            "}\n";
+
+        private byte[] readBuffer() {
+            synchronized (mBufferLock) {
+                return mWriteToBack ? mFrameBufferFront : mFrameBufferBack;
+            }
+        }
+
+        private byte[] writeBuffer() {
+            synchronized (mBufferLock) {
+                return mWriteToBack ? mFrameBufferBack : mFrameBufferFront;
+            }
+        }
+
+        private synchronized void swapBuffers() {
+            synchronized (mBufferLock) {
+                mWriteToBack = !mWriteToBack;
+            }
+        }
+
+        private PreviewCallback mPreviewCallback = new PreviewCallback() {
+
+            @Override
+            public void onPreviewFrame(byte[] data, Camera camera) {
+                swapBuffers();
+                camera.addCallbackBuffer(writeBuffer());
+                mRunner.signalNewFrame();
+            }
+
+        };
+
+        @Override
+        public void setupServerFrame() {
+            checkCameraDimensions();
+            Camera camera = mRunner.mCamera;
+            int bufferSize = mCameraWidth * (mCameraHeight + mCameraHeight/2);
+            mFrameBufferFront = new byte[bufferSize];
+            mFrameBufferBack = new byte[bufferSize];
+            camera.addCallbackBuffer(writeBuffer());
+            camera.setPreviewCallbackWithBuffer(mPreviewCallback);
+            SurfaceView previewDisplay = getPreviewDisplay();
+            if (previewDisplay != null) {
+                try {
+                    camera.setPreviewDisplay(previewDisplay.getHolder());
+                } catch (IOException e) {
+                    throw new RuntimeException("Could not start camera with given preview " +
+                            "display!");
+                }
+            }
+        }
+
+        private void checkCameraDimensions() {
+            if (mCameraWidth % 4 != 0) {
+                throw new RuntimeException("Camera width must be a multiple of 4!");
+            } else if (mCameraHeight % 2 != 0) {
+                throw new RuntimeException("Camera height must be a multiple of 2!");
+            }
+        }
+
+        @Override
+        public void updateServerFrame() {
+            // Server frame has been updated already, simply inform clients here.
+            informClients();
+        }
+
+        @Override
+        public void grabFrame(FrameImage2D targetFrame) {
+            EGLContext clientContext = RenderTarget.currentContext();
+
+            // Copy camera data to the client YUV texture
+            TextureSource clientTex = textureForContext(clientContext);
+            int texWidth = mCameraWidth / 4;
+            int texHeight = mCameraHeight + mCameraHeight / 2;
+            synchronized(mBufferLock) {    // Don't swap buffers while we are reading
+                ByteBuffer pixels = ByteBuffer.wrap(readBuffer());
+                clientTex.allocateWithPixels(pixels, texWidth, texHeight);
+            }
+            clientTex.setParameter(GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+            clientTex.setParameter(GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+
+            // Setup the YUV-2-RGBA shader
+            ImageShader transferShader = shaderForContext(clientContext);
+            transferShader.setTargetCoords(mTargetCoords);
+            updateShaderPixelSize(transferShader);
+
+            // Convert pixels into target frame
+            targetFrame.resize(new int[] { mOutWidth, mOutHeight });
+            transferShader.process(clientTex,
+                    targetFrame.lockRenderTarget(),
+                    mOutWidth,
+                    mOutHeight);
+            targetFrame.unlock();
+        }
+
+        @Override
+        public void onUpdateCameraOrientation(int orientation) {
+            super.onUpdateCameraOrientation(orientation);
+            if ((mRunner.mActualFacing == FACING_FRONT) && mRunner.mFlipFront) {
+                switch (orientation) {
+                    case 0:
+                        mTargetCoords = new float[] { 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f };
+                        break;
+                    case 90:
+                        mTargetCoords = new float[] { 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f };
+                        break;
+                    case 180:
+                        mTargetCoords = new float[] { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f };
+                        break;
+                    case 270:
+                        mTargetCoords = new float[] { 1f, 1f, 1f, 0f, 0f, 1f, 0f, 0f };
+                        break;
+                }
+            } else {
+                switch (orientation) {
+                    case 0:
+                        mTargetCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
+                        break;
+                    case 90:
+                        mTargetCoords = new float[] { 1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f };
+                        break;
+                    case 180:
+                        mTargetCoords = new float[] { 1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f };
+                        break;
+                    case 270:
+                        mTargetCoords = new float[] { 0f, 1f, 0f, 0f, 1f, 1f, 1f, 0f };
+                        break;
+                }
+            }
+        }
+
+        @Override
+        public void release() {
+            mFrameBufferBack = null;
+            mFrameBufferFront = null;
+        }
+
+        @Override
+        public boolean isFrontMirrored() {
+            return false;
+        }
+
+        @Override
+        protected ImageShader createClientShader() {
+            ImageShader shader = new ImageShader(mNV21ToRGBAVertex, mNV21ToRGBAFragment);
+            // TODO: Make this a VBO
+            float[] yCoords = new float[] {
+                    0f, 0f,
+                    1f, 0f,
+                    0f, 2f / 3f,
+                    1f, 2f / 3f };
+            float[] uvCoords = new float[] {
+                    0f, 2f / 3f,
+                    1f, 2f / 3f,
+                    0f, 1f,
+                    1f, 1f };
+            shader.setAttributeValues("a_y_texcoord", yCoords, 2);
+            shader.setAttributeValues("a_vu_texcoord", uvCoords, 2);
+            return shader;
+        }
+
+        @Override
+        protected TextureSource createClientTexture() {
+            TextureSource texture = TextureSource.newTexture();
+            texture.setParameter(GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+            texture.setParameter(GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+            return texture;
+        }
+
+        private void updateShaderPixelSize(ImageShader shader) {
+            float[] pixCoords = new float[] {
+                    0f, 0f,
+                    mCameraWidth, 0f,
+                    0f, mCameraHeight,
+                    mCameraWidth, mCameraHeight };
+            shader.setAttributeValues("a_pixcoord", pixCoords, 2);
+        }
+
+        private SurfaceView getPreviewDisplay() {
+            if (mSurfaceView == null) {
+                mSurfaceView = mRunner.getContext().getDummySurfaceView();
+            }
+            return mSurfaceView;
+        }
+
+        private void informClients() {
+            synchronized (mClients) {
+                for (FrameClient client : mClients) {
+                    client.onCameraFrameAvailable();
+                }
+            }
+        }
+    }
+
+    private static class State {
+        public static final int STATE_RUNNING = 1;
+        public static final int STATE_STOPPED = 2;
+        public static final int STATE_HALTED = 3;
+
+        private AtomicInteger mCurrent = new AtomicInteger(STATE_STOPPED);
+
+        public int current() {
+            return mCurrent.get();
+        }
+
+        public void set(int newState) {
+            mCurrent.set(newState);
+        }
+    }
+
+    private static class Event {
+        public static final int START = 1;
+        public static final int FRAME = 2;
+        public static final int STOP = 3;
+        public static final int HALT = 4;
+        public static final int RESTART = 5;
+        public static final int UPDATE = 6;
+        public static final int TEARDOWN = 7;
+
+        public int code;
+
+        public Event(int code) {
+            this.code = code;
+        }
+    }
+
+    private final class CameraRunnable implements Runnable {
+
+        /** On slower devices the event queue can easily fill up. We bound the queue to this. */
+        private final static int MAX_EVENTS = 32;
+
+        /** The runner's state */
+        private State mState = new State();
+
+        /** The CameraRunner's event queue */
+        private LinkedBlockingQueue<Event> mEventQueue = new LinkedBlockingQueue<Event>(MAX_EVENTS);
+
+        /** The requested FPS */
+        private int mRequestedFramesPerSec = 30;
+
+        /** The actual FPS */
+        private int mActualFramesPerSec = 0;
+
+        /** The requested preview width and height */
+        private int mRequestedPreviewWidth = 640;
+        private int mRequestedPreviewHeight = 480;
+
+        /** The requested picture width and height */
+        private int mRequestedPictureWidth = 640;
+        private int mRequestedPictureHeight = 480;
+
+        /** The actual camera width and height */
+        private int[] mActualDims = null;
+
+        /** The requested facing */
+        private int mRequestedFacing = FACING_DONTCARE;
+
+        /** The actual facing */
+        private int mActualFacing = FACING_DONTCARE;
+
+        /** Whether to horizontally flip the front facing camera */
+        private boolean mFlipFront = true;
+
+        /** The display the camera streamer is bound to. */
+        private Display mDisplay = null;
+
+        /** The camera and screen orientation. */
+        private int mCamOrientation = 0;
+        private int mOrientation = -1;
+
+        /** The camera rotation (used for capture). */
+        private int mCamRotation = 0;
+
+        /** The camera flash mode */
+        private String mFlashMode = Camera.Parameters.FLASH_MODE_OFF;
+
+        /** The camera object */
+        private Camera mCamera = null;
+
+        private MediaRecorder mRecorder = null;
+
+        /** The ID of the currently used camera */
+        int mCamId = 0;
+
+        /** The platform-dependent camera frame handler. */
+        private CamFrameHandler mCamFrameHandler = null;
+
+        /** The set of camera listeners. */
+        private Set<CameraListener> mCamListeners = new HashSet<CameraListener>();
+
+        private ReentrantLock mCameraReadyLock = new ReentrantLock(true);
+        // mCameraReady condition is used when waiting for the camera getting ready.
+        private Condition mCameraReady = mCameraReadyLock.newCondition();
+        // external camera lock used to provide the capability of external camera access.
+        private ExternalCameraLock mExternalCameraLock = new ExternalCameraLock();
+
+        private RenderTarget mRenderTarget;
+        private MffContext mContext;
+
+        /**
+         *  This provides the capability of locking and unlocking from different threads.
+         *  The thread will wait until the lock state is idle. Any thread can wake up
+         *  a waiting thread by calling unlock (i.e. signal), provided that unlock
+         *  are called using the same context when lock was called. Using context prevents
+         *  from rogue usage of unlock.
+         */
+        private class ExternalCameraLock {
+            public static final int IDLE = 0;
+            public static final int IN_USE = 1;
+            private int mLockState = IDLE;
+            private Object mLockContext;
+            private final ReentrantLock mLock = new ReentrantLock(true);
+            private final Condition mInUseLockCondition= mLock.newCondition();
+
+            public boolean lock(Object context) {
+                if (context == null) {
+                    throw new RuntimeException("Null context when locking");
+                }
+                mLock.lock();
+                if (mLockState == IN_USE) {
+                    try {
+                        mInUseLockCondition.await();
+                    } catch (InterruptedException e) {
+                        return false;
+                    }
+                }
+                mLockState = IN_USE;
+                mLockContext = context;
+                mLock.unlock();
+                return true;
+            }
+
+            public void unlock(Object context) {
+                mLock.lock();
+                if (mLockState != IN_USE) {
+                    throw new RuntimeException("Not in IN_USE state");
+                }
+                if (context != mLockContext) {
+                    throw new RuntimeException("Lock is not owned by this context");
+                }
+                mLockState = IDLE;
+                mLockContext = null;
+                mInUseLockCondition.signal();
+                mLock.unlock();
+            }
+        }
+
+        public CameraRunnable(MffContext context) {
+            mContext = context;
+            createCamFrameHandler();
+            mCamFrameHandler.initWithRunner(this);
+            launchThread();
+        }
+
+        public MffContext getContext() {
+            return mContext;
+        }
+
+        public void loop() {
+            while (true) {
+                try {
+                    Event event = nextEvent();
+                    if (event == null) continue;
+                    switch (event.code) {
+                        case Event.START:
+                            onStart();
+                            break;
+                        case Event.STOP:
+                            onStop();
+                            break;
+                        case Event.FRAME:
+                            onFrame();
+                            break;
+                        case Event.HALT:
+                            onHalt();
+                            break;
+                        case Event.RESTART:
+                            onRestart();
+                            break;
+                        case Event.UPDATE:
+                            onUpdate();
+                            break;
+                        case Event.TEARDOWN:
+                            onTearDown();
+                            break;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            loop();
+        }
+
+        public void signalNewFrame() {
+            pushEvent(Event.FRAME, false);
+        }
+
+        public void pushEvent(int eventId, boolean required) {
+            try {
+                if (required) {
+                    mEventQueue.put(new Event(eventId));
+                } else {
+                    mEventQueue.offer(new Event(eventId));
+                }
+            } catch (InterruptedException e) {
+                // We should never get here (as we do not limit capacity in the queue), but if
+                // we do, we log an error.
+                Log.e("CameraStreamer", "Dropping event " + eventId + "!");
+            }
+        }
+
+        public void launchThread() {
+            Thread cameraThread = new Thread(this);
+            cameraThread.start();
+        }
+
+        @Deprecated
+        public Camera getCamera() {
+            synchronized (mState) {
+                return mCamera;
+            }
+        }
+
+        public Camera lockCamera(Object context) {
+            mExternalCameraLock.lock(context);
+            /**
+             * since lockCamera can happen right after closeCamera,
+             * the camera handle can be null, wait until valid handle
+             * is acquired.
+             */
+            while (mCamera == null) {
+                mExternalCameraLock.unlock(context);
+                mCameraReadyLock.lock();
+                try {
+                    mCameraReady.await();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException("Condition interrupted", e);
+                }
+                mCameraReadyLock.unlock();
+                mExternalCameraLock.lock(context);
+            }
+            return mCamera;
+        }
+
+        public void unlockCamera(Object context) {
+            mExternalCameraLock.unlock(context);
+        }
+
+        public int getCurrentCameraId() {
+            synchronized (mState) {
+                return mCamId;
+            }
+        }
+
+        public boolean isRunning() {
+            return mState.current() != State.STATE_STOPPED;
+        }
+
+        public void addListener(CameraListener listener) {
+            synchronized (mCamListeners) {
+                mCamListeners.add(listener);
+            }
+        }
+
+        public void removeListener(CameraListener listener) {
+            synchronized (mCamListeners) {
+                mCamListeners.remove(listener);
+            }
+        }
+
+        public synchronized void bindToDisplay(Display display) {
+            mDisplay = display;
+        }
+
+        public synchronized void setDesiredPreviewSize(int width, int height) {
+            if (width != mRequestedPreviewWidth || height != mRequestedPreviewHeight) {
+                mRequestedPreviewWidth = width;
+                mRequestedPreviewHeight = height;
+                onParamsUpdated();
+            }
+        }
+
+        public synchronized void setDesiredPictureSize(int width, int height) {
+            if (width != mRequestedPictureWidth || height != mRequestedPictureHeight) {
+                mRequestedPictureWidth = width;
+                mRequestedPictureHeight = height;
+                onParamsUpdated();
+            }
+        }
+
+        public synchronized void setDesiredFrameRate(int fps) {
+            if (fps != mRequestedFramesPerSec) {
+                mRequestedFramesPerSec = fps;
+                onParamsUpdated();
+            }
+        }
+
+        public synchronized void setFacing(int facing) {
+            if (facing != mRequestedFacing) {
+                switch (facing) {
+                    case FACING_DONTCARE:
+                    case FACING_FRONT:
+                    case FACING_BACK:
+                        mRequestedFacing = facing;
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unknown facing value '" + facing
+                            + "' passed to setFacing!");
+                }
+                onParamsUpdated();
+            }
+        }
+
+        public synchronized void setFlipFrontCamera(boolean flipFront) {
+            if (mFlipFront != flipFront) {
+                mFlipFront = flipFront;
+            }
+        }
+
+        public synchronized void setFlashMode(String flashMode) {
+            if (!flashMode.equals(mFlashMode)) {
+                mFlashMode = flashMode;
+                onParamsUpdated();
+            }
+        }
+
+        public synchronized int getCameraFacing() {
+            return mActualFacing;
+        }
+
+        public synchronized int getCameraRotation() {
+            return mCamRotation;
+        }
+
+        public synchronized boolean supportsHardwareFaceDetection() {
+            //return mCamFrameHandler.supportsHardwareFaceDetection();
+            // TODO
+            return true;
+        }
+
+        public synchronized int getCameraWidth() {
+            return (mActualDims != null) ? mActualDims[0] : 0;
+        }
+
+        public synchronized int getCameraHeight() {
+            return (mActualDims != null) ? mActualDims[1] : 0;
+        }
+
+        public synchronized int getCameraFrameRate() {
+            return mActualFramesPerSec;
+        }
+
+        public synchronized String getFlashMode() {
+            return mCamera.getParameters().getFlashMode();
+        }
+
+        public synchronized boolean canStart() {
+            // If we can get a camera id without error we should be able to start.
+            try {
+                getCameraId();
+            } catch (RuntimeException e) {
+                return false;
+            }
+            return true;
+        }
+
+        public boolean grabFrame(FrameImage2D targetFrame) {
+            // Make sure we stay in state running while we are grabbing the frame.
+            synchronized (mState) {
+                if (mState.current() != State.STATE_RUNNING) {
+                    return false;
+                }
+                // we may not have the camera ready, this might happen when in the middle
+                // of switching camera.
+                if (mCamera == null) {
+                    return false;
+                }
+                mCamFrameHandler.grabFrame(targetFrame);
+                return true;
+            }
+        }
+
+        public CamFrameHandler getCamFrameHandler() {
+            return mCamFrameHandler;
+        }
+
+        private void onParamsUpdated() {
+            pushEvent(Event.UPDATE, true);
+        }
+
+        private Event nextEvent() {
+            try {
+                return mEventQueue.take();
+            } catch (InterruptedException e) {
+                // Ignore and keep going.
+                Log.w("GraphRunner", "Event queue processing was interrupted.");
+                return null;
+            }
+        }
+
+        private void onStart() {
+            if (mState.current() == State.STATE_STOPPED) {
+                mState.set(State.STATE_RUNNING);
+                getRenderTarget().focus();
+                openCamera();
+            }
+        }
+
+        private void onStop() {
+            if (mState.current() == State.STATE_RUNNING) {
+                closeCamera();
+                RenderTarget.focusNone();
+            }
+            // Set state to stop (halted becomes stopped).
+            mState.set(State.STATE_STOPPED);
+        }
+
+        private void onHalt() {
+            // Only halt if running. Stopped overrides halt.
+            if (mState.current() == State.STATE_RUNNING) {
+                closeCamera();
+                RenderTarget.focusNone();
+                mState.set(State.STATE_HALTED);
+            }
+        }
+
+        private void onRestart() {
+            // Only restart if halted
+            if (mState.current() == State.STATE_HALTED) {
+                mState.set(State.STATE_RUNNING);
+                getRenderTarget().focus();
+                openCamera();
+            }
+        }
+
+        private void onUpdate() {
+            if (mState.current() == State.STATE_RUNNING) {
+                pushEvent(Event.STOP, true);
+                pushEvent(Event.START, true);
+            }
+        }
+        private void onFrame() {
+            if (mState.current() == State.STATE_RUNNING) {
+                updateRotation();
+                mCamFrameHandler.updateServerFrame();
+            }
+        }
+
+        private void onTearDown() {
+            if (mState.current() == State.STATE_STOPPED) {
+                // Remove all listeners. This will release their resources
+                for (CameraListener listener : mCamListeners) {
+                    removeListener(listener);
+                }
+                mCamListeners.clear();
+            } else {
+                Log.e("CameraStreamer", "Could not tear-down CameraStreamer as camera still "
+                        + "seems to be running!");
+            }
+        }
+
+        private void createCamFrameHandler() {
+            // TODO: For now we simply assert that OpenGL is supported. Later on, we should add
+            // a CamFrameHandler that does not depend on OpenGL.
+            getContext().assertOpenGLSupported();
+            if (VERSION.SDK_INT >= 16) {
+                mCamFrameHandler = new CamFrameHandlerJB();
+            } else if (VERSION.SDK_INT >= 15) {
+                mCamFrameHandler = new CamFrameHandlerICS();
+            } else {
+                mCamFrameHandler = new CamFrameHandlerGB();
+            }
+        }
+
+        private void updateRotation() {
+            if (mDisplay != null) {
+                updateDisplayRotation(mDisplay.getRotation());
+            }
+        }
+
+        private synchronized void updateDisplayRotation(int rotation) {
+            switch (rotation) {
+                case Surface.ROTATION_0:
+                    onUpdateOrientation(0);
+                    break;
+                case Surface.ROTATION_90:
+                    onUpdateOrientation(90);
+                    break;
+                case Surface.ROTATION_180:
+                    onUpdateOrientation(180);
+                    break;
+                case Surface.ROTATION_270:
+                    onUpdateOrientation(270);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported display rotation constant! Use "
+                        + "one of the Surface.ROTATION_ constants!");
+            }
+        }
+
+        private RenderTarget getRenderTarget() {
+            if (mRenderTarget == null) {
+                mRenderTarget = RenderTarget.newTarget(1, 1);
+            }
+            return mRenderTarget;
+        }
+
+        private void updateCamera() {
+            synchronized (mState) {
+                mCamId = getCameraId();
+                updateCameraOrientation(mCamId);
+                mCamera = Camera.open(mCamId);
+                initCameraParameters();
+            }
+        }
+
+        private void updateCameraOrientation(int camId) {
+            CameraInfo cameraInfo = new CameraInfo();
+            Camera.getCameraInfo(camId, cameraInfo);
+            mCamOrientation = cameraInfo.orientation;
+            mOrientation = -1;  // Forces recalculation to match display
+            mActualFacing = (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT)
+                ? FACING_FRONT
+                : FACING_BACK;
+        }
+
+        private int getCameraId() {
+            int camCount = Camera.getNumberOfCameras();
+            if (camCount == 0) {
+                throw new RuntimeException("Device does not have any cameras!");
+            } else if (mRequestedFacing == FACING_DONTCARE) {
+                // Simply return first camera if mRequestedFacing is don't care
+                return 0;
+            }
+
+            // Attempt to find requested camera
+            boolean useFrontCam = (mRequestedFacing == FACING_FRONT);
+            CameraInfo cameraInfo = new CameraInfo();
+            for (int i = 0; i < camCount; ++i) {
+                Camera.getCameraInfo(i, cameraInfo);
+                if ((cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) == useFrontCam) {
+                    return i;
+                }
+            }
+            throw new RuntimeException("Could not find a camera facing (" + mRequestedFacing
+                    + ")!");
+        }
+
+        private void initCameraParameters() {
+            Camera.Parameters params = mCamera.getParameters();
+
+            // Find closest preview size
+            mActualDims =
+                findClosestPreviewSize(mRequestedPreviewWidth, mRequestedPreviewHeight, params);
+            mCamFrameHandler.setCameraSize(mActualDims[0], mActualDims[1]);
+            params.setPreviewSize(mActualDims[0], mActualDims[1]);
+            // Find closest picture size
+            int[] dims =
+                findClosestPictureSize(mRequestedPictureWidth, mRequestedPictureHeight, params);
+            params.setPictureSize(dims[0], dims[1]);
+
+            // Find closest FPS
+            int closestRange[] = findClosestFpsRange(mRequestedFramesPerSec, params);
+            params.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                                      closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+            // Set flash mode (if supported)
+            if (params.getFlashMode() != null) {
+                params.setFlashMode(mFlashMode);
+            }
+
+            mCamera.setParameters(params);
+        }
+
+        private int[] findClosestPreviewSize(int width, int height, Camera.Parameters parameters) {
+            List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+            return findClosestSizeFromList(width, height, previewSizes);
+        }
+
+        private int[] findClosestPictureSize(int width, int height, Camera.Parameters parameters) {
+            List<Camera.Size> pictureSizes = parameters.getSupportedPictureSizes();
+            return findClosestSizeFromList(width, height, pictureSizes);
+        }
+
+        private int[] findClosestSizeFromList(int width, int height, List<Camera.Size> sizes) {
+            int closestWidth = -1;
+            int closestHeight = -1;
+            int smallestWidth = sizes.get(0).width;
+            int smallestHeight =  sizes.get(0).height;
+            for (Camera.Size size : sizes) {
+                // Best match defined as not being larger in either dimension than
+                // the requested size, but as close as possible. The below isn't a
+                // stable selection (reording the size list can give different
+                // results), but since this is a fallback nicety, that's acceptable.
+                if ( size.width <= width &&
+                     size.height <= height &&
+                     size.width >= closestWidth &&
+                     size.height >= closestHeight) {
+                    closestWidth = size.width;
+                    closestHeight = size.height;
+                }
+                if ( size.width < smallestWidth &&
+                     size.height < smallestHeight) {
+                    smallestWidth = size.width;
+                    smallestHeight = size.height;
+                }
+            }
+            if (closestWidth == -1) {
+                // Requested size is smaller than any listed size; match with smallest possible
+                closestWidth = smallestWidth;
+                closestHeight = smallestHeight;
+            }
+            int[] closestSize = {closestWidth, closestHeight};
+            return closestSize;
+        }
+
+        private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
+            List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
+            int[] closestRange = supportedFpsRanges.get(0);
+            int fpsk = fps * 1000;
+            int minDiff = 1000000;
+            for (int[] range : supportedFpsRanges) {
+                int low = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
+                int high = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
+                if (low <= fpsk && high >= fpsk) {
+                    int diff = (fpsk - low) + (high - fpsk);
+                    if (diff < minDiff) {
+                        closestRange = range;
+                        minDiff = diff;
+                    }
+                }
+            }
+            mActualFramesPerSec = closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000;
+            return closestRange;
+        }
+
+        private void onUpdateOrientation(int orientation) {
+            // First we calculate the camera rotation.
+            int rotation = (mActualFacing == FACING_FRONT)
+                    ? (mCamOrientation + orientation) % 360
+                    : (mCamOrientation - orientation + 360) % 360;
+            if (rotation != mCamRotation) {
+                synchronized (this) {
+                    mCamRotation = rotation;
+                }
+            }
+
+            // We compensate for mirroring in the orientation. This differs from the rotation,
+            // where we are invariant to mirroring.
+            int fixedOrientation = rotation;
+            if (mActualFacing == FACING_FRONT && mCamFrameHandler.isFrontMirrored()) {
+                fixedOrientation = (360 - rotation) % 360;  // compensate the mirror
+            }
+            if (mOrientation != fixedOrientation) {
+                mOrientation = fixedOrientation;
+                mCamFrameHandler.onUpdateCameraOrientation(mOrientation);
+            }
+        }
+
+        private void openCamera() {
+            // Acquire lock for camera
+            try {
+                if (!mCameraLock.tryLock(MAX_CAMERA_WAIT_TIME, TimeUnit.SECONDS)) {
+                    throw new RuntimeException("Timed out while waiting to acquire camera!");
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Interrupted while waiting to acquire camera!");
+            }
+
+            // Make sure external entities are not holding camera. We need to hold the lock until
+            // the preview is started again.
+            Object lockContext = new Object();
+            mExternalCameraLock.lock(lockContext);
+
+            // Need to synchronize this as many of the member values are modified during setup.
+            synchronized (this) {
+                updateCamera();
+                updateRotation();
+                mCamFrameHandler.setupServerFrame();
+            }
+
+            mCamera.startPreview();
+
+            // Inform listeners
+            synchronized (mCamListeners) {
+                for (CameraListener listener : mCamListeners) {
+                    listener.onCameraOpened(CameraStreamer.this);
+                }
+            }
+            mExternalCameraLock.unlock(lockContext);
+            // New camera started
+            mCameraReadyLock.lock();
+            mCameraReady.signal();
+            mCameraReadyLock.unlock();
+        }
+
+        /**
+         * Creates an instance of MediaRecorder to be used for the streamer.
+         * User should call the functions in the following sequence:<p>
+         *   {@link #createRecorder}<p>
+         *   {@link #startRecording}<p>
+         *   {@link #stopRecording}<p>
+         *   {@link #releaseRecorder}<p>
+         * @param outputPath the output video path for the recorder
+         * @param profile the recording {@link CamcorderProfile} which has parameters indicating
+         *  the resolution, quality etc.
+         */
+        public void createRecorder(String outputPath, CamcorderProfile profile) {
+            lockCamera(this);
+            mCamera.unlock();
+            if (mRecorder != null) {
+                mRecorder.release();
+            }
+            mRecorder = new MediaRecorder();
+            mRecorder.setCamera(mCamera);
+            mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+            mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+            mRecorder.setProfile(profile);
+            mRecorder.setOutputFile(outputPath);
+            try {
+                mRecorder.prepare();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Starts recording video using the created MediaRecorder object
+         */
+        public void startRecording() {
+            if (mRecorder == null) {
+                throw new RuntimeException("No recorder created");
+            }
+            mRecorder.start();
+        }
+
+        /**
+         * Stops recording video
+         */
+        public void stopRecording() {
+            if (mRecorder == null) {
+                throw new RuntimeException("No recorder created");
+            }
+            mRecorder.stop();
+        }
+
+        /**
+         * Release the resources held by the MediaRecorder, call this after done recording.
+         */
+        public void releaseRecorder() {
+            if (mRecorder == null) {
+                throw new RuntimeException("No recorder created");
+            }
+            mRecorder.release();
+            mRecorder = null;
+            mCamera.lock();
+            unlockCamera(this);
+        }
+
+        private void closeCamera() {
+            Object lockContext = new Object();
+            mExternalCameraLock.lock(lockContext);
+            if (mCamera != null) {
+                mCamera.stopPreview();
+                mCamera.release();
+                mCamera = null;
+            }
+            mCameraLock.unlock();
+            mCamFrameHandler.release();
+            mExternalCameraLock.unlock(lockContext);
+            // Inform listeners
+            synchronized (mCamListeners) {
+                for (CameraListener listener : mCamListeners) {
+                    listener.onCameraClosed(CameraStreamer.this);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * The frame-client callback interface.
+     * FrameClients, that wish to receive Frames from the camera must implement this callback
+     * method.
+     * Note, that this method is called on the Camera server thread. However, the
+     * {@code getLatestFrame()} method must be called from the client thread.
+     */
+    public static interface FrameClient {
+        public void onCameraFrameAvailable();
+    }
+
+    /**
+     * The CameraListener callback interface.
+     * This interface allows observers to monitor the CameraStreamer and respond to stream open
+     * and close events.
+     */
+    public static interface CameraListener {
+        /**
+         * Called when the camera is opened and begins producing frames.
+         * This is also called when settings have changed that caused the camera to be reopened.
+         */
+        public void onCameraOpened(CameraStreamer camera);
+
+        /**
+         * Called when the camera is closed and stops producing frames.
+         */
+        public void onCameraClosed(CameraStreamer camera);
+    }
+
+    /**
+     * Manually update the display rotation.
+     * You do not need to call this, if the camera is bound to a display, or your app does not
+     * support multiple orientations.
+     */
+    public void updateDisplayRotation(int rotation) {
+        mCameraRunner.updateDisplayRotation(rotation);
+    }
+
+    /**
+     * Bind the camera to your Activity's display.
+     * Use this, if your Activity supports multiple display orientation, and you would like the
+     * camera to update accordingly when the orientation is changed.
+     */
+    public void bindToDisplay(Display display) {
+        mCameraRunner.bindToDisplay(display);
+    }
+
+    /**
+     * Sets the desired preview size.
+     * Note that the actual width and height may vary.
+     *
+     * @param width The desired width of the preview camera stream.
+     * @param height The desired height of the preview camera stream.
+     */
+    public void setDesiredPreviewSize(int width, int height) {
+        mCameraRunner.setDesiredPreviewSize(width, height);
+    }
+
+    /**
+     * Sets the desired picture size.
+     * Note that the actual width and height may vary.
+     *
+     * @param width The desired picture width.
+     * @param height The desired picture height.
+     */
+    public void setDesiredPictureSize(int width, int height) {
+        mCameraRunner.setDesiredPictureSize(width, height);
+    }
+
+    /**
+     * Sets the desired camera frame-rate.
+     * Note, that the actual frame-rate may vary.
+     *
+     * @param fps The desired FPS.
+     */
+    public void setDesiredFrameRate(int fps) {
+        mCameraRunner.setDesiredFrameRate(fps);
+    }
+
+    /**
+     * Sets the camera facing direction.
+     *
+     * Specify {@code FACING_DONTCARE} (default) if you would like the CameraStreamer to choose
+     * the direction. When specifying any other direction be sure to first check whether the
+     * device supports the desired facing.
+     *
+     * @param facing The desired camera facing direction.
+     */
+    public void setFacing(int facing) {
+        mCameraRunner.setFacing(facing);
+    }
+
+    /**
+     * Set whether to flip the camera image horizontally when using the front facing camera.
+     */
+    public void setFlipFrontCamera(boolean flipFront) {
+        mCameraRunner.setFlipFrontCamera(flipFront);
+    }
+
+    /**
+     * Sets the camera flash mode.
+     *
+     * This must be one of the String constants defined in the Camera.Parameters class.
+     *
+     * @param flashMode A String constant specifying the flash mode.
+     */
+    public void setFlashMode(String flashMode) {
+        mCameraRunner.setFlashMode(flashMode);
+    }
+
+    /**
+     * Returns the current flash mode.
+     *
+     * This returns the currently running camera's flash-mode, or NULL if flash modes are not
+     * supported on that camera.
+     *
+     * @return The flash mode String, or NULL if flash modes are not supported.
+     */
+    public String getFlashMode() {
+        return mCameraRunner.getFlashMode();
+    }
+
+    /**
+     * Get the actual camera facing.
+     * Returns 0 if actual facing is not yet known.
+     */
+    public int getCameraFacing() {
+        return mCameraRunner.getCameraFacing();
+    }
+
+    /**
+     * Get the current camera rotation.
+     *
+     * Use this rotation if you want to snap pictures from the camera and need to rotate the
+     * picture to be up-right.
+     *
+     * @return the current camera rotation.
+     */
+    public int getCameraRotation() {
+        return mCameraRunner.getCameraRotation();
+    }
+
+    /**
+     * Specifies whether or not the camera supports hardware face detection.
+     * @return true, if the camera supports hardware face detection.
+     */
+    public boolean supportsHardwareFaceDetection() {
+        return mCameraRunner.supportsHardwareFaceDetection();
+    }
+
+    /**
+     * Returns the camera facing that is chosen when DONT_CARE is specified.
+     * Returns 0 if neither a front nor back camera could be found.
+     */
+    public static int getDefaultFacing() {
+        int camCount = Camera.getNumberOfCameras();
+        if (camCount == 0) {
+            return 0;
+        } else {
+            CameraInfo cameraInfo = new CameraInfo();
+            Camera.getCameraInfo(0, cameraInfo);
+            return (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT)
+                ? FACING_FRONT
+                : FACING_BACK;
+        }
+    }
+
+    /**
+     * Get the actual camera width.
+     * Returns 0 if actual width is not yet known.
+     */
+    public int getCameraWidth() {
+        return mCameraRunner.getCameraWidth();
+    }
+
+    /**
+     * Get the actual camera height.
+     * Returns 0 if actual height is not yet known.
+     */
+    public int getCameraHeight() {
+        return mCameraRunner.getCameraHeight();
+    }
+
+    /**
+     * Get the actual camera frame-rate.
+     * Returns 0 if actual frame-rate is not yet known.
+     */
+    public int getCameraFrameRate() {
+        return mCameraRunner.getCameraFrameRate();
+    }
+
+    /**
+     * Returns true if the camera can be started at this point.
+     */
+    public boolean canStart() {
+        return mCameraRunner.canStart();
+    }
+
+    /**
+     * Returns true if the camera is currently running.
+     */
+    public boolean isRunning() {
+        return mCameraRunner.isRunning();
+    }
+
+    /**
+     * Starts the camera.
+     */
+    public void start() {
+        mCameraRunner.pushEvent(Event.START, true);
+    }
+
+    /**
+     * Stops the camera.
+     */
+    public void stop() {
+        mCameraRunner.pushEvent(Event.STOP, true);
+    }
+
+    /**
+     * Stops the camera and waits until it is completely closed. Generally, this should not be
+     * called in the UI thread, but may be necessary if you need the camera to be closed before
+     * performing subsequent steps.
+     */
+    public void stopAndWait() {
+        mCameraRunner.pushEvent(Event.STOP, true);
+        try {
+            if (!mCameraLock.tryLock(MAX_CAMERA_WAIT_TIME, TimeUnit.SECONDS)) {
+                Log.w("CameraStreamer", "Time-out waiting for camera to close!");
+            }
+        } catch (InterruptedException e) {
+            Log.w("CameraStreamer", "Interrupted while waiting for camera to close!");
+        }
+        mCameraLock.unlock();
+    }
+
+    /**
+     * Registers a listener to handle camera state changes.
+     */
+    public void addListener(CameraListener listener) {
+        mCameraRunner.addListener(listener);
+    }
+
+    /**
+     * Unregisters a listener to handle camera state changes.
+     */
+    public void removeListener(CameraListener listener) {
+        mCameraRunner.removeListener(listener);
+    }
+
+    /**
+     * Registers the frame-client with the camera.
+     * This MUST be called from the client thread!
+     */
+    public void registerClient(FrameClient client) {
+        mCameraRunner.getCamFrameHandler().registerClient(client);
+    }
+
+    /**
+     * Unregisters the frame-client with the camera.
+     * This MUST be called from the client thread!
+     */
+    public void unregisterClient(FrameClient client) {
+        mCameraRunner.getCamFrameHandler().unregisterClient(client);
+    }
+
+    /**
+     * Gets the latest camera frame for the client.
+     *
+     * This must be called from the same thread as the {@link #registerClient(FrameClient)} call!
+     * The frame passed in will be resized by the camera streamer to fit the camera frame.
+     * Returns false if the frame could not be grabbed. This may happen if the camera has been
+     * closed in the meantime, and its resources let go.
+     *
+     * @return true, if the frame was grabbed successfully.
+     */
+    public boolean getLatestFrame(FrameImage2D targetFrame) {
+        return mCameraRunner.grabFrame(targetFrame);
+    }
+
+    /**
+     * Expose the underlying android.hardware.Camera object.
+     * Use the returned object with care: some camera functions may break the functionality
+     * of CameraStreamer.
+     * @return the Camera object.
+     */
+    @Deprecated
+    public Camera getCamera() {
+        return mCameraRunner.getCamera();
+    }
+
+    /**
+     * Obtain access to the underlying android.hardware.Camera object.
+     * This grants temporary access to the internal Camera handle. Once you are done using the
+     * handle you must call {@link #unlockCamera(Object)}. While you are holding the Camera,
+     * it will not be modified or released by the CameraStreamer. The Camera object return is
+     * guaranteed to have the preview running.
+     *
+     * The CameraStreamer does not account for changes you make to the Camera. That is, if you
+     * change the Camera unexpectedly this may cause unintended behavior by the streamer.
+     *
+     * Note that the returned object may be null. This can happen when the CameraStreamer is not
+     * running, or is just transitioning to another Camera, such as during a switch from front to
+     * back Camera.
+     * @param context an object used as a context for locking and unlocking. lockCamera and
+     *   unlockCamera should use the same context object.
+     * @return The Camera object.
+     */
+    public Camera lockCamera(Object context) {
+        return mCameraRunner.lockCamera(context);
+    }
+
+    /**
+     * Release the acquire Camera object.
+     * @param context the context object that used when lockCamera is called.
+     */
+    public void unlockCamera(Object context) {
+        mCameraRunner.unlockCamera(context);
+    }
+
+    /**
+     * Creates an instance of MediaRecorder to be used for the streamer.
+     * User should call the functions in the following sequence:<p>
+     *   {@link #createRecorder}<p>
+     *   {@link #startRecording}<p>
+     *   {@link #stopRecording}<p>
+     *   {@link #releaseRecorder}<p>
+     * @param path the output video path for the recorder
+     * @param profile the recording {@link CamcorderProfile} which has parameters indicating
+     *  the resolution, quality etc.
+     */
+    public void createRecorder(String path, CamcorderProfile profile) {
+        mCameraRunner.createRecorder(path, profile);
+    }
+
+    public void releaseRecorder() {
+        mCameraRunner.releaseRecorder();
+    }
+
+    public void startRecording() {
+        mCameraRunner.startRecording();
+    }
+
+    public void stopRecording() {
+        mCameraRunner.stopRecording();
+    }
+
+    /**
+     * Retrieve the ID of the currently used camera.
+     * @return the ID of the currently used camera.
+     */
+    public int getCameraId() {
+        return mCameraRunner.getCurrentCameraId();
+    }
+
+    /**
+     * @return The number of cameras available for streaming on this device.
+     */
+    public static int getNumberOfCameras() {
+        // Currently, this is just the number of cameras that are available on the device.
+        return Camera.getNumberOfCameras();
+    }
+
+    CameraStreamer(MffContext context) {
+        mCameraRunner = new CameraRunnable(context);
+    }
+
+    /** Halt is like stop, but may be resumed using restart(). */
+    void halt() {
+        mCameraRunner.pushEvent(Event.HALT, true);
+    }
+
+    /** Restart starts the camera only if previously halted. */
+    void restart() {
+        mCameraRunner.pushEvent(Event.RESTART, true);
+    }
+
+    static boolean requireDummySurfaceView() {
+        return VERSION.SDK_INT < 15;
+    }
+
+    void tearDown() {
+        mCameraRunner.pushEvent(Event.TEARDOWN, true);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorSpace.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorSpace.java
new file mode 100644
index 0000000..f2bfe08
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorSpace.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Utility functions to convert between color-spaces.
+ *
+ * Currently these methods are all CPU based native methods. These could be updated in the future
+ * to provide other implementations.
+ */
+public class ColorSpace {
+
+    /**
+     * Convert YUV420-Planer data to RGBA8888.
+     *
+     * The input data is expected to be laid out in 3 planes. The width x height Y plane, followed
+     * by the U and V planes, where each chroma value corresponds to a 2x2 luminance value block.
+     * YUV to RGB conversion is done using the ITU-R BT.601 transformation. The output buffer must
+     * be large enough to hold the data, and the dimensions must be multiples of 2.
+     *
+     * @param input data encoded in YUV420-Planar.
+     * @param output buffer to hold RGBA8888 data.
+     * @param width the width of the image (must be a multiple of 2)
+     * @param height the height of the image (must be a multiple of 2)
+     */
+    public static void convertYuv420pToRgba8888(
+            ByteBuffer input, ByteBuffer output, int width, int height) {
+        expectInputSize(input, (3 * width * height) / 2);
+        expectOutputSize(output, width * height * 4);
+        nativeYuv420pToRgba8888(input, output, width, height);
+    }
+
+    /**
+     * Convert ARGB8888 to RGBA8888.
+     *
+     * The input data is expected to be encoded in 8-bit interleaved ARGB channels. The output
+     * buffer must be large enough to hold the data. The output buffer may be the same as the
+     * input buffer.
+     *
+     * @param input data encoded in ARGB8888.
+     * @param output buffer to hold RGBA8888 data.
+     * @param width the width of the image
+     * @param height the height of the image
+     */
+    public static void convertArgb8888ToRgba8888(
+            ByteBuffer input, ByteBuffer output, int width, int height) {
+        expectInputSize(input, width * height * 4);
+        expectOutputSize(output, width * height * 4);
+        nativeArgb8888ToRgba8888(input, output, width, height);
+    }
+
+    /**
+     * Convert RGBA8888 to HSVA8888.
+     *
+     * The input data is expected to be encoded in 8-bit interleaved RGBA channels. The output
+     * buffer must be large enough to hold the data. The output buffer may be the same as the
+     * input buffer.
+     *
+     * @param input data encoded in RGBA8888.
+     * @param output buffer to hold HSVA8888 data.
+     * @param width the width of the image
+     * @param height the height of the image
+     */
+    public static void convertRgba8888ToHsva8888(
+            ByteBuffer input, ByteBuffer output, int width, int height) {
+        expectInputSize(input, width * height * 4);
+        expectOutputSize(output, width * height * 4);
+        nativeRgba8888ToHsva8888(input, output, width, height);
+    }
+
+    /**
+     * Convert RGBA8888 to YCbCrA8888.
+     *
+     * The input data is expected to be encoded in 8-bit interleaved RGBA channels. The output
+     * buffer must be large enough to hold the data. The output buffer may be the same as the
+     * input buffer.
+     *
+     * @param input data encoded in RGBA8888.
+     * @param output buffer to hold YCbCrA8888 data.
+     * @param width the width of the image
+     * @param height the height of the image
+     */
+    public static void convertRgba8888ToYcbcra8888(
+            ByteBuffer input, ByteBuffer output, int width, int height) {
+        expectInputSize(input, width * height * 4);
+        expectOutputSize(output, width * height * 4);
+        nativeRgba8888ToYcbcra8888(input, output, width, height);
+    }
+
+    private static void expectInputSize(ByteBuffer input, int expectedSize) {
+        if (input.remaining() < expectedSize) {
+            throw new IllegalArgumentException("Input buffer's size does not fit given width "
+                    + "and height! Expected: " + expectedSize + ", Got: " + input.remaining()
+                    + ".");
+        }
+    }
+
+    private static void expectOutputSize(ByteBuffer output, int expectedSize) {
+        if (output.remaining() < expectedSize) {
+            throw new IllegalArgumentException("Output buffer's size does not fit given width "
+                    + "and height! Expected: " + expectedSize + ", Got: " + output.remaining()
+                    + ".");
+        }
+    }
+
+    private static native void nativeYuv420pToRgba8888(
+            ByteBuffer input, ByteBuffer output, int width, int height);
+
+    private static native void nativeArgb8888ToRgba8888(
+            ByteBuffer input, ByteBuffer output, int width, int height);
+
+    private static native void nativeRgba8888ToHsva8888(
+            ByteBuffer input, ByteBuffer output, int width, int height);
+
+    private static native void nativeRgba8888ToYcbcra8888(
+            ByteBuffer input, ByteBuffer output, int width, int height);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorfulnessFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorfulnessFilter.java
new file mode 100644
index 0000000..5bdf4af
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ColorfulnessFilter.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+// Extract histogram from image.
+
+package androidx.media.filterpacks.colorspace;
+
+import androidx.media.filterfw.FrameValue;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * ColorfulnessFilter takes in a particular Chroma histogram generated by NewChromaHistogramFilter
+ * and compute the colorfulness based on the entropy in Hue space.
+ */
+public final class ColorfulnessFilter extends Filter {
+
+    public ColorfulnessFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType dataIn = FrameType.buffer2D(FrameType.ELEMENT_FLOAT32);
+        return new Signature()
+            .addInputPort("histogram", Signature.PORT_REQUIRED, dataIn)
+            .addOutputPort("score", Signature.PORT_REQUIRED, FrameType.single(float.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameBuffer2D histogramFrame =
+                getConnectedInputPort("histogram").pullFrame().asFrameBuffer2D();
+        ByteBuffer byteBuffer = histogramFrame.lockBytes(Frame.MODE_READ);
+        byteBuffer.order(ByteOrder.nativeOrder());
+        FloatBuffer histogramBuffer = byteBuffer.asFloatBuffer();
+        histogramBuffer.rewind();
+
+        // Create a hue histogram from hue-saturation histogram
+        int hueBins = histogramFrame.getWidth();
+        int saturationBins = histogramFrame.getHeight() - 1;
+        float[] hueHistogram = new float[hueBins];
+        float total = 0;
+        for (int r = 0; r < saturationBins; ++r) {
+            float weight = (float) Math.pow(2, r);
+            for (int c = 0; c < hueBins; c++) {
+                float value = histogramBuffer.get() * weight;
+                hueHistogram[c] += value;
+                total += value;
+            }
+        }
+        float colorful = 0f;
+        for (int c = 0; c < hueBins; ++c) {
+            float value = hueHistogram[c] / total;
+            if (value > 0f) {
+                colorful -= value * ((float) Math.log(value));
+            }
+        }
+
+        colorful /= Math.log(2);
+
+        histogramFrame.unlock();
+        OutputPort outPort = getConnectedOutputPort("score");
+        FrameValue frameValue = outPort.fetchAvailableFrame(null).asFrameValue();
+        frameValue.setValue(colorful);
+        outPort.pushFrame(frameValue);
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CropFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CropFilter.java
new file mode 100644
index 0000000..91fe21c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/CropFilter.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.transform;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.util.FloatMath;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.geometry.Quad;
+
+public class CropFilter extends Filter {
+
+    private Quad mCropRect = Quad.fromRect(0f, 0f, 1f, 1f);
+    private int mOutputWidth = 0;
+    private int mOutputHeight = 0;
+    private ImageShader mShader;
+    private boolean mUseMipmaps = false;
+    private FrameImage2D mPow2Frame = null;
+
+    public CropFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("cropRect", Signature.PORT_REQUIRED, FrameType.single(Quad.class))
+            .addInputPort("outputWidth", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("outputHeight", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class))
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("cropRect")) {
+            port.bindToFieldNamed("mCropRect");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("outputWidth")) {
+            port.bindToFieldNamed("mOutputWidth");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("outputHeight")) {
+            port.bindToFieldNamed("mOutputHeight");
+            port.setAutoPullEnabled(true);
+        } else  if (port.getName().equals("useMipmaps")) {
+            port.bindToFieldNamed("mUseMipmaps");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onPrepare() {
+        if (isOpenGLSupported()) {
+            mShader = ImageShader.createIdentity();
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("image");
+
+        // Pull input frame
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        int[] inDims = inputImage.getDimensions();
+        int[] croppedDims = { (int)FloatMath.ceil(mCropRect.xEdge().length() * inDims[0]),
+                              (int)FloatMath.ceil(mCropRect.yEdge().length() * inDims[1]) };
+        int[] outDims = { getOutputWidth(croppedDims[0], croppedDims[1]),
+                getOutputHeight(croppedDims[0], croppedDims[1]) };
+        FrameImage2D outputImage = outPort.fetchAvailableFrame(outDims).asFrameImage2D();
+
+        if (isOpenGLSupported()) {
+            FrameImage2D sourceFrame;
+            Quad sourceQuad = null;
+            boolean scaleDown = (outDims[0] < croppedDims[0]) || (outDims[1] < croppedDims[1]);
+            if (scaleDown && mUseMipmaps) {
+                mPow2Frame = TransformUtils.makeMipMappedFrame(mPow2Frame, croppedDims);
+                int[] extDims = mPow2Frame.getDimensions();
+                float targetWidth = croppedDims[0] / (float)extDims[0];
+                float targetHeight = croppedDims[1] / (float)extDims[1];
+                Quad targetQuad = Quad.fromRect(0f, 0f, targetWidth, targetHeight);
+                mShader.setSourceQuad(mCropRect);
+                mShader.setTargetQuad(targetQuad);
+                mShader.process(inputImage, mPow2Frame);
+                TransformUtils.generateMipMaps(mPow2Frame);
+                sourceFrame = mPow2Frame;
+                sourceQuad = targetQuad;
+            } else {
+                sourceFrame = inputImage;
+                sourceQuad = mCropRect;
+            }
+
+            mShader.setSourceQuad(sourceQuad);
+            mShader.setTargetRect(0f, 0f, 1f, 1f);
+            mShader.process(sourceFrame, outputImage);
+        } else {
+            // Convert quads to canvas coordinate space
+            Quad sourceQuad = mCropRect.scale2(inDims[0], inDims[1]);
+            Quad targetQuad = Quad.fromRect(0f, 0f, inDims[0], inDims[1]);
+
+            // Calculate transform for crop
+            Matrix transform = Quad.getTransform(sourceQuad, targetQuad);
+            transform.postScale(outDims[0] / (float)inDims[0], outDims[1] / (float)inDims[1]);
+
+            // Create target canvas
+            Bitmap.Config config = Bitmap.Config.ARGB_8888;
+            Bitmap cropped = Bitmap.createBitmap(outDims[0], outDims[1], config);
+            Canvas canvas = new Canvas(cropped);
+
+            // Draw source bitmap into target canvas
+            Paint paint = new Paint();
+            paint.setFilterBitmap(true);
+            Bitmap sourceBitmap = inputImage.toBitmap();
+            canvas.drawBitmap(sourceBitmap, transform, paint);
+
+            // Assign bitmap to output frame
+            outputImage.setBitmap(cropped);
+        }
+
+        outPort.pushFrame(outputImage);
+    }
+
+    @Override
+    protected void onClose() {
+        if (mPow2Frame != null){
+            mPow2Frame.release();
+            mPow2Frame = null;
+        }
+    }
+
+    protected int getOutputWidth(int inWidth, int inHeight) {
+        return mOutputWidth <= 0 ? inWidth : mOutputWidth;
+    }
+
+    protected int getOutputHeight(int inWidth, int inHeight) {
+        return mOutputHeight <= 0 ? inHeight : mOutputHeight;
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Filter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Filter.java
new file mode 100644
index 0000000..9e2eb92
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Filter.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Filters are the processing nodes of the filter graphs.
+ *
+ * Filters may have any number of input and output ports, through which the data frames flow.
+ * TODO: More documentation on filter life-cycle, port and type checking, GL and RenderScript, ...
+ */
+public abstract class Filter {
+
+    private static class State {
+        private static final int STATE_UNPREPARED = 1;
+        private static final int STATE_PREPARED = 2;
+        private static final int STATE_OPEN = 3;
+        private static final int STATE_CLOSED = 4;
+        private static final int STATE_DESTROYED = 5;
+
+        public int current = STATE_UNPREPARED;
+
+        public synchronized boolean check(int state) {
+            return current == state;
+        }
+
+    }
+
+    private final int REQUEST_FLAG_NONE = 0;
+    private final int REQUEST_FLAG_CLOSE = 1;
+
+    private String mName;
+    private MffContext mContext;
+    private FilterGraph mFilterGraph;
+
+    private State mState = new State();
+    private int mRequests = REQUEST_FLAG_NONE;
+
+    private int mMinimumAvailableInputs = 1;
+    private int mMinimumAvailableOutputs = 1;
+
+    private int mScheduleCount = 0;
+    private long mLastScheduleTime = 0;
+
+    private boolean mIsActive = true;
+    private AtomicBoolean mIsSleeping = new AtomicBoolean(false);
+
+    private long mCurrentTimestamp = Frame.TIMESTAMP_NOT_SET;
+
+    private HashMap<String, InputPort> mConnectedInputPorts = new HashMap<String, InputPort>();
+    private HashMap<String, OutputPort> mConnectedOutputPorts = new HashMap<String, OutputPort>();
+
+    private InputPort[] mConnectedInputPortArray = null;
+    private OutputPort[] mConnectedOutputPortArray = null;
+
+    private ArrayList<Frame> mAutoReleaseFrames = new ArrayList<Frame>();
+
+
+    /**
+     * Constructs a new filter.
+     * A filter is bound to a specific MffContext. Its name can be any String value, but it must
+     * be unique within the filter graph.
+     *
+     * Note that names starting with "$" are reserved for internal use, and should not be used.
+     *
+     * @param context The MffContext in which the filter will live.
+     * @param name The name of the filter.
+     */
+    protected Filter(MffContext context, String name) {
+        mName = name;
+        mContext = context;
+    }
+
+    /**
+     * Checks whether the filter class is available on this platform.
+     * Some filters may not be installed on all platforms and can therefore not be instantiated.
+     * Before instantiating a filter, check if it is available by using this method.
+     *
+     * This method uses the shared FilterFactory to check whether the filter class is available.
+     *
+     * @param filterClassName The fully qualified class name of the Filter class.
+     * @return true, if filters of the specified class name are available.
+     */
+    public static final boolean isAvailable(String filterClassName) {
+        return FilterFactory.sharedFactory().isFilterAvailable(filterClassName);
+    }
+
+    /**
+     * Returns the name of this filter.
+     *
+     * @return the name of the filter (specified during construction).
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the signature of this filter.
+     *
+     * Subclasses should override this and return their filter signature. The default
+     * implementation returns a generic signature with no constraints.
+     *
+     * This method may be called at any time.
+     *
+     * @return the Signature instance for this filter.
+     */
+    public Signature getSignature() {
+        return new Signature();
+    }
+
+    /**
+     * Returns the MffContext that the filter resides in.
+     *
+     * @return the MffContext of the filter.
+     */
+    public MffContext getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns true, if the filter is active.
+     * TODO: thread safety?
+     *
+     * @return true, if the filter is active.
+     */
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Activates the current filter.
+     * Only active filters can be scheduled for execution. This method can only be called if the
+     * GraphRunner that is executing the filter is stopped or paused.
+     */
+    public void activate() {
+        assertIsPaused();
+        if (!mIsActive) {
+            mIsActive = true;
+        }
+    }
+
+    /**
+     * Deactivates the current filter.
+     * Only active filters can be scheduled for execution. This method can only be called if the
+     * GraphRunner that is executing the filter is stopped or paused.
+     */
+    public void deactivate() {
+        // TODO: Support close-on-deactivate (must happen in processing thread).
+        assertIsPaused();
+        if (mIsActive) {
+            mIsActive = false;
+        }
+    }
+
+    /**
+     * Returns the filter's set of input ports.
+     * Note that this contains only the *connected* input ports. To retrieve all
+     * input ports that this filter accepts, one has to go via the filter's Signature.
+     *
+     * @return An array containing all connected input ports.
+     */
+    public final InputPort[] getConnectedInputPorts() {
+        return mConnectedInputPortArray;
+    }
+
+    /**
+     * Returns the filter's set of output ports.
+     * Note that this contains only the *connected* output ports. To retrieve all
+     * output ports that this filter provides, one has to go via the filter's Signature.
+     *
+     * @return An array containing all connected output ports.
+     */
+    public final OutputPort[] getConnectedOutputPorts() {
+        return mConnectedOutputPortArray;
+    }
+
+    /**
+     * Returns the input port with the given name.
+     * Note that this can only access the *connected* input ports. To retrieve all
+     * input ports that this filter accepts, one has to go via the filter's Signature.
+     *
+     * @return the input port with the specified name, or null if no connected input port
+     *  with this name exists.
+     */
+    public final InputPort getConnectedInputPort(String name) {
+        return mConnectedInputPorts.get(name);
+    }
+
+    /**
+     * Returns the output port with the given name.
+     * Note that this can only access the *connected* output ports. To retrieve all
+     * output ports that this filter provides, one has to go via the filter's Signature.
+     *
+     * @return the output port with the specified name, or null if no connected output port
+     *  with this name exists.
+     */
+    public final OutputPort getConnectedOutputPort(String name) {
+        return mConnectedOutputPorts.get(name);
+    }
+
+    /**
+     * Called when an input port has been attached in the graph.
+     * Override this method, in case you want to be informed of any connected input ports, or make
+     * modifications to them. Note that you may not assume that any other ports have been attached
+     * already. If you have dependencies on other ports, override
+     * {@link #onInputPortOpen(InputPort)}. The default implementation does nothing.
+     *
+     * @param port The InputPort instance that was attached.
+     */
+    protected void onInputPortAttached(InputPort port) {
+    }
+
+    /**
+     * Called when an output port has been attached in the graph.
+     * Override this method, in case you want to be informed of any connected output ports, or make
+     * modifications to them. Note that you may not assume that any other ports have been attached
+     * already. If you have dependencies on other ports, override
+     * {@link #onOutputPortOpen(OutputPort)}. The default implementation does nothing.
+     *
+     * @param port The OutputPort instance that was attached.
+     */
+    protected void onOutputPortAttached(OutputPort port) {
+    }
+
+    /**
+     * Called when an input port is opened on this filter.
+     * Input ports are opened by the data produce, that is the filter that is connected to an
+     * input port. Override this if you need to make modifications to the port before processing
+     * begins. Note, that this is only called if the connected filter is scheduled. You may assume
+     * that all ports are attached when this is called.
+     *
+     * @param port The InputPort instance that was opened.
+     */
+    protected void onInputPortOpen(InputPort port) {
+    }
+
+    /**
+     * Called when an output port is opened on this filter.
+     * Output ports are opened when the filter they are attached to is opened. Override this if you
+     * need to make modifications to the port before processing begins. Note, that this is only
+     * called if the filter is scheduled. You may assume that all ports are attached when this is
+     * called.
+     *
+     * @param port The OutputPort instance that was opened.
+     */
+    protected void onOutputPortOpen(OutputPort port) {
+    }
+
+    /**
+     * Returns true, if the filter is currently open.
+     * @return true, if the filter is currently open.
+     */
+    public final boolean isOpen() {
+        return mState.check(State.STATE_OPEN);
+    }
+
+    @Override
+    public String toString() {
+        return mName + " (" + getClass().getSimpleName() + ")";
+    }
+
+    /**
+     * Called when filter is prepared.
+     * Subclasses can override this to prepare the filter for processing. This method gets called
+     * once only just before the filter is scheduled for processing the first time.
+     *
+     * @see #onTearDown()
+     */
+    protected void onPrepare() {
+    }
+
+    /**
+     * Called when the filter is opened.
+     * Subclasses can override this to perform any kind of initialization just before processing
+     * starts. This method may be called any number of times, but is always balanced with an
+     * {@link #onClose()} call.
+     *
+     * @see #onClose()
+     */
+    protected void onOpen() {
+    }
+
+    /**
+     * Called to perform processing on Frame data.
+     * This is the only method subclasses must override. It is called every time the filter is
+     * ready for processing. Typically this is when there is input data to process and available
+     * output ports, but may differ depending on the port configuration.
+     */
+    protected abstract void onProcess();
+
+    /**
+     * Called when the filter is closed.
+     * Subclasses can override this to perform any kind of post-processing steps. Processing will
+     * not resume until {@link #onOpen()} is called again. This method is only called if the filter
+     * is open.
+     *
+     * @see #onOpen()
+     */
+    protected void onClose() {
+    }
+
+    /**
+     * Called when the filter is torn down.
+     * Subclasses can override this to perform clean-up tasks just before the filter is disposed of.
+     * It is called when the filter graph that the filter belongs to is disposed.
+     *
+     * @see #onPrepare()
+     */
+    protected void onTearDown() {
+    }
+
+    /**
+     * Check if the input conditions are met in order to schedule this filter.
+     *
+     * This is used by {@link #canSchedule()} to determine if the input-port conditions given by
+     * the filter are met. Subclasses that override scheduling behavior can make use of this
+     * function.
+     *
+     * @return true, if the filter's input conditions are met.
+     */
+    protected boolean inputConditionsMet() {
+        if (mConnectedInputPortArray.length > 0) {
+            int inputFrames = 0;
+            // [Non-iterator looping]
+            for (int i = 0; i < mConnectedInputPortArray.length; ++i) {
+                if (!mConnectedInputPortArray[i].conditionsMet()) {
+                    return false;
+                } else if (mConnectedInputPortArray[i].hasFrame()) {
+                    ++inputFrames;
+                }
+            }
+            if (inputFrames < mMinimumAvailableInputs) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check if the output conditions are met in order to schedule this filter.
+     *
+     * This is used by {@link #canSchedule()} to determine if the output-port conditions given by
+     * the filter are met. Subclasses that override scheduling behavior can make use of this
+     * function.
+     *
+     * @return true, if the filter's output conditions are met.
+     */
+    protected boolean outputConditionsMet() {
+        if (mConnectedOutputPortArray.length > 0) {
+            int availableOutputs = 0;
+            for (int i = 0; i < mConnectedOutputPortArray.length; ++i) {
+                if (!mConnectedOutputPortArray[i].conditionsMet()) {
+                    return false;
+                } else if (mConnectedOutputPortArray[i].isAvailable()) {
+                    ++availableOutputs;
+                }
+            }
+            if (availableOutputs < mMinimumAvailableOutputs) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check if the Filter is in a state so that it can be scheduled.
+     *
+     * When overriding the filter's {@link #canSchedule()} method, you should never allow
+     * scheduling a filter that is not in a schedulable state. This will result in undefined
+     * behavior.
+     *
+     * @return true, if the filter is in a schedulable state.
+     */
+    protected boolean inSchedulableState() {
+        return (mIsActive && !mState.check(State.STATE_CLOSED));
+    }
+
+    /**
+     * Returns true if the filter can be currently scheduled.
+     *
+     * Filters may override this method if they depend on custom factors that determine whether
+     * they can be scheduled or not. The scheduler calls this method to determine whether or not
+     * a filter can be scheduled for execution. It does not guarantee that it will be executed.
+     * It is strongly recommended to call super's implementation to make sure your filter can be
+     * scheduled based on its state, input and output ports.
+     *
+     * @return true, if the filter can be scheduled.
+     */
+    protected boolean canSchedule() {
+        return inSchedulableState() && inputConditionsMet() && outputConditionsMet();
+    }
+
+    /**
+     * Returns the current FrameManager instance.
+     * @return the current FrameManager instance or null if there is no FrameManager set up yet.
+     */
+    protected final FrameManager getFrameManager() {
+        return mFilterGraph.mRunner != null ? mFilterGraph.mRunner.getFrameManager() : null;
+    }
+
+    /**
+     * Returns whether the GraphRunner for this filter is running.
+     *
+     * Generally, this method should not be used for performing operations that need to be carried
+     * out before running begins. Use {@link #performPreparation(Runnable)} for this.
+     *
+     * @return true, if the GraphRunner for this filter is running.
+     */
+    protected final boolean isRunning() {
+        return mFilterGraph != null && mFilterGraph.mRunner != null
+                && mFilterGraph.mRunner.isRunning();
+    }
+
+    /**
+     * Performs operations before the filter is running.
+     *
+     * Use this method when your filter requires to perform operations while the graph is not
+     * running. The filter will not be scheduled for execution until your method has completed
+     * execution.
+     */
+    protected final boolean performPreparation(Runnable runnable) {
+        synchronized (mState) {
+            if (mState.current == State.STATE_OPEN) {
+                return false;
+            } else {
+                runnable.run();
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Request that this filter be closed after the current processing step.
+     *
+     * Implementations may call this within their {@link #onProcess()} calls to indicate that the
+     * filter is done processing and wishes to be closed. After such a request the filter will be
+     * closed and no longer receive {@link #onProcess()} calls.
+     *
+     * @see #onClose()
+     * @see #onProcess()
+     */
+    protected final void requestClose() {
+        mRequests |= REQUEST_FLAG_CLOSE;
+    }
+
+    /**
+     * Sets the minimum number of input frames required to process.
+     * A filter will not be scheduled unless at least a certain number of input frames are available
+     * on the input ports. This is only relevant if the filter has input ports and is not waiting on
+     * all ports.
+     * The default value is 1.
+     *
+     * @param count the minimum number of frames required to process.
+     * @see #getMinimumAvailableInputs()
+     * @see #setMinimumAvailableOutputs(int)
+     * @see InputPort#setWaitsForFrame(boolean)
+     */
+    protected final void setMinimumAvailableInputs(int count) {
+        mMinimumAvailableInputs = count;
+    }
+
+    /**
+     * Returns the minimum number of input frames required to process this filter.
+     * The default value is 1.
+     *
+     * @return the minimum number of input frames required to process.
+     * @see #setMinimumAvailableInputs(int)
+     */
+    protected final int getMinimumAvailableInputs() {
+        return mMinimumAvailableInputs;
+    }
+
+    /**
+     * Sets the minimum number of available output ports required to process.
+     * A filter will not be scheduled unless atleast a certain number of output ports are available.
+     * This is only relevant if the filter has output ports and is not waiting on all ports. The
+     * default value is 1.
+     *
+     * @param count the minimum number of frames required to process.
+     * @see #getMinimumAvailableOutputs()
+     * @see #setMinimumAvailableInputs(int)
+     * @see OutputPort#setWaitsUntilAvailable(boolean)
+     */
+    protected final void setMinimumAvailableOutputs(int count) {
+        mMinimumAvailableOutputs = count;
+    }
+
+    /**
+     * Returns the minimum number of available outputs required to process this filter.
+     * The default value is 1.
+     *
+     * @return the minimum number of available outputs required to process.
+     * @see #setMinimumAvailableOutputs(int)
+     */
+    protected final int getMinimumAvailableOutputs() {
+        return mMinimumAvailableOutputs;
+    }
+
+    /**
+     * Puts the filter to sleep so that it is no longer scheduled.
+     * To resume scheduling the filter another thread must call wakeUp() on this filter.
+     */
+    protected final void enterSleepState() {
+        mIsSleeping.set(true);
+    }
+
+    /**
+     * Wakes the filter and resumes scheduling.
+     * This is generally called from another thread to signal that this filter should resume
+     * processing. Does nothing if filter is not sleeping.
+     */
+    protected final void wakeUp() {
+        if (mIsSleeping.getAndSet(false)) {
+            if (isRunning()) {
+                mFilterGraph.mRunner.signalWakeUp();
+            }
+        }
+    }
+
+    /**
+     * Returns whether this Filter is allowed to use OpenGL.
+     *
+     * Filters may use OpenGL if the MffContext supports OpenGL and its GraphRunner allows it.
+     *
+     * @return true, if this Filter is allowed to use OpenGL.
+     */
+   protected final boolean isOpenGLSupported() {
+        return mFilterGraph.mRunner.isOpenGLSupported();
+    }
+
+    /**
+     * Connect an output port to an input port of another filter.
+     * Connects the output port with the specified name to the input port with the specified name
+     * of the specified filter. If the input or output ports do not exist already, they are
+     * automatically created and added to the respective filter.
+     */
+    final void connect(String outputName, Filter targetFilter, String inputName) {
+        // Make sure not connected already
+        if (getConnectedOutputPort(outputName) != null) {
+            throw new RuntimeException("Attempting to connect already connected output port '"
+                + outputName + "' of filter " + this + "'!");
+        } else if (targetFilter.getConnectedInputPort(inputName) != null) {
+            throw new RuntimeException("Attempting to connect already connected input port '"
+                + inputName + "' of filter " + targetFilter + "'!");
+        }
+
+        // Establish connection
+        InputPort inputPort = targetFilter.newInputPort(inputName);
+        OutputPort outputPort = newOutputPort(outputName);
+        outputPort.setTarget(inputPort);
+
+        // Fire attachment callbacks
+        targetFilter.onInputPortAttached(inputPort);
+        onOutputPortAttached(outputPort);
+
+        // Update array of ports (which is maintained for more efficient access)
+        updatePortArrays();
+    }
+
+    final Map<String, InputPort> getConnectedInputPortMap() {
+        return mConnectedInputPorts;
+    }
+
+    final Map<String, OutputPort> getConnectedOutputPortMap() {
+        return mConnectedOutputPorts;
+    }
+
+    final void execute() {
+        synchronized (mState) {
+            autoPullInputs();
+            mLastScheduleTime = SystemClock.elapsedRealtime();
+            if (mState.current == State.STATE_UNPREPARED) {
+                onPrepare();
+                mState.current = State.STATE_PREPARED;
+            }
+            if (mState.current == State.STATE_PREPARED) {
+                openPorts();
+                onOpen();
+                mState.current = State.STATE_OPEN;
+            }
+            if (mState.current == State.STATE_OPEN) {
+                onProcess();
+                if (mRequests != REQUEST_FLAG_NONE) {
+                    processRequests();
+                }
+            }
+        }
+        autoReleaseFrames();
+        ++mScheduleCount;
+    }
+
+    final void performClose() {
+        synchronized (mState) {
+            if (mState.current == State.STATE_OPEN) {
+                onClose();
+                mIsSleeping.set(false);
+                mState.current = State.STATE_CLOSED;
+                mCurrentTimestamp = Frame.TIMESTAMP_NOT_SET;
+            }
+        }
+    }
+
+    final void softReset() {
+        synchronized (mState) {
+            performClose();
+            if (mState.current == State.STATE_CLOSED) {
+                mState.current = State.STATE_PREPARED;
+            }
+        }
+    }
+
+    final void performTearDown() {
+        synchronized (mState) {
+            if (mState.current == State.STATE_OPEN) {
+                throw new RuntimeException("Attempting to tear-down filter " + this + " which is "
+                    + "in an open state!");
+            } else if (mState.current != State.STATE_DESTROYED
+                    && mState.current != State.STATE_UNPREPARED) {
+                onTearDown();
+                mState.current = State.STATE_DESTROYED;
+            }
+        }
+    }
+
+    final void insertIntoFilterGraph(FilterGraph graph) {
+        mFilterGraph = graph;
+        updatePortArrays();
+    }
+
+    final int getScheduleCount() {
+        return mScheduleCount;
+    }
+
+    final void resetScheduleCount() {
+        mScheduleCount = 0;
+    }
+
+    final void openPorts() {
+        // Opening the output ports will open the connected input ports
+        for (OutputPort outputPort : mConnectedOutputPorts.values()) {
+            openOutputPort(outputPort);
+        }
+    }
+
+    final void addAutoReleaseFrame(Frame frame) {
+        mAutoReleaseFrames.add(frame);
+    }
+
+    final long getCurrentTimestamp() {
+        return mCurrentTimestamp;
+    }
+
+    final void onPulledFrameWithTimestamp(long timestamp) {
+        if (timestamp > mCurrentTimestamp || mCurrentTimestamp == Frame.TIMESTAMP_NOT_SET) {
+            mCurrentTimestamp = timestamp;
+        }
+    }
+
+    final void openOutputPort(OutputPort outPort) {
+        if (outPort.getQueue() == null) {
+            try {
+                FrameQueue.Builder builder = new FrameQueue.Builder();
+                InputPort inPort = outPort.getTarget();
+                outPort.onOpen(builder);
+                inPort.onOpen(builder);
+                Filter targetFilter = inPort.getFilter();
+                String queueName = mName + "[" + outPort.getName() + "] -> " + targetFilter.mName
+                        + "[" + inPort.getName() + "]";
+                FrameQueue queue = builder.build(queueName);
+                outPort.setQueue(queue);
+                inPort.setQueue(queue);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Could not open output port " + outPort + "!", e);
+            }
+        }
+    }
+
+    final boolean isSleeping() {
+        return mIsSleeping.get();
+    }
+
+    final long getLastScheduleTime() {
+        return mLastScheduleTime ;
+    }
+
+    private final void autoPullInputs() {
+        // [Non-iterator looping]
+        for (int i = 0; i < mConnectedInputPortArray.length; ++i) {
+            InputPort port = mConnectedInputPortArray[i];
+            if (port.hasFrame() && port.isAutoPullEnabled()) {
+                mConnectedInputPortArray[i].pullFrame();
+            }
+        }
+    }
+
+    private final void autoReleaseFrames() {
+        // [Non-iterator looping]
+        for (int i = 0; i < mAutoReleaseFrames.size(); ++i) {
+            mAutoReleaseFrames.get(i).release();
+        }
+        mAutoReleaseFrames.clear();
+    }
+
+    private final InputPort newInputPort(String name) {
+        InputPort result = mConnectedInputPorts.get(name);
+        if (result == null) {
+            Signature.PortInfo info = getSignature().getInputPortInfo(name);
+            result = new InputPort(this, name, info);
+            mConnectedInputPorts.put(name, result);
+        }
+        return result;
+    }
+
+    private final OutputPort newOutputPort(String name) {
+        OutputPort result = mConnectedOutputPorts.get(name);
+        if (result == null) {
+            Signature.PortInfo info = getSignature().getOutputPortInfo(name);
+            result = new OutputPort(this, name, info);
+            mConnectedOutputPorts.put(name, result);
+        }
+        return result;
+    }
+
+    private final void processRequests() {
+        if ((mRequests & REQUEST_FLAG_CLOSE) != 0) {
+            performClose();
+            mRequests = REQUEST_FLAG_NONE;
+        }
+    }
+
+    private void assertIsPaused() {
+        GraphRunner runner = GraphRunner.current();
+        if (runner != null && !runner.isPaused() && !runner.isStopped()) {
+            throw new RuntimeException("Attempting to modify filter state while runner is "
+                + "executing. Please pause or stop the runner first!");
+        }
+    }
+
+    private final void updatePortArrays() {
+        // Copy our port-maps to arrays for faster non-iterator access
+        mConnectedInputPortArray = mConnectedInputPorts.values().toArray(new InputPort[0]);
+        mConnectedOutputPortArray = mConnectedOutputPorts.values().toArray(new OutputPort[0]);
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterFactory.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterFactory.java
new file mode 100644
index 0000000..2c67c79
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterFactory.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.util.Log;
+
+import dalvik.system.PathClassLoader;
+
+import java.lang.reflect.Constructor;
+import java.util.HashSet;
+
+public class FilterFactory {
+
+    private static FilterFactory mSharedFactory;
+    private HashSet<String> mPackages = new HashSet<String>();
+
+    private static ClassLoader mCurrentClassLoader;
+    private static HashSet<String> mLibraries;
+    private static Object mClassLoaderGuard;
+
+    static {
+        mCurrentClassLoader = Thread.currentThread().getContextClassLoader();
+        mLibraries = new HashSet<String>();
+        mClassLoaderGuard = new Object();
+    }
+
+    private static final String TAG = "FilterFactory";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    public static FilterFactory sharedFactory() {
+        if (mSharedFactory == null) {
+            mSharedFactory = new FilterFactory();
+        }
+        return mSharedFactory;
+    }
+
+    /**
+     * Adds a new Java library to the list to be scanned for filters.
+     * libraryPath must be an absolute path of the jar file.  This needs to be
+     * static because only one classloader per process can open a shared native
+     * library, which a filter may well have.
+     */
+    public static void addFilterLibrary(String libraryPath) {
+        if (mLogVerbose) Log.v(TAG, "Adding filter library " + libraryPath);
+        synchronized(mClassLoaderGuard) {
+            if (mLibraries.contains(libraryPath)) {
+                if (mLogVerbose) Log.v(TAG, "Library already added");
+                return;
+            }
+            mLibraries.add(libraryPath);
+            // Chain another path loader to the current chain
+            mCurrentClassLoader = new PathClassLoader(libraryPath, mCurrentClassLoader);
+        }
+    }
+
+    public void addPackage(String packageName) {
+        if (mLogVerbose) Log.v(TAG, "Adding package " + packageName);
+        /* TODO: This should use a getPackage call in the caller's context, but no such method
+                 exists.
+        Package pkg = Package.getPackage(packageName);
+        if (pkg == null) {
+            throw new IllegalArgumentException("Unknown filter package '" + packageName + "'!");
+        }
+        */
+        mPackages.add(packageName);
+    }
+
+    public boolean isFilterAvailable(String className) {
+        return getFilterClass(className) != null;
+    }
+
+    public Filter createFilterByClassName(String className, String filterName, MffContext context) {
+        if (mLogVerbose) Log.v(TAG, "Looking up class " + className);
+        Class<? extends Filter> filterClass = getFilterClass(className);
+        if (filterClass == null) {
+            throw new IllegalArgumentException("Unknown filter class '" + className + "'!");
+        }
+        return createFilterByClass(filterClass, filterName, context);
+    }
+
+    public Filter createFilterByClass(Class<? extends Filter> filterClass,
+            String filterName, MffContext context) {
+        // Look for the correct constructor
+        Constructor<? extends Filter> filterConstructor = null;
+        try {
+            filterConstructor = filterClass.getConstructor(MffContext.class, String.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("The filter class '" + filterClass
+                + "' does not have a constructor of the form <init>(MffContext, String)!");
+        }
+
+        // Construct the filter
+        Filter filter = null;
+        try {
+            filter = filterConstructor.newInstance(context, filterName);
+        } catch (Throwable t) {
+            throw new RuntimeException("Error creating filter " + filterName + "!", t);
+        }
+
+        if (filter == null) {
+            throw new IllegalArgumentException("Could not construct the filter '"
+                + filterName + "'!");
+        }
+        return filter;
+    }
+
+    private Class<? extends Filter> getFilterClass(String name) {
+        Class<?> filterClass = null;
+
+        // Look for the class in the imported packages
+        for (String packageName : mPackages) {
+            try {
+                if (mLogVerbose) Log.v(TAG, "Trying "+ packageName + "." + name);
+                synchronized(mClassLoaderGuard) {
+                    filterClass = mCurrentClassLoader.loadClass(packageName + "." + name);
+                }
+            } catch (ClassNotFoundException e) {
+                continue;
+            }
+            // Exit loop if class was found.
+            if (filterClass != null) {
+                break;
+            }
+        }
+        Class<? extends Filter> result = null;
+        try {
+            if (filterClass != null) {
+                result = filterClass.asSubclass(Filter.class);
+            }
+        } catch (ClassCastException e) {
+            // Leave result == null
+        }
+        return result;
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterGraph.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterGraph.java
new file mode 100644
index 0000000..7d5ed9f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FilterGraph.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.util.Log;
+import android.view.View;
+import androidx.media.filterpacks.base.BranchFilter;
+import androidx.media.filterpacks.base.FrameSlotSource;
+import androidx.media.filterpacks.base.FrameSlotTarget;
+import androidx.media.filterpacks.base.GraphInputSource;
+import androidx.media.filterpacks.base.GraphOutputTarget;
+import androidx.media.filterpacks.base.ValueTarget;
+import androidx.media.filterpacks.base.ValueTarget.ValueListener;
+import androidx.media.filterpacks.base.VariableSource;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A graph of Filter nodes.
+ *
+ * A FilterGraph instance contains a set of Filter instances connected by their output and input
+ * ports. Every filter belongs to exactly one graph and cannot be moved to another graph.
+ *
+ * FilterGraphs may contain sub-graphs that are dependent on the parent graph. These are typically
+ * used when inserting sub-graphs into MetaFilters. When a parent graph is torn down so are its
+ * sub-graphs. The same applies to flushing frames of a graph.
+ */
+public class FilterGraph {
+
+    private final static boolean DEBUG = false;
+
+    /** The context that this graph lives in */
+    private MffContext mContext;
+
+    /** Map from name of filter to the filter instance */
+    private HashMap<String, Filter> mFilterMap = new HashMap<String, Filter>();
+
+    /** Allows quick access to array of all filters. */
+    private Filter[] mAllFilters = null;
+
+    /** The GraphRunner currently attached to this graph */
+    GraphRunner mRunner;
+
+    /** The set of sub-graphs of this graph */
+    HashSet<FilterGraph> mSubGraphs = new HashSet<FilterGraph>();
+
+    /** The parent graph of this graph, or null it this graph is a root graph. */
+    private FilterGraph mParentGraph;
+
+    public static class Builder {
+
+        /** The context that this builder lives in */
+        private MffContext mContext;
+
+        /** Map from name of filter to the filter instance */
+        private HashMap<String, Filter> mFilterMap = new HashMap<String, Filter>();
+
+        /**
+         * Creates a new builder for specifying a graph structure.
+         * @param context The context the graph will live in.
+         */
+        public Builder(MffContext context) {
+            mContext = context;
+        }
+
+        /**
+         * Add a filter to the graph.
+         *
+         * Adds the specified filter to the set of filters of this graph. The filter must not be in
+         * the graph already, and the filter's name must be unique within the graph.
+         *
+         * @param filter the filter to add to the graph.
+         * @throws IllegalArgumentException if the filter is in the graph already, or its name is
+         *                                  is already taken.
+         */
+        public void addFilter(Filter filter) {
+            if (mFilterMap.values().contains(filter)) {
+                throw new IllegalArgumentException("Attempting to add filter " + filter + " that "
+                    + "is in the graph already!");
+            } else if (mFilterMap.containsKey(filter.getName())) {
+                throw new IllegalArgumentException("Graph contains filter with name '"
+                    + filter.getName() + "' already!");
+            } else {
+                mFilterMap.put(filter.getName(), filter);
+            }
+        }
+
+        /**
+         * Adds a variable to the graph.
+         *
+         * TODO: More documentation.
+         *
+         * @param name the name of the variable.
+         * @param value the value of the variable or null if no value is to be set yet.
+         * @return the VariableSource filter that holds the value of this variable.
+         */
+        public VariableSource addVariable(String name, Object value) {
+            if (getFilter(name) != null) {
+                throw new IllegalArgumentException("Filter named '" + name + "' exists already!");
+            }
+            VariableSource valueSource = new VariableSource(mContext, name);
+            addFilter(valueSource);
+            if (value != null) {
+                valueSource.setValue(value);
+            }
+            return valueSource;
+        }
+
+        public FrameSlotSource addFrameSlotSource(String name, String slotName) {
+            FrameSlotSource filter = new FrameSlotSource(mContext, name, slotName);
+            addFilter(filter);
+            return filter;
+        }
+
+        public FrameSlotTarget addFrameSlotTarget(String name, String slotName) {
+            FrameSlotTarget filter = new FrameSlotTarget(mContext, name, slotName);
+            addFilter(filter);
+            return filter;
+        }
+
+        /**
+         * Connect two filters by their ports.
+         * The filters specified must have been previously added to the graph builder.
+         *
+         * @param sourceFilterName The name of the source filter.
+         * @param sourcePort The name of the source port.
+         * @param targetFilterName The name of the target filter.
+         * @param targetPort The name of the target port.
+         */
+        public void connect(String sourceFilterName, String sourcePort,
+                            String targetFilterName, String targetPort) {
+            Filter sourceFilter = getFilter(sourceFilterName);
+            Filter targetFilter = getFilter(targetFilterName);
+            if (sourceFilter == null) {
+                throw new IllegalArgumentException("Unknown filter '" + sourceFilterName + "'!");
+            } else if (targetFilter == null) {
+                throw new IllegalArgumentException("Unknown filter '" + targetFilterName + "'!");
+            }
+            connect(sourceFilter, sourcePort, targetFilter, targetPort);
+        }
+
+        /**
+         * Connect two filters by their ports.
+         * The filters specified must have been previously added to the graph builder.
+         *
+         * @param sourceFilter The source filter.
+         * @param sourcePort The name of the source port.
+         * @param targetFilter The target filter.
+         * @param targetPort The name of the target port.
+         */
+        public void connect(Filter sourceFilter, String sourcePort,
+                            Filter targetFilter, String targetPort) {
+            sourceFilter.connect(sourcePort, targetFilter, targetPort);
+        }
+
+        /**
+         * Returns the filter with the specified name.
+         *
+         * @return the filter with the specified name, or null if no such filter exists.
+         */
+        public Filter getFilter(String name) {
+            return mFilterMap.get(name);
+        }
+
+        /**
+         * Builds the graph and checks signatures.
+         *
+         * @return The new graph instance.
+         */
+        public FilterGraph build() {
+            checkSignatures();
+            return buildWithParent(null);
+        }
+
+        /**
+         * Builds the sub-graph and checks signatures.
+         *
+         * @param parentGraph the parent graph of the built sub-graph.
+         * @return The new graph instance.
+         */
+        public FilterGraph buildSubGraph(FilterGraph parentGraph) {
+            if (parentGraph == null) {
+                throw new NullPointerException("Parent graph must be non-null!");
+            }
+            checkSignatures();
+            return buildWithParent(parentGraph);
+        }
+
+        VariableSource assignValueToFilterInput(Object value, String filterName, String inputName) {
+            // Get filter to connect to
+            Filter filter = getFilter(filterName);
+            if (filter == null) {
+                throw new IllegalArgumentException("Unknown filter '" + filterName + "'!");
+            }
+
+            // Construct a name for our value source and make sure it does not exist already
+            String valueSourceName = filterName + "." + inputName;
+            if (getFilter(valueSourceName) != null) {
+                throw new IllegalArgumentException("VariableSource for '" + filterName + "' and "
+                    + "input '" + inputName + "' exists already!");
+            }
+
+            // Create new VariableSource and connect it to the target filter and port
+            VariableSource valueSource = new VariableSource(mContext, valueSourceName);
+            addFilter(valueSource);
+            try {
+                ((Filter)valueSource).connect("value", filter, inputName);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Could not connect VariableSource to input '" + inputName
+                    + "' of filter '" + filterName + "'!", e);
+            }
+
+            // Assign the value to the VariableSource
+            if (value != null) {
+                valueSource.setValue(value);
+            }
+
+            return valueSource;
+        }
+
+        VariableSource assignVariableToFilterInput(String varName,
+                                                   String filterName,
+                                                   String inputName) {
+            // Get filter to connect to
+            Filter filter = getFilter(filterName);
+            if (filter == null) {
+                throw new IllegalArgumentException("Unknown filter '" + filterName + "'!");
+            }
+
+            // Get variable
+            Filter variable = getFilter(varName);
+            if (variable == null || !(variable instanceof VariableSource)) {
+                throw new IllegalArgumentException("Unknown variable '" + varName + "'!");
+            }
+
+            // Connect variable (and possibly branch) variable to filter
+            try {
+                connectAndBranch(variable, "value", filter, inputName);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Could not connect VariableSource to input '" + inputName
+                    + "' of filter '" + filterName + "'!", e);
+            }
+
+            return (VariableSource)variable;
+        }
+
+        /**
+         * Builds the graph without checking signatures.
+         * If parent is non-null, build a sub-graph of the specified parent.
+         *
+         * @return The new graph instance.
+         */
+        private FilterGraph buildWithParent(FilterGraph parent) {
+            FilterGraph graph = new FilterGraph(mContext, parent);
+            graph.mFilterMap = mFilterMap;
+            graph.mAllFilters = mFilterMap.values().toArray(new Filter[0]);
+            for (Entry<String, Filter> filterEntry : mFilterMap.entrySet()) {
+                filterEntry.getValue().insertIntoFilterGraph(graph);
+            }
+            return graph;
+        }
+
+        private void checkSignatures() {
+            checkSignaturesForFilters(mFilterMap.values());
+        }
+
+        // TODO: Currently this always branches even if the connection is a 1:1 connection. Later
+        // we may optimize to pass through directly in the 1:1 case (may require disconnecting
+        // ports).
+        private void connectAndBranch(Filter sourceFilter,
+                                      String sourcePort,
+                                      Filter targetFilter,
+                                      String targetPort) {
+            String branchName = "__" + sourceFilter.getName() + "_" + sourcePort + "Branch";
+            Filter branch = getFilter(branchName);
+            if (branch == null) {
+                branch = new BranchFilter(mContext, branchName, false);
+                addFilter(branch);
+                sourceFilter.connect(sourcePort, branch, "input");
+            }
+            String portName = "to" + targetFilter.getName() + "_" + targetPort;
+            branch.connect(portName, targetFilter, targetPort);
+        }
+
+    }
+
+    /**
+     * Attach the graph and its subgraphs to a custom GraphRunner.
+     *
+     * Call this if you want the graph to be executed by a specific GraphRunner. You must call
+     * this before any other runner is set. Note that calls to {@code getRunner()} and
+     * {@code run()} auto-create a GraphRunner.
+     *
+     * @param runner The GraphRunner instance that should execute this graph.
+     * @see #getRunner()
+     * @see #run()
+     */
+    public void attachToRunner(GraphRunner runner) {
+        if (mRunner == null) {
+            for (FilterGraph subGraph : mSubGraphs) {
+                subGraph.attachToRunner(runner);
+            }
+            runner.attachGraph(this);
+            mRunner = runner;
+        } else if (mRunner != runner) {
+            throw new RuntimeException("Cannot attach FilterGraph to GraphRunner that is already "
+                + "attached to another GraphRunner!");
+        }
+    }
+
+    /**
+     * Forcibly tear down a filter graph.
+     *
+     * Call this to release any resources associated with the filter graph, its filters and any of
+     * its sub-graphs. This method must not be called if the graph (or any sub-graph) is running.
+     *
+     * You may no longer access this graph instance or any of its subgraphs after calling this
+     * method.
+     *
+     * Tearing down of sub-graphs is not supported. You must tear down the root graph, which will
+     * tear down all of its sub-graphs.
+     *
+     * @throws IllegalStateException if the graph is still running.
+     * @throws RuntimeException if you attempt to tear down a sub-graph.
+     */
+    public void tearDown() {
+        assertNotRunning();
+        if (mParentGraph != null) {
+            throw new RuntimeException("Attempting to tear down sub-graph!");
+        }
+        if (mRunner != null) {
+            mRunner.tearDownGraph(this);
+        }
+        for (FilterGraph subGraph : mSubGraphs) {
+            subGraph.mParentGraph = null;
+            subGraph.tearDown();
+        }
+        mSubGraphs.clear();
+    }
+
+    /**
+     * Returns the context of the graph.
+     *
+     * @return the MffContext instance that this graph is bound to.
+     */
+    public MffContext getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the filter with the specified name.
+     *
+     * @return the filter with the specified name, or null if no such filter exists.
+     */
+    public Filter getFilter(String name) {
+        return mFilterMap.get(name);
+    }
+
+    /**
+     * Returns the VariableSource for the specified variable.
+     *
+     * TODO: More documentation.
+     * TODO: More specialized error handling.
+     *
+     * @param name The name of the VariableSource.
+     * @return The VariableSource filter instance with the specified name.
+     */
+    public VariableSource getVariable(String name) {
+        Filter result = mFilterMap.get(name);
+        if (result != null && result instanceof VariableSource) {
+            return (VariableSource)result;
+        } else {
+            throw new IllegalArgumentException("Unknown variable '" + name + "' specified!");
+        }
+    }
+
+    /**
+     * Returns the GraphOutputTarget with the specified name.
+     *
+     * @param name The name of the target.
+     * @return The GraphOutputTarget instance with the specified name.
+     */
+    public GraphOutputTarget getGraphOutput(String name) {
+        Filter result = mFilterMap.get(name);
+        if (result != null && result instanceof GraphOutputTarget) {
+            return (GraphOutputTarget)result;
+        } else {
+            throw new IllegalArgumentException("Unknown target '" + name + "' specified!");
+        }
+    }
+
+    /**
+     * Returns the GraphInputSource with the specified name.
+     *
+     * @param name The name of the source.
+     * @return The GraphInputSource instance with the specified name.
+     */
+    public GraphInputSource getGraphInput(String name) {
+        Filter result = mFilterMap.get(name);
+        if (result != null && result instanceof GraphInputSource) {
+            return (GraphInputSource)result;
+        } else {
+            throw new IllegalArgumentException("Unknown source '" + name + "' specified!");
+        }
+    }
+
+    /**
+     * Binds a filter to a view.
+     *
+     * ViewFilter instances support visualizing their data to a view. See the specific filter
+     * documentation for details. Views may be bound only if the graph is not running.
+     *
+     * @param filterName the name of the filter to bind.
+     * @param view the view to bind to.
+     * @throws IllegalStateException if the filter is in an illegal state.
+     * @throws IllegalArgumentException if no such view-filter exists.
+     */
+    public void bindFilterToView(String filterName, View view) {
+        Filter filter = mFilterMap.get(filterName);
+        if (filter != null && filter instanceof ViewFilter) {
+            ((ViewFilter)filter).bindToView(view);
+        } else {
+            throw new IllegalArgumentException("Unknown view filter '" + filterName + "'!");
+        }
+    }
+
+    /**
+     * TODO: Documentation.
+     */
+    public void bindValueTarget(String filterName, ValueListener listener, boolean onCallerThread) {
+        Filter filter = mFilterMap.get(filterName);
+        if (filter != null && filter instanceof ValueTarget) {
+            ((ValueTarget)filter).setListener(listener, onCallerThread);
+        } else {
+            throw new IllegalArgumentException("Unknown ValueTarget filter '" + filterName + "'!");
+        }
+    }
+
+    // Running Graphs //////////////////////////////////////////////////////////////////////////////
+    /**
+     * Convenience method to run the graph.
+     *
+     * Creates a new runner for this graph in the specified mode and executes it. Returns the
+     * runner to allow control of execution.
+     *
+     * @throws IllegalStateException if the graph is already running.
+     * @return the GraphRunner instance that was used for execution.
+     */
+    public GraphRunner run() {
+        GraphRunner runner = getRunner();
+        runner.setIsVerbose(false);
+        runner.start(this);
+        return runner;
+    }
+
+    /**
+     * Returns the GraphRunner for this graph.
+     *
+     * Every FilterGraph instance has a GraphRunner instance associated with it for executing the
+     * graph.
+     *
+     * @return the GraphRunner instance for this graph.
+     */
+    public GraphRunner getRunner() {
+        if (mRunner == null) {
+            GraphRunner runner = new GraphRunner(mContext);
+            attachToRunner(runner);
+        }
+        return mRunner;
+    }
+
+    /**
+     * Returns whether the graph is currently running.
+     *
+     * @return true if the graph is currently running.
+     */
+    public boolean isRunning() {
+        return mRunner != null && mRunner.isRunning();
+    }
+
+    /**
+     * Check each filter's signatures if all requirements are fulfilled.
+     *
+     * This will throw a RuntimeException if any unfulfilled requirements are found.
+     * Note that FilterGraph.Builder also has a function checkSignatures(), which allows
+     * to do the same /before/ the FilterGraph is built.
+     */
+    public void checkSignatures() {
+        checkSignaturesForFilters(mFilterMap.values());
+    }
+
+    // MFF Internal Methods ////////////////////////////////////////////////////////////////////////
+    Filter[] getAllFilters() {
+        return mAllFilters;
+    }
+
+    static void checkSignaturesForFilters(Collection<Filter> filters) {
+        for (Filter filter : filters) {
+            if (DEBUG) {
+                Log.d("FilterGraph", "Checking filter " + filter.getName() + "...");
+            }
+            Signature signature = filter.getSignature();
+            signature.checkInputPortsConform(filter);
+            signature.checkOutputPortsConform(filter);
+        }
+    }
+
+    /**
+     * Wipes the filter references in this graph, so that they may be collected.
+     *
+     * This must be called only after a tearDown as this will make the FilterGraph invalid.
+     */
+    void wipe() {
+        mAllFilters = null;
+        mFilterMap = null;
+    }
+
+    void flushFrames() {
+        for (Filter filter : mFilterMap.values()) {
+            for (InputPort inputPort : filter.getConnectedInputPorts()) {
+                inputPort.clear();
+            }
+            for (OutputPort outputPort : filter.getConnectedOutputPorts()) {
+                outputPort.clear();
+            }
+        }
+    }
+
+    Set<FilterGraph> getSubGraphs() {
+        return mSubGraphs;
+    }
+
+    // Internal Methods ////////////////////////////////////////////////////////////////////////////
+    private FilterGraph(MffContext context, FilterGraph parentGraph) {
+        mContext = context;
+        mContext.addGraph(this);
+        if (parentGraph != null) {
+            mParentGraph = parentGraph;
+            mParentGraph.mSubGraphs.add(this);
+        }
+    }
+
+    private void assertNotRunning() {
+        if (isRunning()) {
+            throw new IllegalStateException("Attempting to modify running graph!");
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Frame.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Frame.java
new file mode 100644
index 0000000..67907d3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Frame.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.util.Arrays;
+
+/**
+ * Frames are the data containers that are transported between Filters.
+ *
+ * Frames may be used only within a Filter during filter graph execution. Accessing Frames outside
+ * of graph execution may cause unexpected results.
+ *
+ * There are two ways to obtain new Frame instances. You can call
+ * {@link OutputPort#fetchAvailableFrame(int[])} on an OutputPort to obtain a Frame to pass to an
+ * output. You can also call {@link #create(FrameType, int[])} to obtain
+ * a detached Frame instance that you may hold onto in your filter. If you need to hold on to a
+ * Frame that is owned by an input or output queue, you must call
+ * {@link #retain()} on it.
+ *
+ * When you are done using a detached Frame, you must release it yourself.
+ *
+ * To access frame data, call any of the {@code lock}-methods. This will give you access to the
+ * frame data in the desired format. You must pass in a {@code mode} indicating whether you wish
+ * to read or write to the data. Writing to a read-locked Frame may produce unexpected results and
+ * interfere with other filters. When you are done reading or writing to the data, you must call
+ * {@link #unlock()}. Note, that a Frame must be unlocked before you push it into an output queue.
+ *
+ * Generally, any type of access format to a Frame's data will be granted. However, it is strongly
+ * recommended to specify the access format that you intend to use in your filter's signature or
+ * in the access flags passed to {@code newFrame()}. This will allow the Frame to allocate
+ * the most efficient backings for the intended type of access.
+ *
+ * A frame can be be pushed to an OutputPort by calling the {@link OutputPort#pushFrame(Frame)}
+ * method. Frames that have been pushed become read-only, and can no longer be modified.
+ *
+ * On the other end, a Filter can pull in an input Frame by calling {@link InputPort#pullFrame()}
+ * on the desired InputPort. Such frames are always read-only.
+ */
+public class Frame {
+
+    /** Special timestamp value indicating that no time-stamp was set. */
+    public static final long TIMESTAMP_NOT_SET = -1;
+
+    /** Frame data access mode: Read */
+    public static final int MODE_READ = 1;
+    /** Frame data access mode: Write */
+    public static final int MODE_WRITE = 2;
+
+    BackingStore mBackingStore;
+    boolean mReadOnly = false;
+
+    // Public API //////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Returns the frame's type.
+     * @return A FrameType instance describing the frame data-type.
+     */
+    public final FrameType getType() {
+        return mBackingStore.getFrameType();
+    }
+
+    public final int getElementCount() {
+        return mBackingStore.getElementCount();
+    }
+
+    /**
+     * Set the frame's timestamp in nanoseconds.
+     *
+     * @param timestamp the timestamp of this frame in nanoseconds.
+     */
+    public final void setTimestamp(long timestamp) {
+        mBackingStore.setTimestamp(timestamp);
+    }
+
+    /**
+     * @return the frame's timestamp in nanoseconds.
+     */
+    public final long getTimestamp() {
+        return mBackingStore.getTimestamp();
+    }
+
+    /**
+     * @return the frame's timestamp in milliseconds.
+     */
+    public final long getTimestampMillis() {
+        return mBackingStore.getTimestamp() / 1000000L;
+    }
+
+    public final boolean isReadOnly() {
+        return mReadOnly;
+    }
+
+    public final FrameValue asFrameValue() {
+        return FrameValue.create(mBackingStore);
+    }
+
+    public final FrameValues asFrameValues() {
+        return FrameValues.create(mBackingStore);
+    }
+
+    public final FrameBuffer1D asFrameBuffer1D() {
+        return FrameBuffer1D.create(mBackingStore);
+    }
+
+    public final FrameBuffer2D asFrameBuffer2D() {
+        return FrameBuffer2D.create(mBackingStore);
+    }
+
+    public final FrameImage2D asFrameImage2D() {
+        return FrameImage2D.create(mBackingStore);
+    }
+
+    @Override
+    public String toString() {
+        return "Frame[" + getType().toString() + "]: " + mBackingStore;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        return object instanceof Frame && ((Frame)object).mBackingStore == mBackingStore;
+    }
+
+    public static Frame create(FrameType type, int[] dimensions) {
+        FrameManager manager = FrameManager.current();
+        if (manager == null) {
+            throw new IllegalStateException("Attempting to create new Frame outside of "
+                + "FrameManager context!");
+        }
+        return new Frame(type, dimensions, manager);
+    }
+
+    public final Frame release() {
+        mBackingStore = mBackingStore.release();
+        return mBackingStore != null ? this : null;
+    }
+
+    public final Frame retain() {
+        mBackingStore = mBackingStore.retain();
+        return this;
+    }
+
+    public void unlock() {
+        if (!mBackingStore.unlock()) {
+            throw new RuntimeException("Attempting to unlock frame that is not locked!");
+        }
+    }
+
+    public int[] getDimensions() {
+        int[] dim = mBackingStore.getDimensions();
+        return dim != null ? Arrays.copyOf(dim, dim.length) : null;
+    }
+
+    Frame(FrameType type, int[] dimensions, FrameManager manager) {
+        mBackingStore = new BackingStore(type, dimensions, manager);
+    }
+
+    Frame(BackingStore backingStore) {
+        mBackingStore = backingStore;
+    }
+
+    final void assertAccessible(int mode) {
+        // Make sure frame is in write-mode
+        if (mReadOnly && mode == MODE_WRITE) {
+            throw new RuntimeException("Attempting to write to read-only frame " + this + "!");
+        }
+    }
+
+    final void setReadOnly(boolean readOnly) {
+        mReadOnly = readOnly;
+    }
+
+    void resize(int[] newDims) {
+        int[] oldDims = mBackingStore.getDimensions();
+        int oldCount = oldDims == null ? 0 : oldDims.length;
+        int newCount = newDims == null ? 0 : newDims.length;
+        if (oldCount != newCount) {
+            throw new IllegalArgumentException("Cannot resize " + oldCount + "-dimensional "
+                + "Frame to " + newCount + "-dimensional Frame!");
+        } else if (newDims != null && !Arrays.equals(oldDims, newDims)) {
+            mBackingStore.resize(newDims);
+        }
+    }
+
+    Frame makeCpuCopy(FrameManager frameManager) {
+        Frame frame = new Frame(getType(), getDimensions(), frameManager);
+        frame.mBackingStore.importStore(mBackingStore);
+        return frame;
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java
new file mode 100644
index 0000000..0e24f5b
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.annotation.TargetApi;
+import android.renderscript.Allocation;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class FrameBuffer1D extends Frame {
+
+    private int mLength = 0;
+
+    /**
+     * Access frame's data using a {@link ByteBuffer}.
+     * This is a convenience method and is equivalent to calling {@code lockData} with an
+     * {@code accessFormat} of {@code ACCESS_BYTES}.
+     * When writing to the {@link ByteBuffer}, the byte order should be always set to
+     * {@link ByteOrder#nativeOrder()}.
+     *
+     * @return The byte buffer instance holding the Frame's data.
+     */
+    public ByteBuffer lockBytes(int mode) {
+        assertAccessible(mode);
+        return (ByteBuffer)mBackingStore.lockData(mode, BackingStore.ACCESS_BYTES);
+    }
+
+    /**
+     * Access frame's data using a RenderScript {@link Allocation}.
+     * This is a convenience method and is equivalent to calling {@code lockData} with an
+     * {@code accessFormat} of {@code ACCESS_ALLOCATION}.
+     *
+     * @return The Allocation instance holding the Frame's data.
+     */
+    @TargetApi(11)
+    public Allocation lockAllocation(int mode) {
+        assertAccessible(mode);
+        return (Allocation) mBackingStore.lockData(mode, BackingStore.ACCESS_ALLOCATION);
+    }
+
+    public int getLength() {
+        return mLength;
+    }
+
+    @Override
+    public int[] getDimensions() {
+        return super.getDimensions();
+    }
+
+    /**
+     * TODO: Documentation. Note that frame contents are invalidated.
+     */
+    @Override
+    public void resize(int[] newDimensions) {
+        super.resize(newDimensions);
+    }
+
+    static FrameBuffer1D create(BackingStore backingStore) {
+        assertCanCreate(backingStore);
+        return new FrameBuffer1D(backingStore);
+    }
+
+    FrameBuffer1D(BackingStore backingStore) {
+        super(backingStore);
+        updateLength(backingStore.getDimensions());
+    }
+
+    static void assertCanCreate(BackingStore backingStore) {
+        FrameType type = backingStore.getFrameType();
+        if (type.getElementSize() == 0) {
+            throw new RuntimeException("Cannot access Frame of type " + type + " as a FrameBuffer "
+                + "instance!");
+        }
+        int[] dims = backingStore.getDimensions();
+        if (dims == null || dims.length == 0) {
+            throw new RuntimeException("Cannot access Frame with no dimensions as a FrameBuffer "
+                + "instance!");
+        }
+    }
+
+    void updateLength(int[] dimensions) {
+        mLength = 1;
+        for (int dim : dimensions) {
+            mLength *= dim;
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer2D.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer2D.java
new file mode 100644
index 0000000..6a7f12a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer2D.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+public class FrameBuffer2D extends FrameBuffer1D {
+
+    public int getWidth() {
+        return mBackingStore.getDimensions()[0];
+    }
+
+    public int getHeight() {
+        return mBackingStore.getDimensions()[1];
+    }
+
+    static FrameBuffer2D create(BackingStore backingStore) {
+        assertCanCreate(backingStore);
+        return new FrameBuffer2D(backingStore);
+    }
+
+    FrameBuffer2D(BackingStore backingStore) {
+        super(backingStore);
+    }
+
+    static void assertCanCreate(BackingStore backingStore) {
+        FrameBuffer1D.assertCanCreate(backingStore);
+        int[] dimensions = backingStore.getDimensions();
+        int dimCount = dimensions != null ? dimensions.length : 0;
+        if (dimCount != 2) {
+            throw new RuntimeException("Cannot access " + dimCount + "-dimensional Frame as a "
+                + "FrameBuffer2D instance!");
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameImage2D.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameImage2D.java
new file mode 100644
index 0000000..bca94f7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameImage2D.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import androidx.media.filterfw.BackingStore.Backing;
+
+public class FrameImage2D extends FrameBuffer2D {
+
+    /**
+     * Access frame's data using a TextureSource.
+     * This is a convenience method and is equivalent to calling {@code lockData} with an
+     * {@code accessFormat} of {@code ACCESS_TEXTURE}.
+     *
+     * @return The TextureSource instance holding the Frame's data.
+     */
+    public TextureSource lockTextureSource() {
+        return (TextureSource)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_TEXTURE);
+    }
+
+    /**
+     * Access frame's data using a RenderTarget.
+     * This is a convenience method and is equivalent to calling {@code lockData} with an
+     * {@code accessFormat} of {@code ACCESS_RENDERTARGET}.
+     *
+     * @return The RenderTarget instance holding the Frame's data.
+     */
+    public RenderTarget lockRenderTarget() {
+        return (RenderTarget)mBackingStore.lockData(MODE_WRITE, BackingStore.ACCESS_RENDERTARGET);
+    }
+
+    /**
+     * Assigns the pixel data of the specified bitmap.
+     *
+     * The RGBA pixel data will be extracted from the bitmap and assigned to the frame data. Note,
+     * that the colors are premultiplied with the alpha channel. If you wish to have
+     * non-premultiplied colors, you must pass the Frame through an
+     * {@code UnpremultiplyAlphaFilter}.
+     *
+     * @param bitmap The bitmap pixels to assign.
+     */
+    public void setBitmap(Bitmap bitmap) {
+        bitmap = convertToFrameType(bitmap, mBackingStore.getFrameType());
+        validateBitmapSize(bitmap, mBackingStore.getDimensions());
+        Backing backing = mBackingStore.lockBacking(MODE_WRITE, BackingStore.ACCESS_BITMAP);
+        backing.setData(bitmap);
+        mBackingStore.unlock();
+    }
+
+    /**
+     * Returns the RGBA image contents as a Bitmap instance.
+     *
+     * @return a Bitmap instance holding the RGBA Frame image content.
+     */
+    public Bitmap toBitmap() {
+        Bitmap result = (Bitmap)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_BITMAP);
+        mBackingStore.unlock();
+        return result;
+    }
+
+    /**
+     * Copies the image data from one frame to another.
+     *
+     * The source and target rectangles must be given in normalized coordinates, where 0,0 is the
+     * top-left of the image and 1,1 is the bottom-right.
+     *
+     * If the target rectangle is smaller than the target frame, the pixel values outside of the
+     * target rectangle are undefined.
+     *
+     * This method must be called within a Filter during execution. It supports both GL-enabled
+     * and GL-disabled run contexts.
+     *
+     * @param target The target frame to copy to.
+     * @param sourceRect The source rectangle in normalized coordinates.
+     * @param targetRect The target rectangle in normalized coordinates.
+     */
+    public void copyToFrame(FrameImage2D target, RectF sourceRect, RectF targetRect) {
+        if (GraphRunner.current().isOpenGLSupported()) {
+            gpuImageCopy(this, target, sourceRect, targetRect);
+        } else {
+            cpuImageCopy(this, target, sourceRect, targetRect);
+        }
+    }
+
+    static FrameImage2D create(BackingStore backingStore) {
+        assertCanCreate(backingStore);
+        return new FrameImage2D(backingStore);
+    }
+
+    FrameImage2D(BackingStore backingStore) {
+        super(backingStore);
+    }
+
+    static void assertCanCreate(BackingStore backingStore) {
+        FrameBuffer2D.assertCanCreate(backingStore);
+    }
+
+    private static Bitmap convertToFrameType(Bitmap bitmap, FrameType type) {
+        Bitmap.Config config = bitmap.getConfig();
+        Bitmap result = bitmap;
+        switch(type.getElementId()) {
+            case FrameType.ELEMENT_RGBA8888:
+                if (config != Bitmap.Config.ARGB_8888) {
+                    result = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+                    if (result == null) {
+                        throw new RuntimeException("Could not convert bitmap to frame-type " +
+                                "RGBA8888!");
+                    }
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported frame type '" + type + "' for " +
+                        "bitmap assignment!");
+        }
+        return result;
+    }
+
+    private void validateBitmapSize(Bitmap bitmap, int[] dimensions) {
+        if (bitmap.getWidth() != dimensions[0] || bitmap.getHeight() != dimensions[1]) {
+            throw new IllegalArgumentException("Cannot assign bitmap of size " + bitmap.getWidth()
+                    + "x" + bitmap.getHeight() + " to frame of size " + dimensions[0] + "x"
+                    + dimensions[1] + "!");
+        }
+    }
+
+    private static void gpuImageCopy(
+            FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
+        ImageShader idShader = RenderTarget.currentTarget().getIdentityShader();
+        // We briefly modify the shader
+        // TODO: Implement a safer way to save and restore a shared shader.
+        idShader.setSourceRect(srcRect);
+        idShader.setTargetRect(dstRect);
+        idShader.process(srcImage, dstImage);
+        // And reset it as others may use it as well
+        idShader.setSourceRect(0f, 0f, 1f, 1f);
+        idShader.setTargetRect(0f, 0f, 1f, 1f);
+    }
+
+    private static void cpuImageCopy(
+            FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
+        // Convert rectangles to integer rectangles in image dimensions
+        Rect srcIRect = new Rect((int) srcRect.left * srcImage.getWidth(),
+                (int) srcRect.top * srcImage.getHeight(),
+                (int) srcRect.right * srcImage.getWidth(),
+                (int) srcRect.bottom * srcImage.getHeight());
+        Rect dstIRect = new Rect((int) dstRect.left * srcImage.getWidth(),
+                (int) dstRect.top * srcImage.getHeight(),
+                (int) dstRect.right * srcImage.getWidth(),
+                (int) dstRect.bottom * srcImage.getHeight());
+
+        // Create target canvas
+        Bitmap.Config config = Bitmap.Config.ARGB_8888;
+        Bitmap dstBitmap = Bitmap.createBitmap(dstImage.getWidth(), dstImage.getHeight(), config);
+        Canvas canvas = new Canvas(dstBitmap);
+
+        // Draw source bitmap into target canvas
+        Paint paint = new Paint();
+        paint.setFilterBitmap(true);
+        Bitmap srcBitmap = srcImage.toBitmap();
+        canvas.drawBitmap(srcBitmap, srcIRect, dstIRect, paint);
+
+        // Assign bitmap to output frame
+        dstImage.setBitmap(dstBitmap);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameManager.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameManager.java
new file mode 100644
index 0000000..55ed277
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameManager.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import androidx.media.filterfw.BackingStore.Backing;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+/**
+ * The FrameManager tracks, caches, allocates and deallocates frame data.
+ * All Frame instances are managed by a FrameManager, and belong to exactly one of these. Frames
+ * cannot be shared across FrameManager instances, however multiple MffContexts may use the same
+ * FrameManager.
+ *
+ * Additionally, frame managers allow attaching Frames under a specified key. This allows decoupling
+ * filter-graphs by instructing one node to attach a frame under a specific key, and another to
+ * fetch the frame under the same key.
+ */
+public class FrameManager {
+
+    /** The default max cache size is set to 12 MB */
+    public final static int DEFAULT_MAX_CACHE_SIZE = 12 * 1024 * 1024;
+
+    /** Frame caching policy: No caching */
+    public final static int FRAME_CACHE_NONE = 0;
+    /** Frame caching policy: Drop least recently used frame buffers */
+    public final static int FRAME_CACHE_LRU = 1;
+    /** Frame caching policy: Drop least frequently used frame buffers */
+    public final static int FRAME_CACHE_LFU = 2;
+
+    /** Slot Flag: No flags set */
+    public final static int SLOT_FLAGS_NONE = 0x00;
+    /** Slot Flag: Sticky flag set: Frame will remain in slot after fetch. */
+    public final static int SLOT_FLAG_STICKY = 0x01;
+
+    private GraphRunner mRunner;
+    private Set<Backing> mBackings = new HashSet<Backing>();
+    private BackingCache mCache;
+
+    private Map<String, FrameSlot> mFrameSlots = new HashMap<String, FrameSlot>();
+
+    static class FrameSlot {
+        private FrameType mType;
+        private int mFlags;
+        private Frame mFrame = null;
+
+        public FrameSlot(FrameType type, int flags) {
+            mType = type;
+            mFlags = flags;
+        }
+
+        public FrameType getType() {
+            return mType;
+        }
+
+        public boolean hasFrame() {
+            return mFrame != null;
+        }
+
+        public void releaseFrame() {
+            if (mFrame != null) {
+                mFrame.release();
+                mFrame = null;
+            }
+        }
+
+        // TODO: Type check
+        public void assignFrame(Frame frame) {
+            Frame oldFrame = mFrame;
+            mFrame = frame.retain();
+            if (oldFrame != null) {
+                oldFrame.release();
+            }
+        }
+
+        public Frame getFrame() {
+            Frame result = mFrame.retain();
+            if ((mFlags & SLOT_FLAG_STICKY) == 0) {
+                releaseFrame();
+            }
+            return result;
+        }
+
+        public void markWritable() {
+            if (mFrame != null) {
+                mFrame.setReadOnly(false);
+            }
+        }
+    }
+
+    private static abstract class BackingCache {
+
+        protected int mCacheMaxSize = DEFAULT_MAX_CACHE_SIZE;
+
+        public abstract Backing fetchBacking(int mode, int access, int[] dimensions, int elemSize);
+
+        public abstract boolean cacheBacking(Backing backing);
+
+        public abstract void clear();
+
+        public abstract int getSizeLeft();
+
+        public void setSize(int size) {
+            mCacheMaxSize = size;
+        }
+
+        public int getSize() {
+            return mCacheMaxSize;
+        }
+    }
+
+    private static class BackingCacheNone extends BackingCache {
+
+        @Override
+        public Backing fetchBacking(int mode, int access, int[] dimensions, int elemSize) {
+            return null;
+        }
+
+        @Override
+        public boolean cacheBacking(Backing backing) {
+            return false;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public int getSize() {
+            return 0;
+        }
+
+        @Override
+        public int getSizeLeft() {
+            return 0;
+        }
+    }
+
+    private static abstract class PriorityBackingCache extends BackingCache {
+        private int mSize = 0;
+        private PriorityQueue<Backing> mQueue;
+
+        public PriorityBackingCache() {
+            mQueue = new PriorityQueue<Backing>(4, new Comparator<Backing>() {
+                @Override
+                public int compare(Backing left, Backing right) {
+                    return left.cachePriority - right.cachePriority;
+                }
+            });
+        }
+
+        @Override
+        public Backing fetchBacking(int mode, int access, int[] dimensions, int elemSize) {
+            for (Backing backing : mQueue) {
+                int backingAccess = (mode == Frame.MODE_WRITE)
+                    ? backing.writeAccess()
+                    : backing.readAccess();
+                if ((backingAccess & access) == access
+                    && dimensionsCompatible(backing.getDimensions(), dimensions)
+                    && (elemSize == backing.getElementSize())) {
+                    mQueue.remove(backing);
+                    mSize -= backing.getSize();
+                    onFetchBacking(backing);
+                    return backing;
+                }
+            }
+            //Log.w("FrameManager", "Could not find backing for dimensions " + Arrays.toString(dimensions));
+            return null;
+        }
+
+        @Override
+        public boolean cacheBacking(Backing backing) {
+            if (reserve(backing.getSize())) {
+                onCacheBacking(backing);
+                mQueue.add(backing);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void clear() {
+            mQueue.clear();
+            mSize = 0;
+        }
+
+        @Override
+        public int getSizeLeft() {
+            return mCacheMaxSize - mSize;
+        }
+
+        protected abstract void onCacheBacking(Backing backing);
+
+        protected abstract void onFetchBacking(Backing backing);
+
+        private boolean reserve(int size) {
+            //Log.i("FM", "Reserving " + size + " bytes (max: " + mCacheMaxSize + " bytes).");
+            //Log.i("FM", "Current size " + mSize);
+            if (size > mCacheMaxSize) {
+                return false;
+            }
+            mSize += size;
+            while (mSize > mCacheMaxSize) {
+                Backing dropped = mQueue.poll();
+                mSize -= dropped.getSize();
+                //Log.i("FM", "Dropping  " + dropped + " with priority "
+                //    + dropped.cachePriority + ". New size: " + mSize + "!");
+                dropped.destroy();
+            }
+            return true;
+        }
+
+
+    }
+
+    private static class BackingCacheLru extends PriorityBackingCache {
+        private int mTimestamp = 0;
+
+        @Override
+        protected void onCacheBacking(Backing backing) {
+            backing.cachePriority = 0;
+        }
+
+        @Override
+        protected void onFetchBacking(Backing backing) {
+            ++mTimestamp;
+            backing.cachePriority = mTimestamp;
+        }
+    }
+
+    private static class BackingCacheLfu extends PriorityBackingCache {
+        @Override
+        protected void onCacheBacking(Backing backing) {
+            backing.cachePriority = 0;
+        }
+
+        @Override
+        protected void onFetchBacking(Backing backing) {
+            ++backing.cachePriority;
+        }
+    }
+
+    public static FrameManager current() {
+        GraphRunner runner = GraphRunner.current();
+        return runner != null ? runner.getFrameManager() : null;
+    }
+
+    /**
+     * Returns the context that the FrameManager is bound to.
+     *
+     * @return the MffContext instance that the FrameManager is bound to.
+     */
+    public MffContext getContext() {
+        return mRunner.getContext();
+    }
+
+    /**
+     * Returns the GraphRunner that the FrameManager is bound to.
+     *
+     * @return the GraphRunner instance that the FrameManager is bound to.
+     */
+    public GraphRunner getRunner() {
+        return mRunner;
+    }
+
+    /**
+     * Sets the size of the cache.
+     *
+     * Resizes the cache to the specified size in bytes.
+     *
+     * @param bytes the new size in bytes.
+     */
+    public void setCacheSize(int bytes) {
+        mCache.setSize(bytes);
+    }
+
+    /**
+     * Returns the size of the cache.
+     *
+     * @return the size of the cache in bytes.
+     */
+    public int getCacheSize() {
+        return mCache.getSize();
+    }
+
+    /**
+     * Imports a frame from another FrameManager.
+     *
+     * This will return a frame with the contents of the given frame for use in this FrameManager.
+     * Note, that there is a substantial cost involved in moving a Frame from one FrameManager to
+     * another. This may be called from any thread. After the frame has been imported, it may be
+     * used in the runner that uses this FrameManager. As the new frame may share data with the
+     * provided frame, that frame must be read-only.
+     *
+     * @param frame The frame to import
+     */
+    public Frame importFrame(Frame frame) {
+        if (!frame.isReadOnly()) {
+            throw new IllegalArgumentException("Frame " + frame + " must be read-only to import "
+                    + "into another FrameManager!");
+        }
+        return frame.makeCpuCopy(this);
+    }
+
+    /**
+     * Adds a new frame slot to the frame manager.
+     * Filters can reference frame slots to pass frames between graphs or runs. If the name
+     * specified here is already taken the frame slot is overwritten. You can only
+     * modify frame-slots while no graph of the frame manager is running.
+     *
+     * @param name The name of the slot.
+     * @param type The type of Frame that will be assigned to this slot.
+     * @param flags A mask of {@code SLOT} flags.
+     */
+    public void addFrameSlot(String name, FrameType type, int flags) {
+        assertNotRunning();
+        FrameSlot oldSlot = mFrameSlots.get(name);
+        if (oldSlot != null) {
+            removeFrameSlot(name);
+        }
+        FrameSlot slot = new FrameSlot(type, flags);
+        mFrameSlots.put(name, slot);
+    }
+
+    /**
+     * Removes a frame slot from the frame manager.
+     * Any frame within the slot is released. You can only modify frame-slots while no graph
+     * of the frame manager is running.
+     *
+     * @param name The name of the slot
+     * @throws IllegalArgumentException if no such slot exists.
+     */
+    public void removeFrameSlot(String name) {
+        assertNotRunning();
+        FrameSlot slot = getSlot(name);
+        slot.releaseFrame();
+        mFrameSlots.remove(slot);
+    }
+
+    /**
+     * TODO: Document!
+     */
+    public void storeFrame(Frame frame, String slotName) {
+        assertInGraphRun();
+        getSlot(slotName).assignFrame(frame);
+    }
+
+    /**
+     * TODO: Document!
+     */
+    public Frame fetchFrame(String slotName) {
+        assertInGraphRun();
+        return getSlot(slotName).getFrame();
+    }
+
+    /**
+     * Clears the Frame cache.
+     */
+    public void clearCache() {
+        mCache.clear();
+    }
+
+    /**
+     * Create a new FrameManager instance.
+     *
+     * Creates a new FrameManager instance in the specified context and employing a cache with the
+     * specified cache type (see the cache type constants defined by the FrameManager class).
+     *
+     * @param runner the GraphRunner to bind the FrameManager to.
+     * @param cacheType the type of cache to use.
+     */
+    FrameManager(GraphRunner runner, int cacheType) {
+        mRunner = runner;
+        switch (cacheType) {
+            case FRAME_CACHE_NONE:
+                mCache = new BackingCacheNone();
+                break;
+            case FRAME_CACHE_LRU:
+                mCache = new BackingCacheLru();
+                break;
+            case FRAME_CACHE_LFU:
+                mCache = new BackingCacheLfu();
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown cache-type " + cacheType + "!");
+        }
+    }
+
+    Backing fetchBacking(int mode, int access, int[] dimensions, int elemSize) {
+        return mCache.fetchBacking(mode, access, dimensions, elemSize);
+    }
+
+    void onBackingCreated(Backing backing) {
+        if (backing != null) {
+            mBackings.add(backing);
+            // Log.i("FrameManager", "RM: Now have " + mBackings.size() + " backings");
+        }
+    }
+
+    void onBackingAvailable(Backing backing) {
+        if (!backing.shouldCache() || !mCache.cacheBacking(backing)) {
+            backing.destroy();
+            mBackings.remove(backing);
+            //Log.i("FrameManager", "RM: Now have " + mBackings.size() + " backings (" + mCache.getSizeLeft() + ")");
+        }
+    }
+
+    /**
+     * Destroying all references makes any Frames that contain them invalid.
+     */
+    void destroyBackings() {
+        for (Backing backing : mBackings) {
+            backing.destroy();
+        }
+        mBackings.clear();
+        mCache.clear();
+    }
+
+    FrameSlot getSlot(String name) {
+        FrameSlot slot = mFrameSlots.get(name);
+        if (slot == null) {
+            throw new IllegalArgumentException("Unknown frame slot '" + name + "'!");
+        }
+        return slot;
+    }
+
+    void onBeginRun() {
+        for (FrameSlot slot : mFrameSlots.values()) {
+            slot.markWritable();
+        }
+    }
+
+    // Internals ///////////////////////////////////////////////////////////////////////////////////
+    private static boolean dimensionsCompatible(int[] dimA, int[] dimB) {
+        return dimA == null || dimB == null || Arrays.equals(dimA, dimB);
+    }
+
+    private void assertNotRunning() {
+        if (mRunner.isRunning()) {
+            throw new IllegalStateException("Attempting to modify FrameManager while graph is "
+                + "running!");
+        }
+    }
+
+    private void assertInGraphRun() {
+        if (!mRunner.isRunning() || GraphRunner.current() != mRunner) {
+            throw new IllegalStateException("Attempting to access FrameManager Frame data "
+                + "outside of graph run-loop!");
+        }
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameQueue.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameQueue.java
new file mode 100644
index 0000000..c26f937
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameQueue.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.util.Vector;
+
+class FrameQueue {
+
+    public static class Builder {
+
+        private FrameType mReadType = null;
+        private FrameType mWriteType = null;
+
+        private Vector<FrameQueue> mAttachedQueues = new Vector<FrameQueue>();
+
+        public Builder() {}
+
+        public void setWriteType(FrameType type) {
+            mWriteType = type;
+        }
+
+        public void setReadType(FrameType type) {
+            mReadType = type;
+        }
+
+        public void attachQueue(FrameQueue queue) {
+            mAttachedQueues.add(queue);
+        }
+
+        public FrameQueue build(String name) {
+            FrameType type = buildType();
+            // TODO: This currently does not work correctly (Try camera -> branch -> target-slot)
+            //validateType(type, name);
+            FrameQueue result = new FrameQueue(type, name);
+            buildQueueImpl(result);
+            return result;
+        }
+
+        private void buildQueueImpl(FrameQueue queue) {
+            QueueImpl queueImpl = queue.new SingleFrameQueueImpl();
+            queue.mQueueImpl = queueImpl;
+        }
+
+        private FrameType buildType() {
+            FrameType result = FrameType.merge(mWriteType, mReadType);
+            for (FrameQueue queue : mAttachedQueues) {
+                result = FrameType.merge(result, queue.mType);
+            }
+            return result;
+        }
+
+        /*
+        private void validateType(FrameType type, String queueName) {
+            if (!type.isSpecified()) {
+                throw new RuntimeException("Cannot build connection queue '" + queueName + "' as "
+                        + "its type (" + type + ") is underspecified!");
+            }
+        }
+         */
+    }
+
+    private interface QueueImpl {
+        public boolean canPull();
+
+        public boolean canPush();
+
+        public Frame pullFrame();
+
+        public Frame fetchAvailableFrame(int[] dimensions);
+
+        public Frame peek();
+
+        public void pushFrame(Frame frame);
+
+        public void clear();
+    }
+
+    private class SingleFrameQueueImpl implements QueueImpl {
+        private Frame mFrame = null;
+
+        @Override
+        public boolean canPull() {
+            return mFrame != null;
+        }
+
+        @Override
+        public boolean canPush() {
+            return mFrame == null;
+        }
+
+        @Override
+        public Frame pullFrame() {
+            Frame result = mFrame;
+            mFrame = null;
+            return result;
+        }
+
+        @Override
+        public Frame peek() {
+            return mFrame;
+        }
+
+        @Override
+        public Frame fetchAvailableFrame(int[] dimensions) {
+            // Note that we cannot use a cached frame here, as we do not know where that cached
+            // instance would end up.
+            FrameManager manager = FrameManager.current();
+            return new Frame(mType, dimensions, manager);
+        }
+
+        @Override
+        public void pushFrame(Frame frame) {
+            mFrame = frame.retain();
+            mFrame.setReadOnly(true);
+        }
+
+        @Override
+        public void clear() {
+            if (mFrame != null) {
+                mFrame.release();
+                mFrame = null;
+            }
+        }
+    }
+
+    private QueueImpl mQueueImpl;
+    private FrameType mType;
+    private String mName;
+
+    public FrameType getType() {
+        return mType;
+    }
+
+    public boolean canPull() {
+        return mQueueImpl.canPull();
+    }
+
+    public boolean canPush() {
+        return mQueueImpl.canPush();
+    }
+
+    public Frame pullFrame() {
+        return mQueueImpl.pullFrame();
+    }
+
+    public Frame fetchAvailableFrame(int[] dimensions) {
+        return mQueueImpl.fetchAvailableFrame(dimensions);
+    }
+
+    public void pushFrame(Frame frame) {
+        mQueueImpl.pushFrame(frame);
+    }
+
+    public Frame peek() {
+        return mQueueImpl.peek();
+    }
+
+    @Override
+    public String toString() {
+        return mName;
+    }
+
+    public void clear() {
+        mQueueImpl.clear();
+    }
+
+    private FrameQueue(FrameType type, String name) {
+        mType = type;
+        mName = name;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotSource.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotSource.java
new file mode 100644
index 0000000..0a093f9
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotSource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.*;
+
+public final class FrameSlotSource extends SlotFilter {
+
+    public FrameSlotSource(MffContext context, String name, String slotName) {
+        super(context, name, slotName);
+    }
+
+    @Override
+    public Signature getSignature() {
+        // TODO: It would be nice if we could return the slot type here. Not currently possible
+        // as getSignature() is typically called before a FrameManager and its slots are setup.
+        return new Signature()
+            .addOutputPort("frame", Signature.PORT_REQUIRED, FrameType.any())
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected boolean canSchedule() {
+        return super.canSchedule() && slotHasFrame();
+    }
+
+    @Override
+    protected void onProcess() {
+        Frame frame = getFrameManager().fetchFrame(mSlotName);
+        getConnectedOutputPort("frame").pushFrame(frame);
+        frame.release();
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotTarget.java
new file mode 100644
index 0000000..55648c6
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameSlotTarget.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.*;
+
+public final class FrameSlotTarget extends SlotFilter {
+
+    public FrameSlotTarget(MffContext context, String name, String slotName) {
+        super(context, name, slotName);
+    }
+
+    @Override
+    public Signature getSignature() {
+        // TODO: It would be nice if we could return the slot type here. Not currently possible
+        // as getSignature() is typically called before a FrameManager and its slots are setup.
+        return new Signature()
+            .addInputPort("frame", Signature.PORT_REQUIRED, FrameType.any())
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        Frame frame = getConnectedInputPort("frame").pullFrame();
+        getFrameManager().storeFrame(frame, mSlotName);
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameType.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameType.java
new file mode 100644
index 0000000..bfa4018
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameType.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+
+/**
+ * A FrameType instance specifies the data format of a Frame.
+ *
+ * FrameTypes are used mainly by Filters to specify the data type they intend to consume or produce.
+ * When filters are connected, their FrameType information is analyzed and checked for
+ * compatibility. This allows Filter writers to assume a certain data input type. It also helps
+ * filter-graph designers determine which filters can be hooked up to one another.
+ *
+ * A FrameType generally consists of an element type and number of dimensions. The currently
+ * supported element types are:
+ *
+ * <ul>
+ * <li>int8, int16, int32, in64</li>
+ * <li>float32, float64</li>
+ * <li>rgba8888</li>
+ * <li>object</li>
+ * <li>don't-care</li>
+ * </ul>
+ *
+ * If the object element type is used, class information may be appended to the FrameType to
+ * indicate what class of objects are expected. When constructing an object based FrameType, you
+ * have the option of either specifying a type that represents a single object of that class, or
+ * an array of objects (see the {@link #single()} and {@link #array()} constructors). A single
+ * object has a dimensionality of 0, while an array has a dimensionality of 1.
+ *
+ * When constructing a non-object type, you have the option of creating a 1D or 2D buffer, or
+ * a 2D image (see the {@link #buffer1D(int)}, {@link #buffer2D(int)}, and
+ * {@link #image2D(int, int)} constructors). To optimize access, provide access hints when making
+ * an image type.
+ *
+ * Finally, it is possible to create a wild-card type with the {@link #any()} constructor. This
+ * type matches any other type. Note, that this is a more general type than a {@code single(Object)}
+ * type that matches only object-base types (of any Object subclass). You may also specify the
+ * leave the element of any type unspecified by using the {@code ELEMENT_DONTCARE} constant.
+ *
+ * When a graph is connected the types between outputs and inputs are merged to a queue-type. All
+ * Frames in this queue will be of that type. In order for a merge to succeed the following
+ * conditions must hold:
+ *
+ * <ul>
+ * <li>The element types must be identical.</li>
+ * <li>The dimensions must match (except for singles and arrays, see below).</li>
+ * <li>For object-based types: The classes must be compatible.</li>
+ * <li>If one of the types is a wild-card, both types are always compatible.</li>
+ * </ul>
+ *
+ * Class compatibility is determined in an optimistic fashion, i.e. one class must be the subclass
+ * of the other. It does not matter which of the types is the subclass of the other. For instance,
+ * if one Filter outputs a type of class {@code Object}, and the consumer expects a Filter of type
+ * {@code Bitmap}, the connection is considered compatible. (Of course if at runtime a non-Bitmap
+ * object is produced, this will cause a runtime exception to be thrown).
+ *
+ * For convenience, single and array object-based types are compatible with one another. This
+ * in turn means that Frames with a single object can be accessed as an array with a single entry,
+ * and array based Frames can be accessed as a single object of the array class. For this reason
+ * you should prefer consuming objects as array types (if it makes sense for that specific port),
+ * as this will allow your Filter to handle multiple objects in one Frame while not giving up the
+ * possibility to deal with singles.
+ * TODO: This needs to be reworked. An array(int) should not be interchangeable with a single(int),
+ * but rather with a single(int[]). Use ArraySelectFilter for the former!
+ *
+ * After the types are merged, the queue-type must be a fully specified type. This means that the
+ * type must have its element and dimensions specified. This ensures that filters that need to
+ * query their input or output types receive meaningful information.
+ */
+public final class FrameType {
+
+    public final static int ELEMENT_DONTCARE = 0;
+    public final static int ELEMENT_OBJECT = 1;
+
+    public final static int ELEMENT_INT8 = 100;
+    public final static int ELEMENT_INT16 = 101;
+    public final static int ELEMENT_INT32 = 102;
+    public final static int ELEMENT_INT64 = 103;
+
+    public final static int ELEMENT_FLOAT32 = 200;
+    public final static int ELEMENT_FLOAT64 = 201;
+
+    public final static int ELEMENT_RGBA8888 = 301;
+
+    public final static int READ_CPU = 0x01;
+    public final static int READ_GPU = 0x02;
+    public final static int READ_ALLOCATION = 0x04;
+    public final static int WRITE_CPU = 0x08;
+    public final static int WRITE_GPU = 0x10;
+    public final static int WRITE_ALLOCATION = 0x20;
+
+    private final static int ACCESS_UNKNOWN = 0x00;
+
+    private final int mElementId;
+    private final int mDimensions;
+    private final int mAccessHints;
+    private final Class<?> mClass;
+
+    private static SimpleCache<String, FrameType> mTypeCache =
+            new SimpleCache<String, FrameType>(64);
+
+    /**
+     * Constructs a wild-card FrameType that matches any other FrameType.
+     * @return The wild-card FrameType instance.
+     */
+    public static FrameType any() {
+        return FrameType.fetchType(ELEMENT_DONTCARE, -1, ACCESS_UNKNOWN);
+    }
+
+    /**
+     * Constructs an object-based single FrameType that matches object-based FrameTypes of any
+     * class.
+     * @return A single object-based FrameType instance.
+     */
+    public static FrameType single() {
+        return FrameType.fetchType(null, 0);
+    }
+
+    /**
+     * Constructs an object-based single FrameType of the specified class.
+     * @param clazz The class of the FrameType.
+     * @return A single object-base FrameType instance of the specified class.
+     */
+    public static FrameType single(Class<?> clazz) {
+        return FrameType.fetchType(clazz, 0);
+    }
+
+    /**
+     * Constructs an object-based array FrameType that matches object-based FrameTypes of any class.
+     * @return An array object-based FrameType instance.
+     */
+    public static FrameType array() {
+        return FrameType.fetchType(null, 1);
+    }
+
+    /**
+     * Constructs an object-based array FrameType with elements of the specified class.
+     * @param clazz The class of the array elements (not the array type).
+     * @return An array object-based FrameType instance of the specified class.
+     */
+    public static FrameType array(Class<?> clazz) {
+        return FrameType.fetchType(clazz, 1);
+    }
+
+    /**
+     * Constructs a one-dimensional buffer type of the specified element.
+     * @param elementType One of the {@code ELEMENT} constants.
+     * @return A 1D buffer FrameType instance.
+     */
+    public static FrameType buffer1D(int elementType) {
+        return FrameType.fetchType(elementType, 1, ACCESS_UNKNOWN);
+    }
+
+    /**
+     * Constructs a two-dimensional buffer type of the specified element.
+     * @param elementType One of the {@code ELEMENT} constants.
+     * @return A 2D buffer FrameType instance.
+     */
+    public static FrameType buffer2D(int elementType) {
+        return FrameType.fetchType(elementType, 2, ACCESS_UNKNOWN);
+    }
+
+    /**
+     * Constructs a two-dimensional image type of the specified element.
+     * @param elementType One of the {@code ELEMENT} constants.
+     * @param accessHint A bit-mask of access flags (see {@code READ} and {@code WRITE} constants).
+     * @return A 2D image FrameType instance.
+     */
+    public static FrameType image2D(int elementType, int accessHint) {
+        return FrameType.fetchType(elementType, 2, accessHint);
+    }
+
+    /**
+     * Converts the current array type to a single type.
+     * The type must be an object-based type. If the type is already a single type, this does
+     * nothing.
+     * @return type as a single type.
+     */
+    public FrameType asSingle() {
+        if (mElementId != ELEMENT_OBJECT) {
+            throw new RuntimeException("Calling asSingle() on non-object type!");
+        }
+        return FrameType.fetchType(mClass, 0);
+    }
+
+    /**
+     * Converts the current single type to an array type.
+     * The type must be an object-based type. If the type is already an array type, this does
+     * nothing.
+     * @return type as an array type.
+     */
+    public FrameType asArray() {
+        if (mElementId != ELEMENT_OBJECT) {
+            throw new RuntimeException("Calling asArray() on non-object type!");
+        }
+        return FrameType.fetchType(mClass, 1);
+    }
+
+    /**
+     * Returns the FrameType's class specifier, or null if no class was set or the receiver is not
+     * an object-based type.
+     * @return The FrameType's class specifier or null.
+     */
+    public Class<?> getContentClass() {
+        return mClass;
+    }
+
+    /**
+     * Returns the FrameType's element id.
+     * @return The element id constant.
+     */
+    public int getElementId() {
+        return mElementId;
+    }
+
+    /**
+     * Returns the number of bytes of the FrameType's element, or 0 if no such size can be
+     * determined.
+     * @return The number of bytes of the FrameType's element.
+     */
+    public int getElementSize() {
+        switch (mElementId) {
+            case ELEMENT_INT8:
+                return 1;
+            case ELEMENT_INT16:
+                return 2;
+            case ELEMENT_INT32:
+            case ELEMENT_FLOAT32:
+            case ELEMENT_RGBA8888:
+                return 4;
+            case ELEMENT_INT64:
+            case ELEMENT_FLOAT64:
+                return 4;
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * Returns the access hints bit-mask of the FrameType.
+     * @return The access hints bit-mask of the FrameType.
+     */
+    public int getAccessHints() {
+        return mAccessHints;
+    }
+
+    /**
+     * Returns the number of dimensions of the FrameType or -1 if no dimensions were set.
+     * @return The number of dimensions of the FrameType.
+     */
+    public int getNumberOfDimensions() {
+        return mDimensions;
+    }
+
+    /**
+     * Returns true, if the FrameType is fully specified.
+     *
+     * A FrameType is fully specified if its element and dimensions are specified.
+     *
+     * @return true, if the FrameType is fully specified.
+     */
+    public boolean isSpecified() {
+        return mElementId != ELEMENT_DONTCARE && mDimensions >= 0;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (object instanceof FrameType) {
+            FrameType type = (FrameType) object;
+            return mElementId == type.mElementId && mDimensions == type.mDimensions
+                    && mAccessHints == type.mAccessHints && mClass == type.mClass;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mElementId ^ mDimensions ^ mAccessHints ^ mClass.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        String result = elementToString(mElementId, mClass) + "[" + mDimensions + "]";
+        if ((mAccessHints & READ_CPU) != 0) {
+            result += "(rcpu)";
+        }
+        if ((mAccessHints & READ_GPU) != 0) {
+            result += "(rgpu)";
+        }
+        if ((mAccessHints & READ_ALLOCATION) != 0) {
+            result += "(ralloc)";
+        }
+        if ((mAccessHints & WRITE_CPU) != 0) {
+            result += "(wcpu)";
+        }
+        if ((mAccessHints & WRITE_GPU) != 0) {
+            result += "(wgpu)";
+        }
+        if ((mAccessHints & WRITE_ALLOCATION) != 0) {
+            result += "(walloc)";
+        }
+        return result;
+    }
+
+    String keyString() {
+        return keyValueForType(mElementId, mDimensions, mAccessHints, mClass);
+    }
+
+    static FrameType tryMerge(FrameType writer, FrameType reader) {
+        if (writer.mElementId == ELEMENT_DONTCARE) {
+            return reader;
+        } else if (reader.mElementId == ELEMENT_DONTCARE) {
+            return writer;
+        } else if (writer.mElementId == ELEMENT_OBJECT && reader.mElementId == ELEMENT_OBJECT) {
+            return tryMergeObjectTypes(writer, reader);
+        } else if (writer.mDimensions > 0 && writer.mElementId == reader.mElementId) {
+            return tryMergeBuffers(writer, reader);
+        } else {
+            return null;
+        }
+    }
+
+    static FrameType tryMergeObjectTypes(FrameType writer, FrameType reader) {
+        int dimensions = Math.max(writer.mDimensions, reader.mDimensions);
+        Class<?> mergedClass = mergeClasses(writer.mClass, reader.mClass);
+        boolean success = mergedClass != null || writer.mClass == null;
+        return success ? FrameType.fetchType(mergedClass, dimensions) : null;
+    }
+
+    static FrameType tryMergeBuffers(FrameType writer, FrameType reader) {
+        if (writer.mDimensions == reader.mDimensions) {
+            int accessHints = writer.mAccessHints | reader.mAccessHints;
+            return FrameType.fetchType(writer.mElementId, writer.mDimensions, accessHints);
+        }
+        return null;
+    }
+
+    static FrameType merge(FrameType writer, FrameType reader) {
+        FrameType result = tryMerge(writer, reader);
+        if (result == null) {
+            throw new RuntimeException(
+                    "Incompatible types in connection: " + writer + " vs. " + reader + "!");
+        }
+        return result;
+    }
+
+    private static String keyValueForType(int elemId, int dims, int hints, Class<?> clazz) {
+        return elemId + ":" + dims + ":" + hints + ":" + (clazz != null ? clazz.getName() : "0");
+    }
+
+    private static String elementToString(int elemId, Class<?> clazz) {
+        switch (elemId) {
+            case ELEMENT_INT8:
+                return "int8";
+            case ELEMENT_INT16:
+                return "int16";
+            case ELEMENT_INT32:
+                return "int32";
+            case ELEMENT_INT64:
+                return "int64";
+            case ELEMENT_FLOAT32:
+                return "float32";
+            case ELEMENT_FLOAT64:
+                return "float64";
+            case ELEMENT_RGBA8888:
+                return "rgba8888";
+            case ELEMENT_OBJECT:
+                return "<" + (clazz == null ? "*" : clazz.getSimpleName()) + ">";
+            case ELEMENT_DONTCARE:
+                return "*";
+            default:
+                return "?";
+        }
+    }
+
+    private static Class<?> mergeClasses(Class<?> classA, Class<?> classB) {
+        // Return the most specialized class.
+        if (classA == null) {
+            return classB;
+        } else if (classB == null) {
+            return classA;
+        } else if (classA.isAssignableFrom(classB)) {
+            return classB;
+        } else if (classB.isAssignableFrom(classA)) {
+            return classA;
+        } else {
+            return null;
+        }
+    }
+
+    private static FrameType fetchType(int elementId, int dimensions, int accessHints) {
+        return fetchType(elementId, dimensions, accessHints, null);
+    }
+
+    private static FrameType fetchType(Class<?> clazz, int dimensions) {
+        return fetchType(ELEMENT_OBJECT, dimensions, ACCESS_UNKNOWN, clazz);
+    }
+
+    private static FrameType fetchType(
+            int elementId, int dimensions, int accessHints, Class<?> clazz) {
+        String typeKey = FrameType.keyValueForType(elementId, dimensions, accessHints, clazz);
+        FrameType type = mTypeCache.get(typeKey);
+        if (type == null) {
+            type = new FrameType(elementId, dimensions, accessHints, clazz);
+            mTypeCache.put(typeKey, type);
+        }
+        return type;
+    }
+
+    private FrameType(int elementId, int dimensions, int accessHints, Class<?> clazz) {
+        mElementId = elementId;
+        mDimensions = dimensions;
+        mClass = clazz;
+        mAccessHints = accessHints;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValue.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValue.java
new file mode 100644
index 0000000..fb007e2
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValue.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import androidx.media.filterfw.BackingStore.Backing;
+
+public class FrameValue extends Frame {
+
+    public Object getValue() {
+        Object result = mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_OBJECT);
+        mBackingStore.unlock();
+        return result;
+    }
+
+    public void setValue(Object value) {
+        Backing backing = mBackingStore.lockBacking(MODE_WRITE, BackingStore.ACCESS_OBJECT);
+        backing.setData(value);
+        mBackingStore.unlock();
+    }
+
+    static FrameValue create(BackingStore backingStore) {
+        assertObjectBased(backingStore.getFrameType());
+        return new FrameValue(backingStore);
+    }
+
+    FrameValue(BackingStore backingStore) {
+        super(backingStore);
+    }
+
+    static void assertObjectBased(FrameType type) {
+        if (type.getElementId() != FrameType.ELEMENT_OBJECT) {
+            throw new RuntimeException("Cannot access non-object based Frame as FrameValue!");
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValues.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValues.java
new file mode 100644
index 0000000..fbddcb1
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameValues.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.lang.reflect.Array;
+
+public class FrameValues extends FrameValue {
+
+    /**
+     * Returns the number of values in the Frame.
+     *
+     * This returns 1, if the Frame value is null, or if the value is not an array.
+     *
+     * @return The number of values in the Frame.
+     */
+    public int getCount() {
+        Object value = super.getValue();
+        if (value == null || !value.getClass().isArray()) {
+            return 1;
+        } else {
+            return Array.getLength(super.getValue());
+        }
+    }
+
+    /**
+     * Returns the values in the Frame as an array.
+     *
+     * Note, that this may be called on Frames that have a non-array object assigned to them. In
+     * that case, this method will wrap the object in an array and return that. This way, filters
+     * can treat any object based frame as arrays.
+     *
+     * @return The array of values in this frame.
+     */
+    public Object getValues() {
+        Object value = super.getValue();
+        if (value == null || value.getClass().isArray()) {
+            return super.getValue();
+        } else {
+            // Allow reading a single as an array.
+            Object[] array = (Object[])Array.newInstance(value.getClass(), 1);
+            array[0] = value;
+            return array;
+        }
+    }
+
+    /**
+     * Returns the value at the specified index.
+     *
+     * In case the value is null or not an array, the index must be 0, and the value itself is
+     * returned.
+     *
+     * @param index The index to access.
+     * @return The value at that index.
+     */
+    public Object getValueAtIndex(int index) {
+        Object value = super.getValue();
+        if (value == null || !value.getClass().isArray()) {
+            if (index != 0) {
+                throw new ArrayIndexOutOfBoundsException(index);
+            } else {
+                return value;
+            }
+        } else {
+            return Array.get(value, index);
+        }
+    }
+
+    /**
+     * Returns the value as a FrameValue at the specified index.
+     *
+     * Use this if you want to access elements as FrameValues. You must release the result when
+     * you are done using it.
+     *
+     * @param index The index to access.
+     * @return The value as a FrameValue at that index (must release).
+     */
+    public FrameValue getFrameValueAtIndex(int index) {
+        Object value = getValueAtIndex(index);
+        FrameValue result = Frame.create(getType().asSingle(), new int[0]).asFrameValue();
+        result.setValue(value);
+        return result;
+    }
+
+    /**
+     * Assign the array of values to the frame.
+     *
+     * You may assign null or a non-array object, which are interpreted as a 1-length array.
+     *
+     * @param values The values to assign to the frame.
+     */
+    public void setValues(Object values) {
+        super.setValue(values);
+    }
+
+    /**
+     * Assign a value at the specified index.
+     *
+     * In case the held value is not an array, the index must be 0, and the object will be replaced
+     * by the new object.
+     *
+     * @param value The value to assign.
+     * @param index The index to assign to.
+     */
+    public void setValueAtIndex(Object value, int index) {
+        super.assertAccessible(MODE_WRITE);
+        Object curValue = super.getValue();
+        if (curValue == null || !curValue.getClass().isArray()) {
+            if (index != 0) {
+                throw new ArrayIndexOutOfBoundsException(index);
+            } else {
+                curValue = value;
+            }
+        } else {
+            Array.set(curValue, index, value);
+        }
+    }
+
+    /**
+     * Assign a FrameValue's value at the specified index.
+     *
+     * This method unpacks the FrameValue and assigns the unpacked value to the specified index.
+     * This does not affect the retain-count of the passed Frame.
+     *
+     * @param frame The frame value to assign.
+     * @param index The index to assign to.
+     */
+    public void setFrameValueAtIndex(FrameValue frame, int index) {
+        Object value = frame.getValue();
+        setValueAtIndex(value, index);
+    }
+
+    static FrameValues create(BackingStore backingStore) {
+        assertObjectBased(backingStore.getFrameType());
+        return new FrameValues(backingStore);
+    }
+
+    FrameValues(BackingStore backingStore) {
+        super(backingStore);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GLToolbox.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GLToolbox.java
new file mode 100644
index 0000000..1c3c7e9
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GLToolbox.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.graphics.Bitmap;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.os.Looper;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * TODO: Make this package-private as RenderTarget and TextureSource should suffice as public
+ * facing OpenGL utilities.
+ * @hide
+ */
+public class GLToolbox {
+
+    public static int textureNone() {
+        return 0;
+    }
+
+    public static boolean isTexture(int texId) {
+        return GLES20.glIsTexture(texId);
+    }
+
+    public static void deleteTexture(int texId) {
+        int[] textures = new int[] { texId };
+        assertNonUiThread("glDeleteTextures");
+        GLES20.glDeleteTextures(1, textures, 0);
+        checkGlError("glDeleteTextures");
+    }
+
+    public static void deleteFbo(int fboId) {
+        int[] fbos = new int[] { fboId };
+        assertNonUiThread("glDeleteFramebuffers");
+        GLES20.glDeleteFramebuffers(1, fbos, 0);
+        checkGlError("glDeleteFramebuffers");
+    }
+
+    public static int generateTexture() {
+        int[] textures = new int[1];
+        GLES20.glGenTextures(1, textures, 0);
+        checkGlError("glGenTextures");
+        return textures[0];
+    }
+
+    public static int generateFbo() {
+        int[] fbos = new int[1];
+        GLES20.glGenFramebuffers(1, fbos, 0);
+        checkGlError("glGenFramebuffers");
+        return fbos[0];
+    }
+
+    public static void readFbo(int fboId, ByteBuffer pixels, int width, int height) {
+        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
+        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixels);
+        checkGlError("glReadPixels");
+    }
+
+    public static void readTarget(RenderTarget target, ByteBuffer pixels, int width, int height) {
+        target.focus();
+        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixels);
+        checkGlError("glReadPixels");
+    }
+
+    public static int attachedTexture(int fboId) {
+        int[] params = new int[1];
+        GLES20.glGetFramebufferAttachmentParameteriv(
+            GLES20.GL_FRAMEBUFFER,
+            GLES20.GL_COLOR_ATTACHMENT0,
+            GLES20.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+            params, 0);
+        checkGlError("glGetFramebufferAttachmentParameteriv");
+        return params[0];
+    }
+
+    public static void attachTextureToFbo(int texId, int fboId) {
+        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
+        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
+                                      GLES20.GL_COLOR_ATTACHMENT0,
+                                      GLES20.GL_TEXTURE_2D,
+                                      texId,
+                                      0);
+        checkGlError("glFramebufferTexture2D");
+    }
+
+    public static void allocateTexturePixels(int texId, int target, int width, int height) {
+        setTexturePixels(texId, target, (ByteBuffer)null, width, height);
+    }
+
+    public static void setTexturePixels(int texId, int target, Bitmap bitmap) {
+        GLES20.glBindTexture(target, texId);
+        GLUtils.texImage2D(target, 0, bitmap, 0);
+        checkGlError("glTexImage2D");
+        setDefaultTexParams();
+    }
+
+    public static void setTexturePixels(int texId, int target, ByteBuffer pixels,
+                                        int width, int height) {
+        GLES20.glBindTexture(target, texId);
+
+        // For some devices, "pixels" being null causes system error.
+        if (pixels == null) {
+            pixels = ByteBuffer.allocateDirect(width * height * 4);
+        }
+        GLES20.glTexImage2D(target, 0, GLES20.GL_RGBA, width, height, 0,
+                            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixels);
+        checkGlError("glTexImage2D");
+        setDefaultTexParams();
+    }
+
+    public static void setDefaultTexParams() {
+        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
+                               GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
+                               GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
+                               GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
+                               GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+        checkGlError("glTexParameteri");
+    }
+
+    public static int vboNone() {
+        return 0;
+    }
+
+    public static int generateVbo() {
+        int[] vbos = new int[1];
+        GLES20.glGenBuffers(1, vbos, 0);
+        checkGlError("glGenBuffers");
+        return vbos[0];
+    }
+
+    public static void setVboData(int vboId, ByteBuffer data) {
+        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
+        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, data.remaining(), data, GLES20.GL_STATIC_DRAW);
+        checkGlError("glBufferData");
+    }
+
+    public static void setVboFloats(int vboId, float[] values) {
+        int len = values.length * 4;
+        ByteBuffer buffer = ByteBuffer.allocateDirect(len).order(ByteOrder.nativeOrder());
+        setVboData(vboId, buffer);
+    }
+
+    public static boolean isVbo(int vboId) {
+        return GLES20.glIsBuffer(vboId);
+    }
+
+    public static void deleteVbo(int vboId) {
+        int[] buffers = new int[] { vboId };
+        GLES20.glDeleteBuffers(1, buffers, 0);
+        checkGlError("glDeleteBuffers");
+    }
+
+    public static void checkGlError(String operation) {
+        int error;
+        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+            throw new RuntimeException("GL Operation '" + operation + "' caused error "
+                + Integer.toHexString(error) + "!");
+        }
+    }
+
+    /**
+     * Make sure we are not operating in the UI thread.
+     *
+     * It is often tricky to track down bugs that happen when issuing GL commands in the UI thread.
+     * This is especially true when releasing GL resources. Often this will cause errors much later
+     * on. Therefore we make sure we do not do these dangerous operations on the UI thread.
+     */
+    private static void assertNonUiThread(String operation) {
+        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
+            throw new RuntimeException("Attempting to perform GL operation '" + operation
+                    + "' on UI thread!");
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java
new file mode 100644
index 0000000..0013965
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+// This class provides functions to export a FilterGraph.
+
+package androidx.media.filterfw;
+
+import android.content.Context;
+
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class provides functions to export a FilterGraph as a DOT file.
+ */
+public class GraphExporter {
+
+    /**
+     * Exports the graph as DOT (see http://en.wikipedia.org/wiki/DOT_language).
+     * Using the exported file, the graph can be visualized e.g. with the command line tool dot.
+     * Optionally, one may /exclude/ unconnected optional ports (third parameter = false),
+     * since they can quickly clutter the visualization (and, depending on the purpose, may not
+     * be interesting).
+     *
+     * Example workflow:
+     *  1. run application on device, make sure it calls exportGraphAsDOT(...);
+     *  2. adb pull /data/data/<application name>/files/<graph filename>.gv graph.gv
+     *  3. dot -Tpng graph.gv -o graph.png
+     *  4. eog graph.png
+     */
+    static public void exportAsDot(FilterGraph graph, String filename,
+            boolean includeUnconnectedOptionalPorts)
+            throws java.io.FileNotFoundException, java.io.IOException {
+        // Initialize, open file stream
+        Context myAppContext = graph.getContext().getApplicationContext();
+        Filter[] filters = graph.getAllFilters();
+        FileOutputStream fOut = myAppContext.openFileOutput(filename, Context.MODE_PRIVATE);
+        OutputStreamWriter dotFile = new OutputStreamWriter(fOut);
+
+        // Write beginning of DOT file
+        dotFile.write("digraph graphname {\n");
+        dotFile.write("  node [shape=record];\n");
+
+        // N.B. For specification and lots of examples of the DOT language, see
+        //   http://www.graphviz.org/Documentation/dotguide.pdf
+
+        // Iterate over all filters of the graph, write corresponding DOT node elements
+
+        for(Filter filter : filters) {
+            dotFile.write(getDotName("  " + filter.getName()) + " [label=\"{");
+
+            // Write upper part of element (i.e., input ports)
+            Set<String> inputPorts = getInputPorts(filter, includeUnconnectedOptionalPorts);
+            if(inputPorts.size() > 0) {
+                dotFile.write(" { ");
+                int counter = 0;
+                for(String p : inputPorts) {
+                    dotFile.write("<" + getDotName(p) + "_IN>" + p);
+                    if(++counter != inputPorts.size()) dotFile.write(" | ");
+                }
+                dotFile.write(" } | ");
+            }
+
+            // Write center part of element (i.e., element label)
+            dotFile.write(filter.getName());
+
+            // Write lower part of element (i.e., output ports)
+            Set<String> outputPorts = getOutputPorts(filter, includeUnconnectedOptionalPorts);
+            if(outputPorts.size() > 0) {
+                dotFile.write(" | { ");
+                int counter = 0;
+                for(String p : outputPorts) {
+                    dotFile.write("<" + getDotName(p) + "_OUT>" + p);
+                    if(++counter != outputPorts.size()) dotFile.write(" | ");
+                }
+                dotFile.write(" } ");
+            }
+
+            dotFile.write("}\"];\n");
+        }
+        dotFile.write("\n");
+
+        // Iterate over all filters again to collect connections and find unconnected ports
+
+        int dummyNodeCounter = 0;
+        for(Filter filter : filters) {
+            Set<String> outputPorts = getOutputPorts(filter, includeUnconnectedOptionalPorts);
+            for(String portName : outputPorts) {
+                OutputPort source = filter.getConnectedOutputPort(portName);
+                if(source != null) {
+                    // Found a connection, draw it
+                    InputPort target = source.getTarget();
+                    dotFile.write("  " +
+                        getDotName(source.getFilter().getName()) + ":" +
+                        getDotName(source.getName()) + "_OUT -> " +
+                        getDotName(target.getFilter().getName()) + ":" +
+                        getDotName(target.getName()) + "_IN;\n" );
+                } else {
+                    // Found a unconnected output port, add dummy node
+                    String color = filter.getSignature().getOutputPortInfo(portName).isRequired()
+                        ? "red" : "blue";  // red for unconnected, required ports
+                    dotFile.write("  " +
+                        "dummy" + (++dummyNodeCounter) +
+                        " [shape=point,label=\"\",color=" + color + "];\n" +
+                        "  " + getDotName(filter.getName()) + ":" +
+                        getDotName(portName) + "_OUT -> " +
+                        "dummy" + dummyNodeCounter + " [color=" + color + "];\n");
+                }
+            }
+
+            Set<String> inputPorts = getInputPorts(filter, includeUnconnectedOptionalPorts);
+            for(String portName : inputPorts) {
+                InputPort target = filter.getConnectedInputPort(portName);
+                if(target != null) {
+                    // Found a connection -- nothing to do, connections have been written out above
+                } else {
+                    // Found a unconnected input port, add dummy node
+                    String color = filter.getSignature().getInputPortInfo(portName).isRequired()
+                        ? "red" : "blue";  // red for unconnected, required ports
+                    dotFile.write("  " +
+                        "dummy" + (++dummyNodeCounter) +
+                        " [shape=point,label=\"\",color=" + color + "];\n" +
+                        "  dummy" + dummyNodeCounter + " -> " +
+                        getDotName(filter.getName()) + ":" +
+                        getDotName(portName) + "_IN [color=" + color + "];\n");
+                }
+            }
+        }
+
+        // Write end of DOT file, close file stream
+        dotFile.write("}\n");
+        dotFile.flush();
+        dotFile.close();
+    }
+
+    // Internal methods
+
+    // From element's name in XML, create DOT-allowed element name
+    static private String getDotName(String raw) {
+        return raw.replaceAll("\\.", "___"); // DOT does not allow . in element names
+    }
+
+    // Retrieve all input ports of a filter, including:
+    //  unconnected ports (which can not be retrieved from the filter, only from the signature), and
+    //  additional (connected) ports not listed in the signature (which is allowed by default,
+    //    unless disallowOtherInputs is defined in signature).
+    // With second parameter = false, *omit* unconnected optional ports.
+    static private Set<String> getInputPorts(Filter filter, boolean includeUnconnectedOptional) {
+        // add (connected) ports from filter
+        Set<String> ports = new HashSet<String>();
+        ports.addAll(filter.getConnectedInputPortMap().keySet());
+
+        // add (unconnected) ports from signature
+        HashMap<String, Signature.PortInfo> signaturePorts = filter.getSignature().getInputPorts();
+        if(signaturePorts != null){
+            for(Entry<String, Signature.PortInfo> e : signaturePorts.entrySet()) {
+                if(includeUnconnectedOptional || e.getValue().isRequired()) {
+                    ports.add(e.getKey());
+                }
+            }
+        }
+        return ports;
+    }
+
+    // Retrieve all output ports of a filter (analogous to above function)
+    static private Set<String> getOutputPorts(Filter filter, boolean includeUnconnectedOptional) {
+        // add (connected) ports from filter
+        Set<String> ports = new HashSet<String>();
+        ports.addAll(filter.getConnectedOutputPortMap().keySet());
+
+        // add (unconnected) ports from signature
+        HashMap<String, Signature.PortInfo> signaturePorts = filter.getSignature().getOutputPorts();
+        if(signaturePorts != null){
+            for(Entry<String, Signature.PortInfo> e : signaturePorts.entrySet()) {
+                if(includeUnconnectedOptional || e.getValue().isRequired()) {
+                    ports.add(e.getKey());
+                }
+            }
+        }
+        return ports;
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphInputSource.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphInputSource.java
new file mode 100644
index 0000000..03b3abe
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphInputSource.java
@@ -0,0 +1,58 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.Signature;
+
+public class GraphInputSource extends Filter {
+
+    private Frame mFrame = null;
+
+    public GraphInputSource(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addOutputPort("frame", Signature.PORT_REQUIRED, FrameType.any())
+            .disallowOtherInputs();
+    }
+
+    public void pushFrame(Frame frame) {
+        if (mFrame != null) {
+            mFrame.release();
+        }
+        if (frame == null) {
+            throw new RuntimeException("Attempting to assign null-frame!");
+        }
+        mFrame = frame.retain();
+    }
+
+    @Override
+    protected void onProcess() {
+        if (mFrame != null) {
+            getConnectedOutputPort("frame").pushFrame(mFrame);
+            mFrame.release();
+            mFrame = null;
+        }
+    }
+
+    @Override
+    protected void onTearDown() {
+        if (mFrame != null) {
+            mFrame.release();
+            mFrame = null;
+        }
+    }
+
+    @Override
+    protected boolean canSchedule() {
+        return super.canSchedule() && mFrame != null;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphOutputTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphOutputTarget.java
new file mode 100644
index 0000000..1f3be10
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphOutputTarget.java
@@ -0,0 +1,60 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.Signature;
+
+public class GraphOutputTarget extends Filter {
+
+    private Frame mFrame = null;
+    private FrameType mType = FrameType.any();
+
+    public GraphOutputTarget(MffContext context, String name) {
+        super(context, name);
+    }
+
+    // TODO: During initialization only?
+    public void setType(FrameType type) {
+        mType = type;
+    }
+
+    public FrameType getType() {
+        return mType;
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("frame", Signature.PORT_REQUIRED, mType)
+            .disallowOtherInputs();
+    }
+
+    // Returns a retained frame!
+    public Frame pullFrame() {
+        Frame result = null;
+        if (mFrame != null) {
+            result = mFrame;
+            mFrame = null;
+        }
+        return result;
+    }
+
+    @Override
+    protected void onProcess() {
+        Frame frame = getConnectedInputPort("frame").pullFrame();
+        if (mFrame != null) {
+            mFrame.release();
+        }
+        mFrame = frame.retain();
+    }
+
+    @Override
+    protected boolean canSchedule() {
+        return super.canSchedule() && mFrame == null;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphReader.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphReader.java
new file mode 100644
index 0000000..ef885e3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphReader.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.text.TextUtils;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A GraphReader allows obtaining filter graphs from XML graph files or strings.
+ */
+public class GraphReader {
+
+    private static interface Command {
+        public void execute(CommandStack stack);
+    }
+
+    private static class CommandStack {
+        private ArrayList<Command> mCommands = new ArrayList<Command>();
+        private FilterGraph.Builder mBuilder;
+        private FilterFactory mFactory;
+        private MffContext mContext;
+
+        public CommandStack(MffContext context) {
+            mContext = context;
+            mBuilder = new FilterGraph.Builder(mContext);
+            mFactory = new FilterFactory();
+        }
+
+        public void execute() {
+            for (Command command : mCommands) {
+                command.execute(this);
+            }
+        }
+
+        public void append(Command command) {
+            mCommands.add(command);
+        }
+
+        public FilterFactory getFactory() {
+            return mFactory;
+        }
+
+        public MffContext getContext() {
+            return mContext;
+        }
+
+        protected FilterGraph.Builder getBuilder() {
+            return mBuilder;
+        }
+    }
+
+    private static class ImportPackageCommand implements Command {
+        private String mPackageName;
+
+        public ImportPackageCommand(String packageName) {
+            mPackageName = packageName;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            try {
+                stack.getFactory().addPackage(mPackageName);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+    }
+
+    private static class AddLibraryCommand implements Command {
+        private String mLibraryName;
+
+        public AddLibraryCommand(String libraryName) {
+            mLibraryName = libraryName;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            FilterFactory.addFilterLibrary(mLibraryName);
+        }
+    }
+
+    private static class AllocateFilterCommand implements Command {
+        private String mClassName;
+        private String mFilterName;
+
+        public AllocateFilterCommand(String className, String filterName) {
+            mClassName = className;
+            mFilterName = filterName;
+        }
+
+        @Override
+	public void execute(CommandStack stack) {
+            Filter filter = null;
+            try {
+                filter = stack.getFactory().createFilterByClassName(mClassName,
+                                                                    mFilterName,
+                                                                    stack.getContext());
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException("Error creating filter " + mFilterName + "!", e);
+            }
+            stack.getBuilder().addFilter(filter);
+        }
+    }
+
+    private static class AddSourceSlotCommand implements Command {
+        private String mName;
+        private String mSlotName;
+
+        public AddSourceSlotCommand(String name, String slotName) {
+            mName = name;
+            mSlotName = slotName;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            stack.getBuilder().addFrameSlotSource(mName, mSlotName);
+        }
+    }
+
+    private static class AddTargetSlotCommand implements Command {
+        private String mName;
+        private String mSlotName;
+
+        public AddTargetSlotCommand(String name, String slotName) {
+            mName = name;
+            mSlotName = slotName;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            stack.getBuilder().addFrameSlotTarget(mName, mSlotName);
+        }
+    }
+
+    private static class AddVariableCommand implements Command {
+        private String mName;
+        private Object mValue;
+
+        public AddVariableCommand(String name, Object value) {
+            mName = name;
+            mValue = value;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            stack.getBuilder().addVariable(mName, mValue);
+        }
+    }
+
+    private static class SetFilterInputCommand implements Command {
+        private String mFilterName;
+        private String mFilterInput;
+        private Object mValue;
+
+        public SetFilterInputCommand(String filterName, String input, Object value) {
+            mFilterName = filterName;
+            mFilterInput = input;
+            mValue = value;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            if (mValue instanceof Variable) {
+                String varName = ((Variable)mValue).name;
+                stack.getBuilder().assignVariableToFilterInput(varName, mFilterName, mFilterInput);
+            } else {
+                stack.getBuilder().assignValueToFilterInput(mValue, mFilterName, mFilterInput);
+            }
+        }
+    }
+
+    private static class ConnectCommand implements Command {
+        private String mSourceFilter;
+        private String mSourcePort;
+        private String mTargetFilter;
+        private String mTargetPort;
+
+        public ConnectCommand(String sourceFilter,
+                              String sourcePort,
+                              String targetFilter,
+                              String targetPort) {
+            mSourceFilter = sourceFilter;
+            mSourcePort = sourcePort;
+            mTargetFilter = targetFilter;
+            mTargetPort = targetPort;
+        }
+
+        @Override
+        public void execute(CommandStack stack) {
+            stack.getBuilder().connect(mSourceFilter, mSourcePort, mTargetFilter, mTargetPort);
+        }
+    }
+
+    private static class Variable {
+        public String name;
+
+        public Variable(String name) {
+            this.name = name;
+        }
+    }
+
+    private static class XmlGraphReader {
+
+        private SAXParserFactory mParserFactory;
+
+        private static class GraphDataHandler extends DefaultHandler {
+
+            private CommandStack mCommandStack;
+            private boolean mInGraph = false;
+            private String mCurFilterName = null;
+
+            public GraphDataHandler(CommandStack commandStack) {
+                mCommandStack = commandStack;
+            }
+
+            @Override
+            public void startElement(String uri, String localName, String qName, Attributes attr)
+                    throws SAXException {
+                if (localName.equals("graph")) {
+                    beginGraph();
+                } else {
+                    assertInGraph(localName);
+                    if (localName.equals("import")) {
+                        addImportCommand(attr);
+                    } else if (localName.equals("library")) {
+                        addLibraryCommand(attr);
+                    } else if (localName.equals("connect")) {
+                        addConnectCommand(attr);
+                    } else if (localName.equals("var")) {
+                        addVarCommand(attr);
+                    } else if (localName.equals("filter")) {
+                        beginFilter(attr);
+                    } else if (localName.equals("input")) {
+                        addFilterInput(attr);
+                    } else {
+                        throw new SAXException("Unknown XML element '" + localName + "'!");
+                    }
+                }
+            }
+
+            @Override
+            public void endElement (String uri, String localName, String qName) {
+                if (localName.equals("graph")) {
+                    endGraph();
+                } else if (localName.equals("filter")) {
+                    endFilter();
+                }
+            }
+
+            private void addImportCommand(Attributes attributes) throws SAXException {
+                String packageName = getRequiredAttribute(attributes, "package");
+                mCommandStack.append(new ImportPackageCommand(packageName));
+            }
+
+            private void addLibraryCommand(Attributes attributes) throws SAXException {
+                String libraryName = getRequiredAttribute(attributes, "name");
+                mCommandStack.append(new AddLibraryCommand(libraryName));
+            }
+
+            private void addConnectCommand(Attributes attributes) {
+                String sourcePortName   = null;
+                String sourceFilterName = null;
+                String targetPortName   = null;
+                String targetFilterName = null;
+
+                // check for shorthand: <connect source="filter:port" target="filter:port"/>
+                String sourceTag = attributes.getValue("source");
+                if (sourceTag != null) {
+                    String[] sourceParts = sourceTag.split(":");
+                    if (sourceParts.length == 2) {
+                        sourceFilterName = sourceParts[0];
+                        sourcePortName   = sourceParts[1];
+                    } else {
+                        throw new RuntimeException(
+                            "'source' tag needs to have format \"filter:port\"! " +
+                            "Alternatively, you may use the form " +
+                            "'sourceFilter=\"filter\" sourcePort=\"port\"'.");
+                    }
+                } else {
+                    sourceFilterName = attributes.getValue("sourceFilter");
+                    sourcePortName   = attributes.getValue("sourcePort");
+                }
+
+                String targetTag = attributes.getValue("target");
+                if (targetTag != null) {
+                    String[] targetParts = targetTag.split(":");
+                    if (targetParts.length == 2) {
+                        targetFilterName = targetParts[0];
+                        targetPortName   = targetParts[1];
+                    } else {
+                        throw new RuntimeException(
+                            "'target' tag needs to have format \"filter:port\"! " +
+                            "Alternatively, you may use the form " +
+                            "'targetFilter=\"filter\" targetPort=\"port\"'.");
+                    }
+                } else {
+                    targetFilterName = attributes.getValue("targetFilter");
+                    targetPortName   = attributes.getValue("targetPort");
+                }
+
+                String sourceSlotName = attributes.getValue("sourceSlot");
+                String targetSlotName = attributes.getValue("targetSlot");
+                if (sourceSlotName != null) {
+                    sourceFilterName = "sourceSlot_" + sourceSlotName;
+                    mCommandStack.append(new AddSourceSlotCommand(sourceFilterName,
+                                                                  sourceSlotName));
+                    sourcePortName = "frame";
+                }
+                if (targetSlotName != null) {
+                    targetFilterName = "targetSlot_" + targetSlotName;
+                    mCommandStack.append(new AddTargetSlotCommand(targetFilterName,
+                                                                  targetSlotName));
+                    targetPortName = "frame";
+                }
+                assertValueNotNull("sourceFilter", sourceFilterName);
+                assertValueNotNull("sourcePort", sourcePortName);
+                assertValueNotNull("targetFilter", targetFilterName);
+                assertValueNotNull("targetPort", targetPortName);
+                // TODO: Should slot connections auto-branch?
+                mCommandStack.append(new ConnectCommand(sourceFilterName,
+                                                        sourcePortName,
+                                                        targetFilterName,
+                                                        targetPortName));
+            }
+
+            private void addVarCommand(Attributes attributes) throws SAXException {
+                String varName = getRequiredAttribute(attributes, "name");
+                Object varValue = getAssignmentValue(attributes);
+                mCommandStack.append(new AddVariableCommand(varName, varValue));
+            }
+
+            private void beginGraph() throws SAXException {
+                if (mInGraph) {
+                    throw new SAXException("Found more than one graph element in XML!");
+                }
+                mInGraph = true;
+            }
+
+            private void endGraph() {
+                mInGraph = false;
+            }
+
+            private void beginFilter(Attributes attributes) throws SAXException {
+                String className = getRequiredAttribute(attributes, "class");
+                mCurFilterName = getRequiredAttribute(attributes, "name");
+                mCommandStack.append(new AllocateFilterCommand(className, mCurFilterName));
+            }
+
+            private void endFilter() {
+                mCurFilterName = null;
+            }
+
+            private void addFilterInput(Attributes attributes) throws SAXException {
+                // Make sure we are in a filter element
+                if (mCurFilterName == null) {
+                    throw new SAXException("Found 'input' element outside of 'filter' "
+                        + "element!");
+                }
+
+                // Get input name and value
+                String inputName = getRequiredAttribute(attributes, "name");
+                Object inputValue = getAssignmentValue(attributes);
+                if (inputValue == null) {
+                    throw new SAXException("No value specified for input '" + inputName + "' "
+                        + "of filter '" + mCurFilterName + "'!");
+                }
+
+                // Push commmand
+                mCommandStack.append(new SetFilterInputCommand(mCurFilterName,
+                                                               inputName,
+                                                               inputValue));
+            }
+
+            private void assertInGraph(String localName) throws SAXException {
+                if (!mInGraph) {
+                    throw new SAXException("Encountered '" + localName + "' element outside of "
+                        + "'graph' element!");
+                }
+            }
+
+            private static Object getAssignmentValue(Attributes attributes) {
+                String strValue = null;
+                if ((strValue = attributes.getValue("stringValue")) != null) {
+                    return strValue;
+                } else if ((strValue = attributes.getValue("booleanValue")) != null) {
+                    return Boolean.parseBoolean(strValue);
+                } else if ((strValue = attributes.getValue("intValue")) != null) {
+                    return Integer.parseInt(strValue);
+                } else if ((strValue = attributes.getValue("floatValue")) != null) {
+                    return Float.parseFloat(strValue);
+                } else if ((strValue = attributes.getValue("floatsValue")) != null) {
+                    String[] floatStrings = TextUtils.split(strValue, ",");
+                    float[] result = new float[floatStrings.length];
+                    for (int i = 0; i < floatStrings.length; ++i) {
+                        result[i] = Float.parseFloat(floatStrings[i]);
+                    }
+                    return result;
+                } else if ((strValue = attributes.getValue("varValue")) != null) {
+                    return new Variable(strValue);
+                } else {
+                    return null;
+                }
+            }
+
+            private static String getRequiredAttribute(Attributes attributes, String name)
+                    throws SAXException {
+                String result = attributes.getValue(name);
+                if (result == null) {
+                    throw new SAXException("Required attribute '" + name + "' not found!");
+                }
+                return result;
+            }
+
+            private static void assertValueNotNull(String valueName, Object value) {
+                if (value == null) {
+                    throw new NullPointerException("Required value '" + value + "' not specified!");
+                }
+            }
+
+        }
+
+        public XmlGraphReader() {
+            mParserFactory = SAXParserFactory.newInstance();
+        }
+
+        public void parseString(String graphString, CommandStack commandStack) throws IOException {
+            try {
+                XMLReader reader = getReaderForCommandStack(commandStack);
+                reader.parse(new InputSource(new StringReader(graphString)));
+            } catch (SAXException e) {
+                throw new IOException("XML parse error during graph parsing!", e);
+            }
+        }
+
+        public void parseInput(InputStream inputStream, CommandStack commandStack)
+                throws IOException {
+            try {
+                XMLReader reader = getReaderForCommandStack(commandStack);
+                reader.parse(new InputSource(inputStream));
+            } catch (SAXException e) {
+                throw new IOException("XML parse error during graph parsing!", e);
+            }
+        }
+
+        private XMLReader getReaderForCommandStack(CommandStack commandStack) throws IOException {
+            try {
+                SAXParser parser = mParserFactory.newSAXParser();
+                XMLReader reader = parser.getXMLReader();
+                GraphDataHandler graphHandler = new GraphDataHandler(commandStack);
+                reader.setContentHandler(graphHandler);
+                return reader;
+            } catch (ParserConfigurationException e) {
+                throw new IOException("Error creating SAXParser for graph parsing!", e);
+            } catch (SAXException e) {
+                throw new IOException("Error creating XMLReader for graph parsing!", e);
+            }
+        }
+    }
+
+    /**
+     * Read an XML graph from a String.
+     *
+     * This function automatically checks each filters' signatures and throws a Runtime Exception
+     * if required ports are unconnected. Use the 3-parameter version to avoid this behavior.
+     *
+     * @param context the MffContext into which to load the graph.
+     * @param xmlSource the graph specified in XML.
+     * @return the FilterGraph instance for the XML source.
+     * @throws IOException if there was an error parsing the source.
+     */
+    public static FilterGraph readXmlGraph(MffContext context, String xmlSource)
+            throws IOException {
+        FilterGraph.Builder builder = getBuilderForXmlString(context, xmlSource);
+        return builder.build();
+    }
+
+    /**
+     * Read an XML sub-graph from a String.
+     *
+     * @param context the MffContext into which to load the graph.
+     * @param xmlSource the graph specified in XML.
+     * @param parentGraph the parent graph.
+     * @return the FilterGraph instance for the XML source.
+     * @throws IOException if there was an error parsing the source.
+     */
+    public static FilterGraph readXmlSubGraph(
+            MffContext context, String xmlSource, FilterGraph parentGraph)
+            throws IOException {
+        FilterGraph.Builder builder = getBuilderForXmlString(context, xmlSource);
+        return builder.buildSubGraph(parentGraph);
+    }
+
+    /**
+     * Read an XML graph from a resource.
+     *
+     * This function automatically checks each filters' signatures and throws a Runtime Exception
+     * if required ports are unconnected. Use the 3-parameter version to avoid this behavior.
+     *
+     * @param context the MffContext into which to load the graph.
+     * @param resourceId the XML resource ID.
+     * @return the FilterGraph instance for the XML source.
+     * @throws IOException if there was an error reading or parsing the resource.
+     */
+    public static FilterGraph readXmlGraphResource(MffContext context, int resourceId)
+            throws IOException {
+        FilterGraph.Builder builder = getBuilderForXmlResource(context, resourceId);
+        return builder.build();
+    }
+
+    /**
+     * Read an XML graph from a resource.
+     *
+     * This function automatically checks each filters' signatures and throws a Runtime Exception
+     * if required ports are unconnected. Use the 3-parameter version to avoid this behavior.
+     *
+     * @param context the MffContext into which to load the graph.
+     * @param resourceId the XML resource ID.
+     * @return the FilterGraph instance for the XML source.
+     * @throws IOException if there was an error reading or parsing the resource.
+     */
+    public static FilterGraph readXmlSubGraphResource(
+            MffContext context, int resourceId, FilterGraph parentGraph)
+            throws IOException {
+        FilterGraph.Builder builder = getBuilderForXmlResource(context, resourceId);
+        return builder.buildSubGraph(parentGraph);
+    }
+
+    private static FilterGraph.Builder getBuilderForXmlString(MffContext context, String source)
+            throws IOException {
+        XmlGraphReader reader = new XmlGraphReader();
+        CommandStack commands = new CommandStack(context);
+        reader.parseString(source, commands);
+        commands.execute();
+        return commands.getBuilder();
+    }
+
+    private static FilterGraph.Builder getBuilderForXmlResource(MffContext context, int resourceId)
+            throws IOException {
+        InputStream inputStream = context.getApplicationContext().getResources()
+                .openRawResource(resourceId);
+        XmlGraphReader reader = new XmlGraphReader();
+        CommandStack commands = new CommandStack(context);
+        reader.parseInput(inputStream, commands);
+        commands.execute();
+        return commands.getBuilder();
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphRunner.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphRunner.java
new file mode 100644
index 0000000..36aed63
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphRunner.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.os.ConditionVariable;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * A GraphRunner schedules and executes the filter nodes of a graph.
+ *
+ * Typically, you create a GraphRunner given a FilterGraph instance, and execute it by calling
+ * {@link #start(FilterGraph)}.
+ *
+ * The scheduling strategy determines how the filter nodes are selected
+ * for scheduling. More precisely, given the set of nodes that can be scheduled, the scheduling
+ * strategy determines which node of this set to select for execution. For instance, an LFU
+ * scheduler (the default) chooses the node that has been executed the least amount of times.
+ */
+public final class GraphRunner {
+
+    private static int PRIORITY_SLEEP = -1;
+    private static int PRIORITY_STOP = -2;
+
+    private static final Event BEGIN_EVENT = new Event(Event.BEGIN, null);
+    private static final Event FLUSH_EVENT = new Event(Event.FLUSH, null);
+    private static final Event HALT_EVENT = new Event(Event.HALT, null);
+    private static final Event KILL_EVENT = new Event(Event.KILL, null);
+    private static final Event PAUSE_EVENT = new Event(Event.PAUSE, null);
+    private static final Event RELEASE_FRAMES_EVENT = new Event(Event.RELEASE_FRAMES, null);
+    private static final Event RESTART_EVENT = new Event(Event.RESTART, null);
+    private static final Event RESUME_EVENT = new Event(Event.RESUME, null);
+    private static final Event STEP_EVENT = new Event(Event.STEP, null);
+    private static final Event STOP_EVENT = new Event(Event.STOP, null);
+
+    private static class State {
+        public static final int STOPPED = 1;
+        public static final int PREPARING = 2;
+        public static final int RUNNING = 4;
+        public static final int PAUSED = 8;
+        public static final int HALTED = 16;
+
+        private int mCurrent = STOPPED;
+
+        public synchronized void setState(int newState) {
+            mCurrent = newState;
+        }
+
+        public synchronized boolean check(int state) {
+            return ((mCurrent & state) == state);
+        }
+
+        public synchronized boolean addState(int state) {
+            if ((mCurrent & state) != state) {
+                mCurrent |= state;
+                return true;
+            }
+            return false;
+        }
+
+        public synchronized boolean removeState(int state) {
+            boolean result = (mCurrent & state) == state;
+            mCurrent &= (~state);
+            return result;
+        }
+
+        public synchronized int current() {
+            return mCurrent;
+        }
+    }
+
+    private static class Event {
+        public static final int PREPARE = 1;
+        public static final int BEGIN = 2;
+        public static final int STEP = 3;
+        public static final int STOP = 4;
+        public static final int PAUSE = 6;
+        public static final int HALT = 7;
+        public static final int RESUME = 8;
+        public static final int RESTART = 9;
+        public static final int FLUSH = 10;
+        public static final int TEARDOWN = 11;
+        public static final int KILL = 12;
+        public static final int RELEASE_FRAMES = 13;
+
+        public int code;
+        public Object object;
+
+        public Event(int code, Object object) {
+            this.code = code;
+            this.object = object;
+        }
+    }
+
+    private final class GraphRunLoop implements Runnable {
+
+        private State mState = new State();
+        private final boolean mAllowOpenGL;
+        private RenderTarget mRenderTarget = null;
+        private LinkedBlockingQueue<Event> mEventQueue = new LinkedBlockingQueue<Event>();
+        private Exception mCaughtException = null;
+        private boolean mClosedSuccessfully = true;
+        private Stack<Filter[]> mFilters = new Stack<Filter[]>();
+        private Stack<SubListener> mSubListeners = new Stack<SubListener>();
+        private Set<FilterGraph> mOpenedGraphs = new HashSet<FilterGraph>();
+        public ConditionVariable mStopCondition = new ConditionVariable(true);
+
+        private void loop() {
+            boolean killed = false;
+            while (!killed) {
+                try {
+                    Event event = nextEvent();
+                    if (event == null) continue;
+                    switch (event.code) {
+                        case Event.PREPARE:
+                            onPrepare((FilterGraph)event.object);
+                            break;
+                        case Event.BEGIN:
+                            onBegin();
+                            break;
+                        case Event.STEP:
+                            onStep();
+                            break;
+                        case Event.STOP:
+                            onStop();
+                            break;
+                        case Event.PAUSE:
+                            onPause();
+                            break;
+                        case Event.HALT:
+                            onHalt();
+                            break;
+                        case Event.RESUME:
+                            onResume();
+                            break;
+                        case Event.RESTART:
+                            onRestart();
+                            break;
+                        case Event.FLUSH:
+                            onFlush();
+                            break;
+                        case Event.TEARDOWN:
+                            onTearDown((FilterGraph)event.object);
+                            break;
+                        case Event.KILL:
+                            killed = true;
+                            break;
+                        case Event.RELEASE_FRAMES:
+                            onReleaseFrames();
+                            break;
+                    }
+                } catch (Exception e) {
+                    if (mCaughtException == null) {
+                        mCaughtException = e;
+                        mClosedSuccessfully = true;
+                        e.printStackTrace();
+                        pushEvent(STOP_EVENT);
+                    } else {
+                        // Exception during exception recovery? Abort all processing. Do not
+                        // overwrite the original exception.
+                        mClosedSuccessfully = false;
+                        mEventQueue.clear();
+                        cleanUp();
+                    }
+                }
+            }
+        }
+
+        public GraphRunLoop(boolean allowOpenGL) {
+            mAllowOpenGL = allowOpenGL;
+        }
+
+        @Override
+        public void run() {
+            onInit();
+            loop();
+            onDestroy();
+        }
+
+        public void enterSubGraph(FilterGraph graph, SubListener listener) {
+            if (mState.check(State.RUNNING)) {
+                onOpenGraph(graph);
+                mSubListeners.push(listener);
+            }
+        }
+
+        public void pushWakeEvent(Event event) {
+            // This is of course not race-condition proof. The worst case is that the event
+            // is pushed even though the queue was not empty, which is acceptible for our cases.
+            if (mEventQueue.isEmpty()) {
+                pushEvent(event);
+            }
+        }
+
+        public void pushEvent(Event event) {
+            mEventQueue.offer(event);
+        }
+
+        public void pushEvent(int eventId, Object object) {
+            mEventQueue.offer(new Event(eventId, object));
+        }
+
+        public boolean checkState(int state) {
+            return mState.check(state);
+        }
+
+        public ConditionVariable getStopCondition() {
+            return mStopCondition;
+        }
+
+        public boolean isOpenGLAllowed() {
+            // Does not need synchronization as mAllowOpenGL flag is final.
+            return mAllowOpenGL;
+        }
+
+        private Event nextEvent() {
+            try {
+                return mEventQueue.take();
+            } catch (InterruptedException e) {
+                // Ignore and keep going.
+                Log.w("GraphRunner", "Event queue processing was interrupted.");
+                return null;
+            }
+        }
+
+        private void onPause() {
+            mState.addState(State.PAUSED);
+        }
+
+        private void onResume() {
+            if (mState.removeState(State.PAUSED)) {
+                if (mState.current() == State.RUNNING) {
+                    pushEvent(STEP_EVENT);
+                }
+            }
+        }
+
+        private void onHalt() {
+            if (mState.addState(State.HALTED) && mState.check(State.RUNNING)) {
+                closeAllFilters();
+            }
+        }
+
+        private void onRestart() {
+            if (mState.removeState(State.HALTED)) {
+                if (mState.current() == State.RUNNING) {
+                    pushEvent(STEP_EVENT);
+                }
+            }
+        }
+
+        private void onDestroy() {
+            mFrameManager.destroyBackings();
+            if (mRenderTarget != null) {
+                mRenderTarget.release();
+                mRenderTarget = null;
+            }
+        }
+
+        private void onReleaseFrames() {
+            mFrameManager.destroyBackings();
+        }
+
+        private void onInit() {
+            mThreadRunner.set(GraphRunner.this);
+            if (getContext().isOpenGLSupported()) {
+                mRenderTarget = RenderTarget.newTarget(1, 1);
+                mRenderTarget.focus();
+            }
+        }
+
+        private void onPrepare(FilterGraph graph) {
+            if (mState.current() == State.STOPPED) {
+                mState.setState(State.PREPARING);
+                mCaughtException = null;
+                onOpenGraph(graph);
+            }
+        }
+
+        private void onOpenGraph(FilterGraph graph) {
+            loadFilters(graph);
+            mOpenedGraphs.add(graph);
+            mScheduler.prepare(currentFilters());
+            pushEvent(BEGIN_EVENT);
+        }
+
+        private void onBegin() {
+            if (mState.current() == State.PREPARING) {
+                mState.setState(State.RUNNING);
+                pushEvent(STEP_EVENT);
+            }
+        }
+
+        private void onStarve() {
+            mFilters.pop();
+            if (mFilters.empty()) {
+                onStop();
+            } else {
+                SubListener listener = mSubListeners.pop();
+                if (listener != null) {
+                    listener.onSubGraphRunEnded(GraphRunner.this);
+                }
+                mScheduler.prepare(currentFilters());
+                pushEvent(STEP_EVENT);
+            }
+        }
+
+        private void onStop() {
+            if (mState.check(State.RUNNING)) {
+                // Close filters if not already halted (and already closed)
+                if (!mState.check(State.HALTED)) {
+                    closeAllFilters();
+                }
+                cleanUp();
+            }
+        }
+
+        private void cleanUp() {
+            mState.setState(State.STOPPED);
+            if (flushOnClose()) {
+                onFlush();
+            }
+            mOpenedGraphs.clear();
+            mFilters.clear();
+            onRunnerStopped(mCaughtException, mClosedSuccessfully);
+            mStopCondition.open();
+        }
+
+        private void onStep() {
+            if (mState.current() == State.RUNNING) {
+                Filter bestFilter = null;
+                long maxPriority = PRIORITY_STOP;
+                mScheduler.beginStep();
+                Filter[] filters = currentFilters();
+                for (int i = 0; i < filters.length; ++i) {
+                    Filter filter = filters[i];
+                    long priority = mScheduler.priorityForFilter(filter);
+                    if (priority > maxPriority) {
+                        maxPriority = priority;
+                        bestFilter = filter;
+                    }
+                }
+                if (maxPriority == PRIORITY_SLEEP) {
+                    // NOOP: When going into sleep mode, we simply do not schedule another node.
+                    // If some other event (such as a resume()) does schedule, then we may schedule
+                    // during sleeping. This is an edge case an irrelevant. (On the other hand,
+                    // going into a dedicated "sleep state" requires highly complex synchronization
+                    // to not "miss" a wake-up event. Thus we choose the more defensive approach
+                    // here).
+                } else if (maxPriority == PRIORITY_STOP) {
+                    onStarve();
+                } else {
+                    scheduleFilter(bestFilter);
+                    pushEvent(STEP_EVENT);
+                }
+            } else {
+                Log.w("GraphRunner", "State is not running! (" + mState.current() + ")");
+            }
+        }
+
+        private void onFlush() {
+           if (mState.check(State.HALTED) || mState.check(State.STOPPED)) {
+               for (FilterGraph graph : mOpenedGraphs) {
+                   graph.flushFrames();
+               }
+           }
+        }
+
+        private void onTearDown(FilterGraph graph) {
+            for (Filter filter : graph.getAllFilters()) {
+                filter.performTearDown();
+            }
+            graph.wipe();
+        }
+
+        private void loadFilters(FilterGraph graph) {
+            Filter[] filters = graph.getAllFilters();
+            mFilters.push(filters);
+        }
+
+        private void closeAllFilters() {
+            for (FilterGraph graph : mOpenedGraphs) {
+                closeFilters(graph);
+            }
+        }
+
+        private void closeFilters(FilterGraph graph) {
+            // [Non-iterator looping]
+            Log.v("GraphRunner", "CLOSING FILTERS");
+            Filter[] filters = graph.getAllFilters();
+            boolean isVerbose = isVerbose();
+            for (int i = 0; i < filters.length; ++i) {
+                if (isVerbose) {
+                    Log.i("GraphRunner", "Closing Filter " + filters[i] + "!");
+                }
+                filters[i].softReset();
+            }
+        }
+
+        private Filter[] currentFilters() {
+            return mFilters.peek();
+        }
+
+        private void scheduleFilter(Filter filter) {
+            long scheduleTime = 0;
+            if (isVerbose()) {
+                scheduleTime = SystemClock.elapsedRealtime();
+                Log.i("GraphRunner", scheduleTime + ": Scheduling " + filter + "!");
+            }
+            filter.execute();
+            if (isVerbose()) {
+                long nowTime = SystemClock.elapsedRealtime();
+                Log.i("GraphRunner",
+                        "-> Schedule time (" + filter + ") = " + (nowTime - scheduleTime) + " ms.");
+            }
+        }
+
+    }
+
+    // GraphRunner.Scheduler classes ///////////////////////////////////////////////////////////////
+    private interface Scheduler {
+        public void prepare(Filter[] filters);
+
+        public int getStrategy();
+
+        public void beginStep();
+
+        public long priorityForFilter(Filter filter);
+
+    }
+
+    private class LruScheduler implements Scheduler {
+
+        private long mNow;
+
+        @Override
+        public void prepare(Filter[] filters) {
+        }
+
+        @Override
+        public int getStrategy() {
+            return STRATEGY_LRU;
+        }
+
+        @Override
+        public void beginStep() {
+            // TODO(renn): We could probably do this with a simple GraphRunner counter that would
+            // represent GraphRunner local time. This would allow us to use integers instead of
+            // longs, and save us calls to the system clock.
+            mNow = SystemClock.elapsedRealtime();
+        }
+
+        @Override
+        public long priorityForFilter(Filter filter) {
+            if (filter.isSleeping()) {
+                return PRIORITY_SLEEP;
+            } else if (filter.canSchedule()) {
+                return mNow - filter.getLastScheduleTime();
+            } else {
+                return PRIORITY_STOP;
+            }
+        }
+
+    }
+
+    private class LfuScheduler implements Scheduler {
+
+        private final int MAX_PRIORITY = Integer.MAX_VALUE;
+
+        @Override
+        public void prepare(Filter[] filters) {
+            // [Non-iterator looping]
+            for (int i = 0; i < filters.length; ++i) {
+                filters[i].resetScheduleCount();
+            }
+        }
+
+        @Override
+        public int getStrategy() {
+            return STRATEGY_LFU;
+        }
+
+        @Override
+        public void beginStep() {
+        }
+
+        @Override
+        public long priorityForFilter(Filter filter) {
+            return filter.isSleeping() ? PRIORITY_SLEEP
+                    : (filter.canSchedule() ? (MAX_PRIORITY - filter.getScheduleCount())
+                            : PRIORITY_STOP);
+        }
+
+    }
+
+    private class OneShotScheduler extends LfuScheduler {
+        private int mCurCount = 1;
+
+        @Override
+        public void prepare(Filter[] filters) {
+            // [Non-iterator looping]
+            for (int i = 0; i < filters.length; ++i) {
+                filters[i].resetScheduleCount();
+            }
+        }
+
+        @Override
+        public int getStrategy() {
+            return STRATEGY_ONESHOT;
+        }
+
+        @Override
+        public void beginStep() {
+        }
+
+        @Override
+        public long priorityForFilter(Filter filter) {
+            return filter.getScheduleCount() < mCurCount ? super.priorityForFilter(filter)
+                    : PRIORITY_STOP;
+        }
+
+    }
+
+    // GraphRunner.Listener callback class /////////////////////////////////////////////////////////
+    public interface Listener {
+        /**
+         * Callback method that is called when the runner completes a run. This method is called
+         * only if the graph completed without an error.
+         */
+        public void onGraphRunnerStopped(GraphRunner runner);
+
+        /**
+         * Callback method that is called when runner encounters an error.
+         *
+         *  Any exceptions thrown in the GraphRunner's thread will cause the run to abort. The
+         * thrown exception is passed to the listener in this method. If no listener is set, the
+         * exception message is logged to the error stream. You will not receive an
+         * {@link #onGraphRunnerStopped(GraphRunner)} callback in case of an error.
+         *
+         * @param exception the exception that was thrown.
+         * @param closedSuccessfully true, if the graph was closed successfully after the error.
+         */
+        public void onGraphRunnerError(Exception exception, boolean closedSuccessfully);
+    }
+
+    public interface SubListener {
+        public void onSubGraphRunEnded(GraphRunner runner);
+    }
+
+    /**
+     * Config class to setup a GraphRunner with a custom configuration.
+     *
+     * The configuration object is passed to the constructor. Any changes to it will not affect
+     * the created GraphRunner instance.
+     */
+    public static class Config {
+        /** The runner's thread priority. */
+        public int threadPriority = Thread.NORM_PRIORITY;
+        /** Whether to allow filters to use OpenGL or not. */
+        public boolean allowOpenGL = true;
+    }
+
+    /** Parameters shared between run-thread and GraphRunner frontend. */
+    private class RunParameters {
+        public Listener listener = null;
+        public boolean isVerbose = false;
+        public boolean flushOnClose = true;
+    }
+
+    // GraphRunner implementation //////////////////////////////////////////////////////////////////
+    /** Schedule strategy: From set of candidates, pick a random one. */
+    public static final int STRATEGY_RANDOM = 1;
+    /** Schedule strategy: From set of candidates, pick node executed least recently executed. */
+    public static final int STRATEGY_LRU = 2;
+    /** Schedule strategy: From set of candidates, pick node executed least number of times. */
+    public static final int STRATEGY_LFU = 3;
+    /** Schedule strategy: Schedules no node more than once. */
+    public static final int STRATEGY_ONESHOT = 4;
+
+    private final MffContext mContext;
+
+    private FilterGraph mRunningGraph = null;
+    private Set<FilterGraph> mGraphs = new HashSet<FilterGraph>();
+
+    private Scheduler mScheduler;
+
+    private GraphRunLoop mRunLoop;
+
+    private Thread mRunThread = null;
+
+    private FrameManager mFrameManager = null;
+
+    private static ThreadLocal<GraphRunner> mThreadRunner = new ThreadLocal<GraphRunner>();
+
+    private RunParameters mParams = new RunParameters();
+
+    /**
+     * Creates a new GraphRunner with the default configuration. You must attach FilterGraph
+     * instances to this runner before you can execute any of these graphs.
+     *
+     * @param context The MffContext instance for this runner.
+     */
+    public GraphRunner(MffContext context) {
+        mContext = context;
+        init(new Config());
+    }
+
+    /**
+     * Creates a new GraphRunner with the specified configuration. You must attach FilterGraph
+     * instances to this runner before you can execute any of these graphs.
+     *
+     * @param context The MffContext instance for this runner.
+     * @param config A Config instance with the configuration of this runner.
+     */
+    public GraphRunner(MffContext context, Config config) {
+        mContext = context;
+        init(config);
+    }
+
+    /**
+     * Returns the currently running graph-runner.
+     * @return The currently running graph-runner.
+     */
+    public static GraphRunner current() {
+        return mThreadRunner.get();
+    }
+
+    /**
+     * Returns the graph that this runner is currently executing. Returns null if no graph is
+     * currently being executed by this runner.
+     *
+     * @return the FilterGraph instance that this GraphRunner is executing.
+     */
+    public synchronized FilterGraph getRunningGraph() {
+        return mRunningGraph;
+    }
+
+    /**
+     * Returns the context that this runner is bound to.
+     *
+     * @return the MffContext instance that this runner is bound to.
+     */
+    public MffContext getContext() {
+        return mContext;
+    }
+
+    /**
+     * Begins graph execution. The graph filters are scheduled and executed until processing
+     * finishes or is stopped.
+     */
+    public synchronized void start(FilterGraph graph) {
+        if (graph.mRunner != this) {
+            throw new IllegalArgumentException("Graph must be attached to runner!");
+        }
+        mRunningGraph = graph;
+        mRunLoop.getStopCondition().close();
+        mRunLoop.pushEvent(Event.PREPARE, graph);
+    }
+
+    /**
+     * Begin executing a sub-graph. This only succeeds if the current runner is already
+     * executing.
+     */
+    public void enterSubGraph(FilterGraph graph, SubListener listener) {
+        if (Thread.currentThread() != mRunThread) {
+            throw new RuntimeException("enterSubGraph must be called from the runner's thread!");
+        }
+        mRunLoop.enterSubGraph(graph, listener);
+    }
+
+    /**
+     * Waits until graph execution has finished or stopped with an error.
+     * Care must be taken when using this method to not block the UI thread. This is typically
+     * used when a graph is run in one-shot mode to compute a result.
+     */
+    public void waitUntilStop() {
+        mRunLoop.getStopCondition().block();
+    }
+
+    /**
+     * Pauses graph execution.
+     */
+    public void pause() {
+        mRunLoop.pushEvent(PAUSE_EVENT);
+    }
+
+    /**
+     * Resumes graph execution after pausing.
+     */
+    public void resume() {
+        mRunLoop.pushEvent(RESUME_EVENT);
+    }
+
+    /**
+     * Stops graph execution.
+     */
+    public void stop() {
+        mRunLoop.pushEvent(STOP_EVENT);
+    }
+
+    /**
+     * Returns whether the graph is currently being executed. A graph is considered to be running,
+     * even if it is paused or in the process of being stopped.
+     *
+     * @return true, if the graph is currently being executed.
+     */
+    public boolean isRunning() {
+        return !mRunLoop.checkState(State.STOPPED);
+    }
+
+    /**
+     * Returns whether the graph is currently paused.
+     *
+     * @return true, if the graph is currently paused.
+     */
+    public boolean isPaused() {
+        return mRunLoop.checkState(State.PAUSED);
+    }
+
+    /**
+     * Returns whether the graph is currently stopped.
+     *
+     * @return true, if the graph is currently stopped.
+     */
+    public boolean isStopped() {
+        return mRunLoop.checkState(State.STOPPED);
+    }
+
+    /**
+     * Sets the filter scheduling strategy. This method can not be called when the GraphRunner is
+     * running.
+     *
+     * @param strategy a constant specifying which scheduler strategy to use.
+     * @throws RuntimeException if the GraphRunner is running.
+     * @throws IllegalArgumentException if invalid strategy is specified.
+     * @see #getSchedulerStrategy()
+     */
+    public void setSchedulerStrategy(int strategy) {
+        if (isRunning()) {
+            throw new RuntimeException(
+                    "Attempting to change scheduling strategy on running " + "GraphRunner!");
+        }
+        createScheduler(strategy);
+    }
+
+    /**
+     * Returns the current scheduling strategy.
+     *
+     * @return the scheduling strategy used by this GraphRunner.
+     * @see #setSchedulerStrategy(int)
+     */
+    public int getSchedulerStrategy() {
+        return mScheduler.getStrategy();
+    }
+
+    /**
+     * Set whether or not the runner is verbose. When set to true, the runner will output individual
+     * scheduling steps that may help identify and debug problems in the graph structure. The
+     * default is false.
+     *
+     * @param isVerbose true, if the GraphRunner should log scheduling details.
+     * @see #isVerbose()
+     */
+    public void setIsVerbose(boolean isVerbose) {
+        synchronized (mParams) {
+            mParams.isVerbose = isVerbose;
+        }
+    }
+
+    /**
+     * Returns whether the GraphRunner is verbose.
+     *
+     * @return true, if the GraphRunner logs scheduling details.
+     * @see #setIsVerbose(boolean)
+     */
+    public boolean isVerbose() {
+        synchronized (mParams) {
+            return mParams.isVerbose;
+        }
+    }
+
+    /**
+     * Returns whether Filters of this GraphRunner can use OpenGL.
+     *
+     * Filters may use OpenGL if the MffContext supports OpenGL and the GraphRunner allows it.
+     *
+     * @return true, if Filters are allowed to use OpenGL.
+     */
+    public boolean isOpenGLSupported() {
+        return mRunLoop.isOpenGLAllowed() && mContext.isOpenGLSupported();
+    }
+
+    /**
+     * Enable flushing all frames from the graph when running completes.
+     *
+     * If this is set to false, then frames may remain in the pipeline even after running completes.
+     * The default value is true.
+     *
+     * @param flush true, if the GraphRunner should flush the graph when running completes.
+     * @see #flushOnClose()
+     */
+    public void setFlushOnClose(boolean flush) {
+        synchronized (mParams) {
+            mParams.flushOnClose = flush;
+        }
+    }
+
+    /**
+     * Returns whether the GraphRunner flushes frames when running completes.
+     *
+     * @return true, if the GraphRunner flushes frames when running completes.
+     * @see #setFlushOnClose(boolean)
+     */
+    public boolean flushOnClose() {
+        synchronized (mParams) {
+            return mParams.flushOnClose;
+        }
+    }
+
+    /**
+     * Sets the listener for receiving runtime events. A GraphRunner.Listener instance can be used
+     * to determine when certain events occur during graph execution (and react on them). See the
+     * {@link GraphRunner.Listener} class for details.
+     *
+     * @param listener the GraphRunner.Listener instance to set.
+     * @see #getListener()
+     */
+    public void setListener(Listener listener) {
+        synchronized (mParams) {
+            mParams.listener = listener;
+        }
+    }
+
+    /**
+     * Returns the currently assigned GraphRunner.Listener.
+     *
+     * @return the currently assigned GraphRunner.Listener instance.
+     * @see #setListener(Listener)
+     */
+    public Listener getListener() {
+        synchronized (mParams) {
+            return mParams.listener;
+        }
+    }
+
+    /**
+     * Returns the FrameManager that manages the runner's frames.
+     *
+     * @return the FrameManager instance that manages the runner's frames.
+     */
+    public FrameManager getFrameManager() {
+        return mFrameManager;
+    }
+
+    /**
+     * Tear down a GraphRunner and all its resources.
+     * <p>
+     * You must make sure that before calling this, no more graphs are attached to this runner.
+     * Typically, graphs are removed from runners when they are torn down.
+     *
+     * @throws IllegalStateException if there are still graphs attached to this runner.
+     */
+    public void tearDown() {
+        synchronized (mGraphs) {
+            if (!mGraphs.isEmpty()) {
+                throw new IllegalStateException("Attempting to tear down runner with "
+                        + mGraphs.size() + " graphs still attached!");
+            }
+        }
+        mRunLoop.pushEvent(KILL_EVENT);
+        // Wait for thread to complete, so that everything is torn down by the time we return.
+        try {
+            mRunThread.join();
+        } catch (InterruptedException e) {
+            Log.e("GraphRunner", "Error waiting for runner thread to finish!");
+        }
+    }
+
+    /**
+     * Release all frames managed by this runner.
+     * <p>
+     * Note, that you must make sure no graphs are attached to this runner before calling this
+     * method, as otherwise Filters in the graph may reference frames that are now released.
+     *
+     * TODO: Eventually, this method should be removed. Instead we should have better analysis
+     * that catches leaking frames from filters.
+     *
+     * @throws IllegalStateException if there are still graphs attached to this runner.
+     */
+    public void releaseFrames() {
+        synchronized (mGraphs) {
+            if (!mGraphs.isEmpty()) {
+                throw new IllegalStateException("Attempting to release frames with "
+                        + mGraphs.size() + " graphs still attached!");
+            }
+        }
+        mRunLoop.pushEvent(RELEASE_FRAMES_EVENT);
+    }
+
+    // Core internal methods ///////////////////////////////////////////////////////////////////////
+    void attachGraph(FilterGraph graph) {
+        synchronized (mGraphs) {
+            mGraphs.add(graph);
+        }
+    }
+
+    void signalWakeUp() {
+        mRunLoop.pushWakeEvent(STEP_EVENT);
+    }
+
+    void begin() {
+        mRunLoop.pushEvent(BEGIN_EVENT);
+    }
+
+    /** Like pause(), but closes all filters. Can be resumed using restart(). */
+    void halt() {
+        mRunLoop.pushEvent(HALT_EVENT);
+    }
+
+    /** Resumes a previously halted runner, and restores it to its non-halted state. */
+    void restart() {
+        mRunLoop.pushEvent(RESTART_EVENT);
+    }
+
+    /**
+     * Tears down the specified graph.
+     *
+     * The graph must be attached to this runner.
+     */
+    void tearDownGraph(FilterGraph graph) {
+        if (graph.getRunner() != this) {
+            throw new IllegalArgumentException("Attempting to tear down graph with foreign "
+                    + "GraphRunner!");
+        }
+        mRunLoop.pushEvent(Event.TEARDOWN, graph);
+        synchronized (mGraphs) {
+            mGraphs.remove(graph);
+        }
+    }
+
+    /**
+     * Remove all frames that are waiting to be processed.
+     *
+     * Removes and releases frames that are waiting in the graph connections of the currently
+     * halted graphs, i.e. frames that are waiting to be processed. This does not include frames
+     * that may be held or cached by filters themselves.
+     *
+     * TODO: With the new sub-graph architecture, this can now be simplified and made public.
+     * It can then no longer rely on opened graphs, and instead flush a graph and all its
+     * sub-graphs.
+     */
+    void flushFrames() {
+        mRunLoop.pushEvent(FLUSH_EVENT);
+    }
+
+    // Private methods /////////////////////////////////////////////////////////////////////////////
+    private void init(Config config) {
+        mFrameManager = new FrameManager(this, FrameManager.FRAME_CACHE_LRU);
+        createScheduler(STRATEGY_LRU);
+        mRunLoop = new GraphRunLoop(config.allowOpenGL);
+        mRunThread = new Thread(mRunLoop);
+        mRunThread.setPriority(config.threadPriority);
+        mRunThread.start();
+        mContext.addRunner(this);
+    }
+
+    private void createScheduler(int strategy) {
+        switch (strategy) {
+            case STRATEGY_LRU:
+                mScheduler = new LruScheduler();
+                break;
+            case STRATEGY_LFU:
+                mScheduler = new LfuScheduler();
+                break;
+            case STRATEGY_ONESHOT:
+                mScheduler = new OneShotScheduler();
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown schedule-strategy constant " + strategy + "!");
+        }
+    }
+
+    // Called within the runner's thread
+    private void onRunnerStopped(final Exception exception, final boolean closed) {
+        mRunningGraph = null;
+        synchronized (mParams) {
+            if (mParams.listener != null) {
+                getContext().postRunnable(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (exception == null) {
+                            mParams.listener.onGraphRunnerStopped(GraphRunner.this);
+                        } else {
+                            mParams.listener.onGraphRunnerError(exception, closed);
+                        }
+                    }
+                });
+            } else if (exception != null) {
+                Log.e("GraphRunner",
+                        "Uncaught exception during graph execution! Stack Trace: ");
+                exception.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ImageShader.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ImageShader.java
new file mode 100644
index 0000000..0ec50a3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ImageShader.java
@@ -0,0 +1,793 @@
+/*
+ * 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 androidx.media.filterfw;
+
+import android.graphics.RectF;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import androidx.media.filterfw.geometry.Quad;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * Convenience class to perform GL shader operations on image data.
+ * <p>
+ * The ImageShader class greatly simplifies the task of running GL shader language kernels over
+ * Frame data buffers that contain RGBA image data.
+ * </p><p>
+ * TODO: More documentation
+ * </p>
+ */
+public class ImageShader {
+
+    private int mProgram = 0;
+    private boolean mClearsOutput = false;
+    private float[] mClearColor = { 0f, 0f, 0f, 0f };
+    private boolean mBlendEnabled = false;
+    private int mSFactor = GLES20.GL_SRC_ALPHA;
+    private int mDFactor = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+    private int mDrawMode = GLES20.GL_TRIANGLE_STRIP;
+    private int mVertexCount = 4;
+    private int mBaseTexUnit = GLES20.GL_TEXTURE0;
+    private int mClearBuffers = GLES20.GL_COLOR_BUFFER_BIT;
+    private float[] mSourceCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
+    private float[] mTargetCoords = new float[] { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f };
+
+    private HashMap<String, ProgramUniform> mUniforms;
+    private HashMap<String, VertexAttribute> mAttributes = new HashMap<String, VertexAttribute>();
+
+    private final static int FLOAT_SIZE = 4;
+
+    private final static String mDefaultVertexShader =
+        "attribute vec4 a_position;\n" +
+        "attribute vec2 a_texcoord;\n" +
+        "varying vec2 v_texcoord;\n" +
+        "void main() {\n" +
+        "  gl_Position = a_position;\n" +
+        "  v_texcoord = a_texcoord;\n" +
+        "}\n";
+
+    private final static String mIdentityShader =
+        "precision mediump float;\n" +
+        "uniform sampler2D tex_sampler_0;\n" +
+        "varying vec2 v_texcoord;\n" +
+        "void main() {\n" +
+        "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+        "}\n";
+
+    private static class VertexAttribute {
+        private String mName;
+        private boolean mIsConst;
+        private int mIndex;
+        private boolean mShouldNormalize;
+        private int mOffset;
+        private int mStride;
+        private int mComponents;
+        private int mType;
+        private int mVbo;
+        private int mLength;
+        private FloatBuffer mValues;
+
+        public VertexAttribute(String name, int index) {
+            mName = name;
+            mIndex = index;
+            mLength = -1;
+        }
+
+        public void set(boolean normalize, int stride, int components, int type, float[] values) {
+            mIsConst = false;
+            mShouldNormalize = normalize;
+            mStride = stride;
+            mComponents = components;
+            mType = type;
+            mVbo = 0;
+            if (mLength != values.length){
+                initBuffer(values);
+                mLength = values.length;
+            }
+            copyValues(values);
+        }
+
+        public void set(boolean normalize, int offset, int stride, int components, int type,
+                int vbo){
+            mIsConst = false;
+            mShouldNormalize = normalize;
+            mOffset = offset;
+            mStride = stride;
+            mComponents = components;
+            mType = type;
+            mVbo = vbo;
+            mValues = null;
+        }
+
+        public boolean push() {
+            if (mIsConst) {
+                switch (mComponents) {
+                    case 1:
+                        GLES20.glVertexAttrib1fv(mIndex, mValues);
+                        break;
+                    case 2:
+                        GLES20.glVertexAttrib2fv(mIndex, mValues);
+                        break;
+                    case 3:
+                        GLES20.glVertexAttrib3fv(mIndex, mValues);
+                        break;
+                    case 4:
+                        GLES20.glVertexAttrib4fv(mIndex, mValues);
+                        break;
+                    default:
+                        return false;
+                }
+                GLES20.glDisableVertexAttribArray(mIndex);
+            } else {
+                if (mValues != null) {
+                    // Note that we cannot do any size checking here, as the correct component
+                    // count depends on the drawing step. GL should catch such errors then, and
+                    // we will report them to the user.
+                    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+                    GLES20.glVertexAttribPointer(mIndex,
+                                                 mComponents,
+                                                 mType,
+                                                 mShouldNormalize,
+                                                 mStride,
+                                                 mValues);
+                } else {
+                    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo);
+                    GLES20.glVertexAttribPointer(mIndex,
+                                                 mComponents,
+                                                 mType,
+                                                 mShouldNormalize,
+                                                 mStride,
+                                                 mOffset);
+                }
+                GLES20.glEnableVertexAttribArray(mIndex);
+            }
+            GLToolbox.checkGlError("Set vertex-attribute values");
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return mName;
+        }
+
+        private void initBuffer(float[] values) {
+            mValues = ByteBuffer.allocateDirect(values.length * FLOAT_SIZE)
+                .order(ByteOrder.nativeOrder()).asFloatBuffer();
+        }
+
+        private void copyValues(float[] values) {
+            mValues.put(values).position(0);
+        }
+
+    }
+
+    private static final class ProgramUniform {
+        private String mName;
+        private int mLocation;
+        private int mType;
+        private int mSize;
+
+        public ProgramUniform(int program, int index) {
+            int[] len = new int[1];
+            GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, len, 0);
+
+            int[] type = new int[1];
+            int[] size = new int[1];
+            byte[] name = new byte[len[0]];
+            int[] ignore = new int[1];
+
+            GLES20.glGetActiveUniform(program, index, len[0], ignore, 0, size, 0, type, 0, name, 0);
+            mName = new String(name, 0, strlen(name));
+            mLocation = GLES20.glGetUniformLocation(program, mName);
+            mType = type[0];
+            mSize = size[0];
+            GLToolbox.checkGlError("Initializing uniform");
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        public int getLocation() {
+            return mLocation;
+        }
+
+        public int getSize() {
+            return mSize;
+        }
+    }
+
+    public ImageShader(String fragmentShader) {
+        mProgram = createProgram(mDefaultVertexShader, fragmentShader);
+        scanUniforms();
+    }
+
+    public ImageShader(String vertexShader, String fragmentShader) {
+        mProgram = createProgram(vertexShader, fragmentShader);
+        scanUniforms();
+    }
+
+    public static ImageShader createIdentity() {
+        return new ImageShader(mIdentityShader);
+    }
+
+    public static ImageShader createIdentity(String vertexShader) {
+        return new ImageShader(vertexShader, mIdentityShader);
+    }
+
+    public static void renderTextureToTarget(TextureSource texture,
+                                             RenderTarget target,
+                                             int width,
+                                             int height) {
+        ImageShader shader = RenderTarget.currentTarget().getIdentityShader();
+        shader.process(texture, target, width, height);
+    }
+
+    public void process(FrameImage2D input, FrameImage2D output) {
+        TextureSource texSource = input.lockTextureSource();
+        RenderTarget renderTarget = output.lockRenderTarget();
+        processMulti(new TextureSource[] { texSource },
+                     renderTarget,
+                     output.getWidth(),
+                     output.getHeight());
+        input.unlock();
+        output.unlock();
+    }
+
+    public void processMulti(FrameImage2D[] inputs, FrameImage2D output) {
+        TextureSource[] texSources = new TextureSource[inputs.length];
+        for (int i = 0; i < inputs.length; ++i) {
+            texSources[i] = inputs[i].lockTextureSource();
+        }
+        RenderTarget renderTarget = output.lockRenderTarget();
+        processMulti(texSources,
+                     renderTarget,
+                     output.getWidth(),
+                     output.getHeight());
+        for (FrameImage2D input : inputs) {
+            input.unlock();
+        }
+        output.unlock();
+    }
+
+    public void process(TextureSource texture, RenderTarget target, int width, int height) {
+        processMulti(new TextureSource[] { texture }, target, width, height);
+    }
+
+    public void processMulti(TextureSource[] sources, RenderTarget target, int width, int height) {
+        GLToolbox.checkGlError("Unknown Operation");
+        checkExecutable();
+        checkTexCount(sources.length);
+        focusTarget(target, width, height);
+        pushShaderState();
+        bindInputTextures(sources);
+        render();
+    }
+
+    public void processNoInput(FrameImage2D output) {
+        RenderTarget renderTarget = output.lockRenderTarget();
+        processNoInput(renderTarget, output.getWidth(), output.getHeight());
+        output.unlock();
+    }
+
+    public void processNoInput(RenderTarget target, int width, int height) {
+        processMulti(new TextureSource[] {}, target, width, height);
+    }
+
+    public int getUniformLocation(String name) {
+        return getProgramUniform(name, true).getLocation();
+    }
+
+    public int getAttributeLocation(String name) {
+        if (name.equals(positionAttributeName()) || name.equals(texCoordAttributeName())) {
+            Log.w("ImageShader", "Attempting to access internal attribute '" + name
+                + "' directly!");
+        }
+        int loc = GLES20.glGetAttribLocation(mProgram, name);
+        if (loc < 0) {
+            throw new RuntimeException("Unknown attribute '" + name + "' in shader program!");
+        }
+        return loc;
+    }
+
+    public void setUniformValue(String uniformName, int value) {
+        useProgram();
+        int uniformHandle = getUniformLocation(uniformName);
+        GLES20.glUniform1i(uniformHandle, value);
+        GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
+    }
+
+    public void setUniformValue(String uniformName, float value) {
+        useProgram();
+        int uniformHandle = getUniformLocation(uniformName);
+        GLES20.glUniform1f(uniformHandle, value);
+        GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
+    }
+
+    public void setUniformValue(String uniformName, int[] values) {
+        ProgramUniform uniform = getProgramUniform(uniformName, true);
+        useProgram();
+        int len = values.length;
+        switch (uniform.getType()) {
+            case GLES20.GL_INT:
+                checkUniformAssignment(uniform, len, 1);
+                GLES20.glUniform1iv(uniform.getLocation(), len, values, 0);
+                break;
+            case GLES20.GL_INT_VEC2:
+                checkUniformAssignment(uniform, len, 2);
+                GLES20.glUniform2iv(uniform.getLocation(), len / 2, values, 0);
+                break;
+            case GLES20.GL_INT_VEC3:
+                checkUniformAssignment(uniform, len, 3);
+                GLES20.glUniform2iv(uniform.getLocation(), len / 3, values, 0);
+                break;
+            case GLES20.GL_INT_VEC4:
+                checkUniformAssignment(uniform, len, 4);
+                GLES20.glUniform2iv(uniform.getLocation(), len / 4, values, 0);
+                break;
+            default:
+                throw new RuntimeException("Cannot assign int-array to incompatible uniform type "
+                    + "for uniform '" + uniformName + "'!");
+        }
+        GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
+    }
+
+
+    public void setUniformValue(String uniformName, float[] values) {
+        ProgramUniform uniform = getProgramUniform(uniformName, true);
+        useProgram();
+        int len = values.length;
+        switch (uniform.getType()) {
+            case GLES20.GL_FLOAT:
+                checkUniformAssignment(uniform, len, 1);
+                GLES20.glUniform1fv(uniform.getLocation(), len, values, 0);
+                break;
+            case GLES20.GL_FLOAT_VEC2:
+                checkUniformAssignment(uniform, len, 2);
+                GLES20.glUniform2fv(uniform.getLocation(), len / 2, values, 0);
+                break;
+            case GLES20.GL_FLOAT_VEC3:
+                checkUniformAssignment(uniform, len, 3);
+                GLES20.glUniform3fv(uniform.getLocation(), len / 3, values, 0);
+                break;
+            case GLES20.GL_FLOAT_VEC4:
+                checkUniformAssignment(uniform, len, 4);
+                GLES20.glUniform4fv(uniform.getLocation(), len / 4, values, 0);
+                break;
+            case GLES20.GL_FLOAT_MAT2:
+                checkUniformAssignment(uniform, len, 4);
+                GLES20.glUniformMatrix2fv(uniform.getLocation(), len / 4, false, values, 0);
+                break;
+            case GLES20.GL_FLOAT_MAT3:
+                checkUniformAssignment(uniform, len, 9);
+                GLES20.glUniformMatrix3fv(uniform.getLocation(), len / 9, false, values, 0);
+                break;
+            case GLES20.GL_FLOAT_MAT4:
+                checkUniformAssignment(uniform, len, 16);
+                GLES20.glUniformMatrix4fv(uniform.getLocation(), len / 16, false, values, 0);
+                break;
+            default:
+                throw new RuntimeException("Cannot assign float-array to incompatible uniform type "
+                    + "for uniform '" + uniformName + "'!");
+        }
+        GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
+    }
+
+    public void setAttributeValues(String attributeName, float[] data, int components) {
+        VertexAttribute attr = getProgramAttribute(attributeName, true);
+        attr.set(false, FLOAT_SIZE * components, components, GLES20.GL_FLOAT, data);
+    }
+
+    public void setAttributeValues(String attributeName, int vbo, int type, int components,
+                                   int stride, int offset, boolean normalize) {
+        VertexAttribute attr = getProgramAttribute(attributeName, true);
+        attr.set(normalize, offset, stride, components, type, vbo);
+    }
+
+    public void setSourceRect(float x, float y, float width, float height) {
+        setSourceCoords(new float[] { x, y, x + width, y, x, y + height, x + width, y + height });
+    }
+
+    public void setSourceRect(RectF rect) {
+        setSourceRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+    }
+
+    public void setSourceQuad(Quad quad) {
+        setSourceCoords(new float[] { quad.topLeft().x,     quad.topLeft().y,
+                                      quad.topRight().x,    quad.topRight().y,
+                                      quad.bottomLeft().x,  quad.bottomLeft().y,
+                                      quad.bottomRight().x, quad.bottomRight().y });
+    }
+
+    public void setSourceCoords(float[] coords) {
+        if (coords.length != 8) {
+            throw new IllegalArgumentException("Expected 8 coordinates as source coordinates but "
+                + "got " + coords.length + " coordinates!");
+        }
+        mSourceCoords = Arrays.copyOf(coords, 8);
+    }
+
+    public void setSourceTransform(float[] matrix) {
+        if (matrix.length != 16) {
+            throw new IllegalArgumentException("Expected 4x4 matrix for source transform!");
+        }
+        setSourceCoords(new float[] {
+            matrix[12],
+            matrix[13],
+
+            matrix[0] + matrix[12],
+            matrix[1] + matrix[13],
+
+            matrix[4] + matrix[12],
+            matrix[5] + matrix[13],
+
+            matrix[0] + matrix[4] + matrix[12],
+            matrix[1] + matrix[5] + matrix[13],
+        });
+    }
+
+    public void setTargetRect(float x, float y, float width, float height) {
+        setTargetCoords(new float[] { x, y,
+                                      x + width, y,
+                                      x, y + height,
+                                      x + width, y + height });
+    }
+
+    public void setTargetRect(RectF rect) {
+        setTargetCoords(new float[] { rect.left,    rect.top,
+                                      rect.right,   rect.top,
+                                      rect.left,    rect.bottom,
+                                      rect.right,   rect.bottom });
+    }
+
+    public void setTargetQuad(Quad quad) {
+        setTargetCoords(new float[] { quad.topLeft().x,     quad.topLeft().y,
+                                      quad.topRight().x,    quad.topRight().y,
+                                      quad.bottomLeft().x,  quad.bottomLeft().y,
+                                      quad.bottomRight().x, quad.bottomRight().y });
+    }
+
+    public void setTargetCoords(float[] coords) {
+        if (coords.length != 8) {
+            throw new IllegalArgumentException("Expected 8 coordinates as target coordinates but "
+                + "got " + coords.length + " coordinates!");
+        }
+        mTargetCoords = new float[8];
+        for (int i = 0; i < 8; ++i) {
+            mTargetCoords[i] = coords[i] * 2f - 1f;
+        }
+    }
+
+    public void setTargetTransform(float[] matrix) {
+        if (matrix.length != 16) {
+            throw new IllegalArgumentException("Expected 4x4 matrix for target transform!");
+        }
+        setTargetCoords(new float[] {
+            matrix[12],
+            matrix[13],
+
+            matrix[0] + matrix[12],
+            matrix[1] + matrix[13],
+
+            matrix[4] + matrix[12],
+            matrix[5] + matrix[13],
+
+            matrix[0] + matrix[4] + matrix[12],
+            matrix[1] + matrix[5] + matrix[13],
+        });
+    }
+
+    public void setClearsOutput(boolean clears) {
+        mClearsOutput = clears;
+    }
+
+    public boolean getClearsOutput() {
+        return mClearsOutput;
+    }
+
+    public void setClearColor(float[] rgba) {
+        mClearColor = rgba;
+    }
+
+    public float[] getClearColor() {
+        return mClearColor;
+    }
+
+    public void setClearBufferMask(int bufferMask) {
+        mClearBuffers = bufferMask;
+    }
+
+    public int getClearBufferMask() {
+        return mClearBuffers;
+    }
+
+    public void setBlendEnabled(boolean enable) {
+        mBlendEnabled = enable;
+    }
+
+    public boolean getBlendEnabled() {
+        return mBlendEnabled;
+    }
+
+    public void setBlendFunc(int sFactor, int dFactor) {
+        mSFactor = sFactor;
+        mDFactor = dFactor;
+    }
+
+    public void setDrawMode(int drawMode) {
+        mDrawMode = drawMode;
+    }
+
+    public int getDrawMode() {
+        return mDrawMode;
+    }
+
+    public void setVertexCount(int count) {
+        mVertexCount = count;
+    }
+
+    public int getVertexCount() {
+        return mVertexCount;
+    }
+
+    public void setBaseTextureUnit(int baseTexUnit) {
+        mBaseTexUnit = baseTexUnit;
+    }
+
+    public int baseTextureUnit() {
+        return mBaseTexUnit;
+    }
+
+    public String texCoordAttributeName() {
+        return "a_texcoord";
+    }
+
+    public String positionAttributeName() {
+        return "a_position";
+    }
+
+    public String inputTextureUniformName(int index) {
+        return "tex_sampler_" + index;
+    }
+
+    public static int maxTextureUnits() {
+        return GLES20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        GLES20.glDeleteProgram(mProgram);
+    }
+
+    protected void pushShaderState() {
+        useProgram();
+        updateSourceCoordAttribute();
+        updateTargetCoordAttribute();
+        pushAttributes();
+        if (mClearsOutput) {
+            GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]);
+            GLES20.glClear(mClearBuffers);
+        }
+        if (mBlendEnabled) {
+            GLES20.glEnable(GLES20.GL_BLEND);
+            GLES20.glBlendFunc(mSFactor, mDFactor);
+        } else {
+            GLES20.glDisable(GLES20.GL_BLEND);
+        }
+        GLToolbox.checkGlError("Set render variables");
+    }
+
+    private void focusTarget(RenderTarget target, int width, int height) {
+        target.focus();
+        GLES20.glViewport(0, 0, width, height);
+        GLToolbox.checkGlError("glViewport");
+    }
+
+    private void bindInputTextures(TextureSource[] sources) {
+        for (int i = 0; i < sources.length; ++i) {
+            // Activate texture unit i
+            GLES20.glActiveTexture(baseTextureUnit() + i);
+
+            // Bind texture
+            sources[i].bind();
+
+            // Assign the texture uniform in the shader to unit i
+            int texUniform = GLES20.glGetUniformLocation(mProgram, inputTextureUniformName(i));
+            if (texUniform >= 0) {
+                GLES20.glUniform1i(texUniform, i);
+            } else {
+                throw new RuntimeException("Shader does not seem to support " + sources.length
+                    + " number of input textures! Missing uniform " + inputTextureUniformName(i)
+                    + "!");
+            }
+            GLToolbox.checkGlError("Binding input texture " + i);
+        }
+    }
+
+    private void pushAttributes() {
+        for (VertexAttribute attr : mAttributes.values()) {
+            if (!attr.push()) {
+                throw new RuntimeException("Unable to assign attribute value '" + attr + "'!");
+            }
+        }
+        GLToolbox.checkGlError("Push Attributes");
+    }
+
+    private void updateSourceCoordAttribute() {
+        // If attribute does not exist, simply do nothing (may be custom shader).
+        VertexAttribute attr = getProgramAttribute(texCoordAttributeName(), false);
+        // A non-null value of mSourceCoords indicates new values to be set.
+        if (mSourceCoords != null && attr != null) {
+            // Upload new source coordinates to GPU
+            attr.set(false, FLOAT_SIZE * 2, 2, GLES20.GL_FLOAT, mSourceCoords);
+        }
+        // Do not set again (even if failed, to not cause endless attempts)
+        mSourceCoords = null;
+    }
+
+    private void updateTargetCoordAttribute() {
+        // If attribute does not exist, simply do nothing (may be custom shader).
+        VertexAttribute attr = getProgramAttribute(positionAttributeName(), false);
+        // A non-null value of mTargetCoords indicates new values to be set.
+        if (mTargetCoords != null && attr != null) {
+            // Upload new target coordinates to GPU
+            attr.set(false, FLOAT_SIZE * 2, 2, GLES20.GL_FLOAT, mTargetCoords);
+        }
+        // Do not set again (even if failed, to not cause endless attempts)
+        mTargetCoords = null;
+    }
+
+    private void render() {
+        GLES20.glDrawArrays(mDrawMode, 0, mVertexCount);
+        GLToolbox.checkGlError("glDrawArrays");
+    }
+
+    private void checkExecutable() {
+        if (mProgram == 0) {
+            throw new RuntimeException("Attempting to execute invalid shader-program!");
+        }
+    }
+
+    private void useProgram() {
+        GLES20.glUseProgram(mProgram);
+        GLToolbox.checkGlError("glUseProgram");
+    }
+
+    private static void checkTexCount(int count) {
+        if (count > maxTextureUnits()) {
+            throw new RuntimeException("Number of textures passed (" + count + ") exceeds the "
+                + "maximum number of allowed texture units (" + maxTextureUnits() + ")!");
+        }
+    }
+
+    private static int loadShader(int shaderType, String source) {
+        int shader = GLES20.glCreateShader(shaderType);
+        if (shader != 0) {
+            GLES20.glShaderSource(shader, source);
+            GLES20.glCompileShader(shader);
+            int[] compiled = new int[1];
+            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+            if (compiled[0] == 0) {
+                String info = GLES20.glGetShaderInfoLog(shader);
+                GLES20.glDeleteShader(shader);
+                shader = 0;
+                throw new RuntimeException("Could not compile shader " + shaderType + ":" + info);
+            }
+        }
+        return shader;
+    }
+
+    private static int createProgram(String vertexSource, String fragmentSource) {
+        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+        if (vertexShader == 0) {
+            throw new RuntimeException("Could not create shader-program as vertex shader "
+                + "could not be compiled!");
+        }
+        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+        if (pixelShader == 0) {
+            throw new RuntimeException("Could not create shader-program as fragment shader "
+                + "could not be compiled!");
+        }
+
+        int program = GLES20.glCreateProgram();
+        if (program != 0) {
+            GLES20.glAttachShader(program, vertexShader);
+            GLToolbox.checkGlError("glAttachShader");
+            GLES20.glAttachShader(program, pixelShader);
+            GLToolbox.checkGlError("glAttachShader");
+            GLES20.glLinkProgram(program);
+            int[] linkStatus = new int[1];
+            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+            if (linkStatus[0] != GLES20.GL_TRUE) {
+                String info = GLES20.glGetProgramInfoLog(program);
+                GLES20.glDeleteProgram(program);
+                program = 0;
+                throw new RuntimeException("Could not link program: " + info);
+            }
+        }
+
+        GLES20.glDeleteShader(vertexShader);
+        GLES20.glDeleteShader(pixelShader);
+
+        return program;
+    }
+
+    private void scanUniforms() {
+        int uniformCount[] = new int [1];
+        GLES20.glGetProgramiv(mProgram, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, 0);
+        if (uniformCount[0] > 0) {
+            mUniforms = new HashMap<String, ProgramUniform>(uniformCount[0]);
+            for (int i = 0; i < uniformCount[0]; ++i) {
+                ProgramUniform uniform = new ProgramUniform(mProgram, i);
+                mUniforms.put(uniform.getName(), uniform);
+            }
+        }
+    }
+
+    private ProgramUniform getProgramUniform(String name, boolean required) {
+        ProgramUniform result = mUniforms.get(name);
+        if (result == null && required) {
+            throw new IllegalArgumentException("Unknown uniform '" + name + "'!");
+        }
+        return result;
+    }
+
+    private VertexAttribute getProgramAttribute(String name, boolean required) {
+        VertexAttribute result = mAttributes.get(name);
+        if (result == null) {
+            int handle = GLES20.glGetAttribLocation(mProgram, name);
+            if (handle >= 0) {
+                result = new VertexAttribute(name, handle);
+                mAttributes.put(name, result);
+            } else if (required) {
+                throw new IllegalArgumentException("Unknown attribute '" + name + "'!");
+            }
+        }
+        return result;
+    }
+
+    private void checkUniformAssignment(ProgramUniform uniform, int values, int components) {
+        if (values % components != 0) {
+            throw new RuntimeException("Size mismatch: Attempting to assign values of size "
+                + values + " to uniform '" + uniform.getName() + "' (must be multiple of "
+                + components + ")!");
+        } else if (uniform.getSize() != values / components) {
+            throw new RuntimeException("Size mismatch: Cannot assign " + values + " values to "
+                + "uniform '" + uniform.getName() + "'!");
+        }
+    }
+
+    private static int strlen(byte[] strVal) {
+        for (int i = 0; i < strVal.length; ++i) {
+            if (strVal[i] == '\0') {
+                return i;
+            }
+        }
+        return strVal.length;
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/InputPort.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/InputPort.java
new file mode 100644
index 0000000..82749c5
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/InputPort.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.lang.reflect.Field;
+
+/**
+ * Input ports are the receiving ports of frames in a filter.
+ * <p>
+ * InputPort instances receive Frame data from connected OutputPort instances of a previous filter.
+ * Frames flow from output ports to input ports. Filters can process frame data by calling
+ * {@link #pullFrame()} on an input port. If the input port is set to wait for an input frame
+ * (see {@link #setWaitsForFrame(boolean)}), there is guaranteed to be Frame on the port before
+ * {@code onProcess()} is called. This is the default setting. Otherwise, calling
+ * {@link #pullFrame()} may return a value of {@code null}.
+ * <p/><p>
+ * InputPorts may be bound to fields of the Filter. When an input port is bound to a field, Frame
+ * values will be assigned to the field once a Frame is received on that port. The Frame value must
+ * be of a type that is compatible with the field type.
+ * </p>
+ */
+public final class InputPort {
+
+    private Filter mFilter;
+    private String mName;
+    private Signature.PortInfo mInfo;
+    private FrameListener mListener = null;
+    private FrameQueue.Builder mQueueBuilder = null;
+    private FrameQueue mQueue = null;
+    private boolean mWaitForFrame = true;
+    private boolean mAutoPullEnabled = false;
+
+    public interface FrameListener {
+        public void onFrameReceived(InputPort port, Frame frame);
+    }
+
+    private class FieldBinding implements FrameListener {
+        private Field mField;
+
+        public FieldBinding(Field field) {
+            mField = field;
+        }
+
+        @Override
+        public void onFrameReceived(InputPort port, Frame frame) {
+            try {
+                if(port.mInfo.type.getNumberOfDimensions() > 0) {
+                    FrameValues frameValues = frame.asFrameValues();
+                    mField.set(mFilter, frameValues.getValues());
+                } else {
+                    FrameValue frameValue = frame.asFrameValue();
+                    mField.set(mFilter, frameValue.getValue());
+                }
+            } catch (Exception e) {
+                throw new RuntimeException("Assigning frame " + frame + " to field "
+                    + mField + " of filter " + mFilter + " caused exception!", e);
+            }
+        }
+    }
+
+    /**
+     * Attach this input port to an output port for frame passing.
+     *
+     * Use this method whenever you plan on passing a Frame through from an input port to an
+     * output port. This must be called from inside
+     * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}.
+     *
+     * @param outputPort the output port that Frames will be pushed to.
+     */
+    public void attachToOutputPort(OutputPort outputPort) {
+        assertInAttachmentStage();
+        mFilter.openOutputPort(outputPort);
+        mQueueBuilder.attachQueue(outputPort.getQueue());
+    }
+
+    /**
+     * Bind this input port to the specified listener.
+     *
+     * Use this when you wish to be notified of incoming frames. The listener method
+     * {@link FrameListener#onFrameReceived(InputPort, Frame)} will be called once a Frame is pulled
+     * on this port. Typically this is called from inside
+     * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
+     * conjunction with {@link #setAutoPullEnabled(boolean)}. Overrides any previous bindings.
+     *
+     * @param listener the listener to handle incoming Frames.
+     */
+    public void bindToListener(FrameListener listener) {
+        assertInAttachmentStage();
+        mListener = listener;
+    }
+
+    /**
+     * Bind this input port to the specified field.
+     *
+     * Use this when you wish to pull frames directly into a field of the filter. This requires
+     * that the input frames can be interpreted as object-based frames of the field's class.
+     * Overrides any previous bindings.
+     *
+     * This is typically called from inside
+     * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
+     * conjunction with {@link #setAutoPullEnabled(boolean)}.
+     *
+     * @param field the field to pull frame data into.
+     * @see #bindToFieldNamed(String)
+     * @see #setAutoPullEnabled(boolean)
+     */
+    public void bindToField(Field field) {
+        assertInAttachmentStage();
+        mListener = new FieldBinding(field);
+    }
+
+    /**
+     * Bind this input port to the field with the specified name.
+     *
+     * Use this when you wish to pull frames directly into a field of the filter. This requires
+     * that the input frames can be interpreted as object-based frames of the field's class.
+     * Overrides any previous bindings.
+     *
+     * This is typically called from inside
+     * {@link Filter#onInputPortAttached(InputPort) onInputPortAttached}, and used in
+     * conjunction with {@link #setAutoPullEnabled(boolean)}.
+     *
+     * @param fieldName the field to pull frame data into.
+     * @see #bindToField(Field)
+     * @see #setAutoPullEnabled(boolean)
+     */
+    public void bindToFieldNamed(String fieldName) {
+        Field field = findFieldNamed(fieldName, mFilter.getClass());
+        if (field == null) {
+            throw new IllegalArgumentException("Attempting to bind to unknown field '"
+                + fieldName + "'!");
+        }
+        bindToField(field);
+    }
+
+    /**
+     * Set whether the InputPort automatically pulls frames.
+     * This is typically only used when the port is bound to another target.
+     * @param enabled true, if frames should be automatically pulled on this port.
+     */
+    public void setAutoPullEnabled(boolean enabled) {
+        mAutoPullEnabled = enabled;
+    }
+
+    /**
+     * Returns whether the InputPort automatically pulls frames.
+     * @return true, if frames are automatically pulled on this port.
+     */
+    public boolean isAutoPullEnabled() {
+        return mAutoPullEnabled;
+    }
+
+    /**
+     * Pull a waiting a frame from the port.
+     *
+     * Call this to pull a frame from the input port for processing. If no frame is waiting on the
+     * input port, returns null. After this call the port will have no Frame waiting (empty port).
+     * Note, that this returns a frame owned by the input queue. You must detach the frame if you
+     * wish to hold on to it.
+     *
+     * @return Frame instance, or null if no frame is available for pulling.
+     */
+    public synchronized Frame pullFrame() {
+        if (mQueue == null) {
+            throw new IllegalStateException("Cannot pull frame from closed input port!");
+        }
+        Frame frame = mQueue.pullFrame();
+        if (frame != null) {
+            if (mListener != null) {
+                mListener.onFrameReceived(this, frame);
+            }
+            //Log.i("InputPort", "Adding frame " + frame + " to auto-release pool");
+            mFilter.addAutoReleaseFrame(frame);
+            long timestamp = frame.getTimestamp();
+            if (timestamp != Frame.TIMESTAMP_NOT_SET) {
+                mFilter.onPulledFrameWithTimestamp(frame.getTimestamp());
+            }
+        }
+        return frame;
+    }
+
+    public synchronized Frame peek() {
+        if (mQueue == null) {
+            throw new IllegalStateException("Cannot pull frame from closed input port!");
+        }
+        return mQueue.peek();
+    }
+
+    /**
+     * Returns true, if the port is connected.
+     * @return true, if there is an output port that connects to this port.
+     */
+    public boolean isConnected() {
+        return mQueue != null;
+    }
+
+    /**
+     * Returns true, if there is a frame waiting on this port.
+     * @return true, if there is a frame waiting on this port.
+     */
+    public synchronized boolean hasFrame() {
+        return mQueue != null && mQueue.canPull();
+    }
+
+    /**
+     * Sets whether to wait for a frame on this port before processing.
+     * When set to true, the Filter will not be scheduled for processing unless there is a Frame
+     * waiting on this port. The default value is true.
+     *
+     * @param wait true, if the Filter should wait for a Frame before processing.
+     * @see #waitsForFrame()
+     */
+    public void setWaitsForFrame(boolean wait) {
+        mWaitForFrame = wait;
+    }
+
+    /**
+     * Returns whether the filter waits for a frame on this port before processing.
+     * @return true, if the filter waits for a frame on this port before processing.
+     * @see #setWaitsForFrame(boolean)
+     */
+    public boolean waitsForFrame() {
+        return mWaitForFrame;
+    }
+
+    /**
+     * Returns the input port's name.
+     * This is the name that was specified when the input port was connected.
+     *
+     * @return the input port's name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the FrameType of this port.
+     * This is the type that was specified when the input port was declared.
+     *
+     * @return the input port's FrameType.
+     */
+    public FrameType getType() {
+        return getQueue().getType();
+    }
+
+    /**
+     * Return the filter object that this port belongs to.
+     *
+     * @return the input port's filter.
+     */
+    public Filter getFilter() {
+        return mFilter;
+    }
+
+    @Override
+    public String toString() {
+        return mFilter.getName() + ":" + mName;
+    }
+
+    // Internal only ///////////////////////////////////////////////////////////////////////////////
+    InputPort(Filter filter, String name, Signature.PortInfo info) {
+        mFilter = filter;
+        mName = name;
+        mInfo = info;
+    }
+
+    boolean conditionsMet() {
+        return !mWaitForFrame || hasFrame();
+    }
+
+    void onOpen(FrameQueue.Builder builder) {
+        mQueueBuilder = builder;
+        mQueueBuilder.setReadType(mInfo.type);
+        mFilter.onInputPortOpen(this);
+    }
+
+    void setQueue(FrameQueue queue) {
+        mQueue = queue;
+        mQueueBuilder = null;
+    }
+
+    FrameQueue getQueue() {
+        return mQueue;
+    }
+
+    void clear() {
+        if (mQueue != null) {
+            mQueue.clear();
+        }
+    }
+
+    private void assertInAttachmentStage() {
+        if (mQueueBuilder == null) {
+            throw new IllegalStateException("Attempting to attach port while not in attachment "
+                + "stage!");
+        }
+    }
+
+    private Field findFieldNamed(String fieldName, Class<?> clazz) {
+        Field field = null;
+        try {
+            field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            Class<?> superClass = clazz.getSuperclass();
+            if (superClass != null) {
+                field = findFieldNamed(fieldName, superClass);
+            }
+        }
+        return field;
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
new file mode 100644
index 0000000..b7212f9
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.renderscript.RenderScript;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The MffContext holds the state and resources of a Mobile Filter Framework processing instance.
+ * Though it is possible to create multiple MffContext instances, typical applications will rely on
+ * a single MffContext to perform all processing within the Mobile Filter Framework.
+ *
+ * The MffContext class declares two methods {@link #onPause()} and {@link #onResume()}, that are
+ * typically called when the application activity is paused and resumed. This will take care of
+ * halting any processing in the context, and releasing resources while the activity is paused.
+ */
+public class MffContext {
+
+    /**
+     * Class to hold configuration information for MffContexts.
+     */
+    public static class Config {
+        /**
+         * Set to true, if this context will make use of the camera.
+         * If your application does not require the camera, the context does not guarantee that
+         * a camera is available for streaming. That is, you may only use a CameraStreamer if
+         * the context's {@link #isCameraStreamingSupported()} returns true.
+         */
+        public boolean requireCamera = true;
+
+        /**
+         * Set to true, if this context requires OpenGL.
+         * If your application does not require OpenGL, the context does not guarantee that OpenGL
+         * is available. That is, you may only use OpenGL (within filters running in this context)
+         * if the context's {@link #isOpenGLSupported()} method returns true.
+         */
+        public boolean requireOpenGL = true;
+
+        /**
+         * On older Android versions the Camera may need a SurfaceView to render into in order to
+         * function. You may specify a dummy SurfaceView here if you do not want the context to
+         * create its own view. Note, that your view may or may not be used. You cannot rely on
+         * your dummy view to be used by the Camera. If you pass null, no dummy view will be used.
+         * In this case your application may not run correctly on older devices if you use the
+         * camera. This flag has no effect if you do not require the camera.
+         */
+        public SurfaceView dummySurface = null;
+
+        /** Force MFF to not use OpenGL in its processing. */
+        public boolean forceNoGL = false;
+    }
+
+    static private class State {
+        public static final int STATE_RUNNING = 1;
+        public static final int STATE_PAUSED = 2;
+        public static final int STATE_DESTROYED = 3;
+
+        public int current = STATE_RUNNING;
+    }
+
+    /** The application context. */
+    private Context mApplicationContext = null;
+
+    /** The set of filter graphs within this context */
+    private Set<FilterGraph> mGraphs = new HashSet<FilterGraph>();
+
+    /** The set of graph runners within this context */
+    private Set<GraphRunner> mRunners = new HashSet<GraphRunner>();
+
+    /** True, if the context preserves frames when paused. */
+    private boolean mPreserveFramesOnPause = false;
+
+    /** The shared CameraStreamer that streams camera frames to CameraSource filters. */
+    private CameraStreamer mCameraStreamer = null;
+
+    /** The current context state. */
+    private State mState = new State();
+
+    /** A dummy SurfaceView that is required for Camera operation on older devices. */
+    private SurfaceView mDummySurfaceView = null;
+
+    /** Handler to execute code in the context's thread, such as issuing callbacks. */
+    private Handler mHandler = null;
+
+    /** Flag whether OpenGL ES 2 is supported in this context. */
+    private boolean mGLSupport;
+
+    /** Flag whether camera streaming is supported in this context. */
+    private boolean mCameraStreamingSupport;
+
+    /** RenderScript base master class. */
+    private RenderScript mRenderScript;
+
+    /**
+     * Creates a new MffContext with the default configuration.
+     *
+     * An MffContext must be attached to a Context object of an application. You may create
+     * multiple MffContexts, however data between them cannot be shared. The context must be
+     * created in a thread with a Looper (such as the main/UI thread).
+     *
+     * On older versions of Android, the MffContext may create a visible dummy view for the
+     * camera to render into. This is a 1x1 SurfaceView that is placed into the top-left corner.
+     *
+     * @param context The application context to attach the MffContext to.
+     */
+    public MffContext(Context context) {
+        init(context, new Config());
+    }
+
+    /**
+     * Creates a new MffContext with the specified configuration.
+     *
+     * An MffContext must be attached to a Context object of an application. You may create
+     * multiple MffContexts, however data between them cannot be shared. The context must be
+     * created in a thread with a Looper (such as the main/UI thread).
+     *
+     * On older versions of Android, the MffContext may create a visible dummy view for the
+     * camera to render into. This is a 1x1 SurfaceView that is placed into the top-left corner.
+     * You may alternatively specify your own SurfaceView in the configuration.
+     *
+     * @param context The application context to attach the MffContext to.
+     * @param config The configuration to use.
+     *
+     * @throws RuntimeException If no context for the requested configuration could be created.
+     */
+    public MffContext(Context context, Config config) {
+        init(context, config);
+    }
+
+    /**
+     * Put all processing in the context on hold.
+     * This is typically called from your application's <code>onPause()</code> method, and will
+     * stop all running graphs (closing their filters). If the context does not preserve frames on
+     * pause (see {@link #setPreserveFramesOnPause(boolean)}) all frames attached to this context
+     * are released.
+     */
+    public void onPause() {
+        synchronized (mState) {
+            if (mState.current == State.STATE_RUNNING) {
+                if (mCameraStreamer != null) {
+                    mCameraStreamer.halt();
+                }
+                stopRunners(true);
+                mState.current = State.STATE_PAUSED;
+            }
+        }
+    }
+
+    /**
+     * Resumes the processing in this context.
+     * This is typically called from the application's <code>onResume()</code> method, and will
+     * resume processing any of the previously stopped filter graphs.
+     */
+    public void onResume() {
+        synchronized (mState) {
+            if (mState.current == State.STATE_PAUSED) {
+                resumeRunners();
+                resumeCamera();
+                mState.current = State.STATE_RUNNING;
+            }
+        }
+    }
+
+    /**
+     * Release all resources associated with this context.
+     * This will also stop any running graphs.
+     */
+    public void release() {
+        synchronized (mState) {
+            if (mState.current != State.STATE_DESTROYED) {
+                if (mCameraStreamer != null) {
+                    mCameraStreamer.stop();
+                    mCameraStreamer.tearDown();
+                }
+                if (Build.VERSION.SDK_INT >= 11) {
+                    maybeDestroyRenderScript();
+                }
+                stopRunners(false);
+                waitUntilStopped();
+                tearDown();
+                mState.current = State.STATE_DESTROYED;
+            }
+        }
+    }
+
+    /**
+     * Set whether frames are preserved when the context is paused.
+     * When passing false, all Frames associated with this context are released. The default
+     * value is true.
+     *
+     * @param preserve true, to preserve frames when the context is paused.
+     *
+     * @see #getPreserveFramesOnPause()
+     */
+    public void setPreserveFramesOnPause(boolean preserve) {
+        mPreserveFramesOnPause = preserve;
+    }
+
+    /**
+     * Returns whether frames are preserved when the context is paused.
+     *
+     * @return true, if frames are preserved when the context is paused.
+     *
+     * @see #setPreserveFramesOnPause(boolean)
+     */
+    public boolean getPreserveFramesOnPause() {
+        return mPreserveFramesOnPause;
+    }
+
+    /**
+     * Returns the application context that the MffContext is attached to.
+     *
+     * @return The application context for this context.
+     */
+    public Context getApplicationContext() {
+        return mApplicationContext;
+    }
+
+    /**
+     * Returns the context's shared CameraStreamer.
+     * Use the CameraStreamer to control the Camera. Frames from the Camera are typically streamed
+     * to CameraSource filters.
+     *
+     * @return The context's CameraStreamer instance.
+     */
+    public CameraStreamer getCameraStreamer() {
+        if (mCameraStreamer == null) {
+            mCameraStreamer = new CameraStreamer(this);
+        }
+        return mCameraStreamer;
+    }
+
+    /**
+     * Set the default EGL config chooser.
+     *
+     * When an EGL context is required by the MFF, the channel sizes specified here are used. The
+     * default sizes are 8 bits per R,G,B,A channel and 0 bits for depth and stencil channels.
+     *
+     * @param redSize The size of the red channel in bits.
+     * @param greenSize The size of the green channel in bits.
+     * @param blueSize The size of the blue channel in bits.
+     * @param alphaSize The size of the alpha channel in bits.
+     * @param depthSize The size of the depth channel in bits.
+     * @param stencilSize The size of the stencil channel in bits.
+     */
+    public static void setEGLConfigChooser(int redSize,
+                                           int greenSize,
+                                           int blueSize,
+                                           int alphaSize,
+                                           int depthSize,
+                                           int stencilSize) {
+        RenderTarget.setEGLConfigChooser(redSize,
+                                         greenSize,
+                                         blueSize,
+                                         alphaSize,
+                                         depthSize,
+                                         stencilSize);
+    }
+
+    /**
+     * Returns true, if this context supports using OpenGL.
+     * @return true, if this context supports using OpenGL.
+     */
+    public final boolean isOpenGLSupported() {
+        return mGLSupport;
+    }
+
+    /**
+     * Returns true, if this context supports camera streaming.
+     * @return true, if this context supports camera streaming.
+     */
+    public final boolean isCameraStreamingSupported() {
+        return mCameraStreamingSupport;
+    }
+
+    @TargetApi(11)
+    public final RenderScript getRenderScript() {
+        if (mRenderScript == null) {
+            mRenderScript = RenderScript.create(mApplicationContext);
+        }
+        return mRenderScript;
+    }
+
+    final void assertOpenGLSupported() {
+        if (!isOpenGLSupported()) {
+            throw new RuntimeException("Attempting to use OpenGL ES 2 in a context that does not "
+                    + "support it!");
+        }
+    }
+
+    void addGraph(FilterGraph graph) {
+        synchronized (mGraphs) {
+            mGraphs.add(graph);
+        }
+    }
+
+    void addRunner(GraphRunner runner) {
+        synchronized (mRunners) {
+            mRunners.add(runner);
+        }
+    }
+
+    SurfaceView getDummySurfaceView() {
+        return mDummySurfaceView;
+    }
+
+    void postRunnable(Runnable runnable) {
+        mHandler.post(runnable);
+    }
+
+    private void init(Context context, Config config) {
+        determineGLSupport(context, config);
+        determineCameraSupport(config);
+        createHandler();
+        mApplicationContext = context.getApplicationContext();
+        fetchDummySurfaceView(context, config);
+    }
+
+    private void fetchDummySurfaceView(Context context, Config config) {
+        if (config.requireCamera && CameraStreamer.requireDummySurfaceView()) {
+            mDummySurfaceView = config.dummySurface != null
+                    ? config.dummySurface
+                    : createDummySurfaceView(context);
+        }
+    }
+
+    private void determineGLSupport(Context context, Config config) {
+        if (config.forceNoGL) {
+            mGLSupport = false;
+        } else {
+            mGLSupport = getPlatformSupportsGLES2(context);
+            if (config.requireOpenGL && !mGLSupport) {
+                throw new RuntimeException("Cannot create context that requires GL support on "
+                        + "this platform!");
+            }
+        }
+    }
+
+    private void determineCameraSupport(Config config) {
+        mCameraStreamingSupport = (CameraStreamer.getNumberOfCameras() > 0);
+        if (config.requireCamera && !mCameraStreamingSupport) {
+            throw new RuntimeException("Cannot create context that requires a camera on "
+                    + "this platform!");
+        }
+    }
+
+    private static boolean getPlatformSupportsGLES2(Context context) {
+        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        ConfigurationInfo configurationInfo = am.getDeviceConfigurationInfo();
+        return configurationInfo.reqGlEsVersion >= 0x20000;
+    }
+
+    private void createHandler() {
+        if (Looper.myLooper() == null) {
+            throw new RuntimeException("MffContext must be created in a thread with a Looper!");
+        }
+        mHandler = new Handler();
+    }
+
+    private void stopRunners(boolean haltOnly) {
+        synchronized (mRunners) {
+            // Halt all runners (does nothing if not running)
+            for (GraphRunner runner : mRunners) {
+                if (haltOnly) {
+                    runner.halt();
+                } else {
+                    runner.stop();
+                }
+            }
+            // Flush all graphs if requested (this is queued up after the call to halt)
+            if (!mPreserveFramesOnPause) {
+                for (GraphRunner runner : mRunners) {
+                    runner.flushFrames();
+                }
+            }
+        }
+    }
+
+    private void resumeRunners() {
+        synchronized (mRunners) {
+            for (GraphRunner runner : mRunners) {
+                runner.restart();
+            }
+        }
+    }
+
+    private void resumeCamera() {
+        // Restart only affects previously halted cameras that were running.
+        if (mCameraStreamer != null) {
+            mCameraStreamer.restart();
+        }
+    }
+
+    private void waitUntilStopped() {
+        for (GraphRunner runner : mRunners) {
+            runner.waitUntilStop();
+        }
+    }
+
+    private void tearDown() {
+        // Tear down graphs
+        for (FilterGraph graph : mGraphs) {
+            graph.tearDown();
+        }
+
+        // Tear down runners
+        for (GraphRunner runner : mRunners) {
+            runner.tearDown();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private SurfaceView createDummySurfaceView(Context context) {
+        // This is only called on Gingerbread devices, so deprecation warning is unnecessary.
+        SurfaceView dummySurfaceView = new SurfaceView(context);
+        dummySurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+        // If we have an activity for this context we'll add the SurfaceView to it (as a 1x1 view
+        // in the top-left corner). If not, we warn the user that they may need to add one manually.
+        Activity activity = findActivityForContext(context);
+        if (activity != null) {
+            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(1, 1);
+            activity.addContentView(dummySurfaceView, params);
+        } else {
+            Log.w("MffContext", "Could not find activity for dummy surface! Consider specifying "
+                    + "your own SurfaceView!");
+        }
+        return dummySurfaceView;
+    }
+
+    private Activity findActivityForContext(Context context) {
+        return (context instanceof Activity) ? (Activity) context : null;
+    }
+
+    @TargetApi(11)
+    private void maybeDestroyRenderScript() {
+        if (mRenderScript != null) {
+            mRenderScript.destroy();
+            mRenderScript = null;
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MotionSensor.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MotionSensor.java
new file mode 100644
index 0000000..95558f2
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MotionSensor.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+// Make values from a motion sensor (e.g., accelerometer) available as filter outputs.
+
+package androidx.media.filterpacks.sensors;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValues;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public final class MotionSensor extends Filter implements SensorEventListener {
+
+    private SensorManager mSensorManager = null;
+    private Sensor mSensor = null;
+
+    private float[] mValues = new float[3];
+
+    public MotionSensor(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addOutputPort("values", Signature.PORT_REQUIRED, FrameType.array(float.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onPrepare() {
+        mSensorManager = (SensorManager)getContext().getApplicationContext()
+                            .getSystemService(Context.SENSOR_SERVICE);
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
+        // TODO: currently, the type of sensor is hardcoded. Should be able to set the sensor
+        //  type as filter input!
+        mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI);
+    }
+
+    @Override
+    protected void onTearDown() {
+        mSensorManager.unregisterListener(this);
+    }
+
+    @Override
+    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // (Do we need to do something when sensor accuracy changes?)
+    }
+
+    @Override
+    public final void onSensorChanged(SensorEvent event) {
+        synchronized(mValues) {
+            mValues[0] = event.values[0];
+            mValues[1] = event.values[1];
+            mValues[2] = event.values[2];
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("values");
+        FrameValues outFrame = outPort.fetchAvailableFrame(null).asFrameValues();
+        synchronized(mValues) {
+            outFrame.setValues(mValues);
+        }
+        outFrame.setTimestamp(System.currentTimeMillis() * 1000000L);
+        outPort.pushFrame(outFrame);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NewChromaHistogramFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NewChromaHistogramFilter.java
new file mode 100644
index 0000000..f524be371
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NewChromaHistogramFilter.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+// Extract histogram from image.
+
+package androidx.media.filterpacks.histogram;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * ChromaHistogramFilter takes in an image in HSVA format and computes a 2-D histogram with a
+ * 2 dimensional chroma histogram based on hue (column) and saturation (row) at the top and
+ * a 1-D value histogram in the last row. The number of bin in the value histogram equals to
+ * the number of bins in hue.
+ */
+public final class NewChromaHistogramFilter extends Filter {
+
+    private int mHueBins = 6;
+    private int mSaturationBins = 3;
+    private int mValueBins;
+
+    private int mSaturationThreshold = 26; // 255 * 0.1
+    private int mValueThreshold = 51; // 255 * 0.2
+
+    public NewChromaHistogramFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU);
+        FrameType dataOut = FrameType.buffer2D(FrameType.ELEMENT_FLOAT32);
+
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("huebins", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("saturationbins", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("saturationthreshold", Signature.PORT_OPTIONAL,
+                    FrameType.single(int.class))
+            .addInputPort("valuethreshold", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addOutputPort("histogram", Signature.PORT_REQUIRED, dataOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("huebins")) {
+            port.bindToFieldNamed("mHueBins");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("saturationbins")) {
+            port.bindToFieldNamed("mSaturationBins");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("saturationthreshold")) {
+            port.bindToFieldNamed("mSaturationThreshold");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("valuethreshold")) {
+            port.bindToFieldNamed("mValueThreshold");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameBuffer2D imageFrame = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        OutputPort outPort = getConnectedOutputPort("histogram");
+
+        mValueBins = mHueBins;
+        int[] outDims = new int[] {mHueBins, mSaturationBins + 1};
+        FrameBuffer2D histogramFrame = outPort.fetchAvailableFrame(outDims).asFrameBuffer2D();
+
+        ByteBuffer imageBuffer  = imageFrame.lockBytes(Frame.MODE_READ);
+        ByteBuffer histogramBuffer = histogramFrame.lockBytes(Frame.MODE_READ);
+        histogramBuffer.order(ByteOrder.nativeOrder());
+        FloatBuffer floatHistogram = histogramBuffer.asFloatBuffer();
+
+        // Run native method
+        extractChromaHistogram(imageBuffer, floatHistogram, mHueBins, mSaturationBins, mValueBins,
+                mSaturationThreshold, mValueThreshold);
+
+        imageFrame.unlock();
+        histogramFrame.unlock();
+
+        outPort.pushFrame(histogramFrame);
+    }
+
+    private static native void extractChromaHistogram(ByteBuffer imageBuffer,
+            FloatBuffer histogramBuffer, int hueBins, int saturationBins, int valueBins,
+            int saturationThreshold, int valueThreshold);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NormFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NormFilter.java
new file mode 100644
index 0000000..e816110
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/NormFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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 androidx.media.filterpacks.numeric;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+/**
+ * Filter to calculate the 2-norm of the inputs. i.e. sqrt(x^2 + y^2)
+ * TODO: Add support for more norms in the future.
+ */
+public final class NormFilter extends Filter {
+   private static final String TAG = "NormFilter";
+   private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+   public NormFilter(MffContext context, String name) {
+       super(context, name);
+   }
+
+   @Override
+   public Signature getSignature() {
+       FrameType floatT = FrameType.single(float.class);
+       return new Signature()
+           .addInputPort("x", Signature.PORT_REQUIRED, floatT)
+           .addInputPort("y", Signature.PORT_REQUIRED, floatT)
+           .addOutputPort("norm", Signature.PORT_REQUIRED, floatT)
+           .disallowOtherPorts();
+   }
+
+   @Override
+   protected void onProcess() {
+     FrameValue xFrameValue = getConnectedInputPort("x").pullFrame().asFrameValue();
+     float xValue = ((Float)xFrameValue.getValue()).floatValue();
+     FrameValue yFrameValue = getConnectedInputPort("y").pullFrame().asFrameValue();
+     float yValue = ((Float)yFrameValue.getValue()).floatValue();
+
+     float norm = (float) Math.hypot(xValue, yValue);
+     if (mLogVerbose) Log.v(TAG, "Norm = " + norm);
+     OutputPort outPort = getConnectedOutputPort("norm");
+     FrameValue outFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+     outFrame.setValue(norm);
+     outPort.pushFrame(outFrame);
+   }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/OutputPort.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/OutputPort.java
new file mode 100644
index 0000000..06117c3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/OutputPort.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+/**
+ * Output ports are the data emitting ports of filters.
+ * <p>
+ * Filters push data frames onto output-ports, which in turn push them onto their connected input
+ * ports. Output ports must be connected to an input port before data can be pushed onto them.
+ * Input and output ports share their Frame slot, meaning that when a frame is waiting on an output
+ * port, it is also waiting on the connected input port.
+ * </p><p>
+ * Only one frame can be pushed onto an output port at a time. In other words, a Frame must first
+ * be consumed by the target filter before a new frame can be pushed on the output port. If the
+ * output port is set to wait until it becomes free (see {@link #setWaitsUntilAvailable(boolean)}),
+ * it is guaranteed to be available when {@code onProcess()} is called. This is the default setting.
+ * </p>
+ */
+public final class OutputPort {
+
+    private Filter mFilter;
+    private String mName;
+    private Signature.PortInfo mInfo;
+    private FrameQueue.Builder mQueueBuilder = null;
+    private FrameQueue mQueue = null;
+    private boolean mWaitsUntilAvailable = true;
+    private InputPort mTarget = null;
+
+    /**
+     * Returns true, if this port is connected to a target port.
+     * @return true, if this port is connected to a target port.
+     */
+    public boolean isConnected() {
+        return mTarget != null;
+    }
+
+    /**
+     * Returns true, if there is no frame waiting on this port.
+     * @return true, if no Frame instance is waiting on this port.
+     */
+    public boolean isAvailable() {
+        return mQueue == null || mQueue.canPush();
+    }
+
+    /**
+     * Returns a frame for writing.
+     *
+     * Call this method to fetch a new frame to write into. When you have finished writing the
+     * frame data, you can push it into the output queue using {@link #pushFrame(Frame)}. Note,
+     * that the Frame returned is owned by the queue. If you wish to hold on to the frame, you
+     * must detach it.
+     *
+     * @param dimensions the size of the Frame you wish to obtain.
+     * @return a writable Frame instance.
+     */
+    public Frame fetchAvailableFrame(int[] dimensions) {
+        Frame frame = getQueue().fetchAvailableFrame(dimensions);
+        if (frame != null) {
+            //Log.i("OutputPort", "Adding frame " + frame + " to auto-release pool");
+            mFilter.addAutoReleaseFrame(frame);
+        }
+        return frame;
+    }
+
+    /**
+     * Pushes a frame onto this output port.
+     *
+     * This is typically a Frame instance you obtained by previously calling
+     * {@link #fetchAvailableFrame(int[])}, but may come from other sources such as an input port
+     * that is attached to this output port.
+     *
+     * Once you have pushed a frame to an output, you may no longer modify it as it may be shared
+     * among other filters.
+     *
+     * @param frame the frame to push to the output queue.
+     */
+    public void pushFrame(Frame frame) {
+        // Some queues allow pushing without fetching, so we need to make sure queue is open
+        // before pushing!
+        long timestamp = frame.getTimestamp();
+        if (timestamp == Frame.TIMESTAMP_NOT_SET)
+            frame.setTimestamp(mFilter.getCurrentTimestamp());
+        getQueue().pushFrame(frame);
+    }
+
+    /**
+     * Sets whether to wait until this port becomes available before processing.
+     * When set to true, the Filter will not be scheduled for processing unless there is no Frame
+     * waiting on this port. The default value is true.
+     *
+     * @param wait true, if filter should wait for the port to become available before processing.
+     * @see #waitsUntilAvailable()
+     */
+    public void setWaitsUntilAvailable(boolean wait) {
+        mWaitsUntilAvailable = wait;
+    }
+
+    /**
+     * Returns whether the filter waits until this port is available before processing.
+     * @return true, if the filter waits until this port is available before processing.
+     * @see #setWaitsUntilAvailable(boolean)
+     */
+    public boolean waitsUntilAvailable() {
+        return mWaitsUntilAvailable;
+    }
+
+    /**
+     * Returns the output port's name.
+     * This is the name that was specified when the output port was connected.
+     *
+     * @return the output port's name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Return the filter object that this port belongs to.
+     *
+     * @return the output port's filter.
+     */
+    public Filter getFilter() {
+        return mFilter;
+    }
+
+    @Override
+    public String toString() {
+        return mFilter.getName() + ":" + mName;
+    }
+
+    OutputPort(Filter filter, String name, Signature.PortInfo info) {
+        mFilter = filter;
+        mName = name;
+        mInfo = info;
+    }
+
+    void setTarget(InputPort target) {
+        mTarget = target;
+    }
+
+    /**
+     * Return the (input) port that this output port is connected to.
+     *
+     * @return the connected port, null if not connected.
+     */
+    public InputPort getTarget() {
+        return mTarget;
+    }
+
+    FrameQueue getQueue() {
+        return mQueue;
+    }
+
+    void setQueue(FrameQueue queue) {
+        mQueue = queue;
+        mQueueBuilder = null;
+    }
+
+    void onOpen(FrameQueue.Builder builder) {
+        mQueueBuilder = builder;
+        mQueueBuilder.setWriteType(mInfo.type);
+        mFilter.onOutputPortOpen(this);
+    }
+
+    boolean isOpen() {
+        return mQueue != null;
+    }
+
+    final boolean conditionsMet() {
+        return !mWaitsUntilAvailable || isAvailable();
+    }
+
+    void clear() {
+        if (mQueue != null) {
+            mQueue.clear();
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/PixelUtils.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/PixelUtils.java
new file mode 100644
index 0000000..88538d4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/PixelUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A collection of utilities to deal with pixel operations on ByteBuffers.
+ */
+public class PixelUtils {
+
+    /**
+     * Copy pixels from one buffer to another, applying a transformation.
+     *
+     * <p>The transformation is specified by specifying the initial offset in the output buffer, the
+     * stride (in pixels) between each pixel, and the stride (in pixels) between each row. The row
+     * stride is measured as the number of pixels between the start of each row.</p>
+     *
+     * <p>Note that this method is native for efficiency reasons. It does NOT do any bounds checking
+     * other than making sure the buffers are of sufficient size. This means that you can corrupt
+     * memory if specifying incorrect stride values!</p>
+     *
+     * @param input The input buffer containing pixel data.
+     * @param output The output buffer to hold the transformed pixel data.
+     * @param width The width of the input image.
+     * @param height The height of the input image.
+     * @param offset The start offset in the output (in pixels)
+     * @param pixStride The stride between each pixel (in pixels)
+     * @param rowStride The stride between the start of each row (in pixels)
+     */
+    public static void copyPixels(ByteBuffer input,
+            ByteBuffer output,
+            int width,
+            int height,
+            int offset,
+            int pixStride,
+            int rowStride) {
+        if (input.remaining() != output.remaining()) {
+            throw new IllegalArgumentException("Input and output buffers must have the same size!");
+        } else if (input.remaining() % 4 != 0) {
+            throw new IllegalArgumentException("Input buffer size must be a multiple of 4!");
+        } else if (output.remaining() % 4 != 0) {
+            throw new IllegalArgumentException("Output buffer size must be a multiple of 4!");
+        } else if ((width * height * 4) != input.remaining()) {
+            throw new IllegalArgumentException(
+                    "Input buffer size does not match given dimensions!");
+        } else if ((width * height * 4) != output.remaining()) {
+            throw new IllegalArgumentException(
+                    "Output buffer size does not match given dimensions!");
+        }
+        nativeCopyPixels(input, output, width, height, offset, pixStride, rowStride);
+    }
+
+    private static native void nativeCopyPixels(ByteBuffer input,
+            ByteBuffer output,
+            int width,
+            int height,
+            int offset,
+            int pixStride,
+            int rowStride);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RenderTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RenderTarget.java
new file mode 100644
index 0000000..ab0546d
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RenderTarget.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.media.MediaRecorder;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.os.Build.VERSION;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+public final class RenderTarget {
+
+    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+    private static final int EGL_OPENGL_ES2_BIT = 4;
+
+    // Pre-HC devices do not necessarily support multiple display surfaces.
+    private static boolean mSupportsMultipleDisplaySurfaces = (VERSION.SDK_INT >= 11);
+
+    /** A Map that tracks which objects are wrapped by EGLSurfaces */
+    private static HashMap<Object, EGLSurface> mSurfaceSources = new HashMap<Object, EGLSurface>();
+
+    /** A Map for performing reference counting over shared objects across RenderTargets */
+    private static HashMap<Object, Integer> mRefCounts = new HashMap<Object, Integer>();
+
+    /** Stores the RenderTarget that is focused on the current thread. */
+    private static ThreadLocal<RenderTarget> mCurrentTarget = new ThreadLocal<RenderTarget>();
+
+    /** The source for the surface used in this target (if any) */
+    private Object mSurfaceSource = null;
+
+    /** The cached EGLConfig instance. */
+    private static EGLConfig mEglConfig = null;
+
+    /** The display for which the EGLConfig was chosen. We expect only one. */
+    private static EGLDisplay mConfiguredDisplay;
+
+    private EGL10 mEgl;
+    private EGLDisplay mDisplay;
+    private EGLContext mContext;
+    private EGLSurface mSurface;
+    private int mFbo;
+
+    private boolean mOwnsContext;
+    private boolean mOwnsSurface;
+
+    private static HashMap<EGLContext, ImageShader> mIdShaders
+        = new HashMap<EGLContext, ImageShader>();
+
+    private static HashMap<EGLContext, EGLSurface> mDisplaySurfaces
+        = new HashMap<EGLContext, EGLSurface>();
+
+    private static int sRedSize = 8;
+    private static int sGreenSize = 8;
+    private static int sBlueSize = 8;
+    private static int sAlphaSize = 8;
+    private static int sDepthSize = 0;
+    private static int sStencilSize = 0;
+
+    public static RenderTarget newTarget(int width, int height) {
+        EGL10 egl = (EGL10) EGLContext.getEGL();
+        EGLDisplay eglDisplay = createDefaultDisplay(egl);
+        EGLConfig eglConfig = chooseEglConfig(egl, eglDisplay);
+        EGLContext eglContext = createContext(egl, eglDisplay, eglConfig);
+        EGLSurface eglSurface = createSurface(egl, eglDisplay, width, height);
+        RenderTarget result = new RenderTarget(eglDisplay, eglContext, eglSurface, 0, true, true);
+        result.addReferenceTo(eglSurface);
+        return result;
+    }
+
+    public static RenderTarget currentTarget() {
+        // As RenderTargets are immutable, we can safely return the last focused instance on this
+        // thread, as we know it cannot have changed, and therefore must be current.
+        return mCurrentTarget.get();
+    }
+
+    public RenderTarget forTexture(TextureSource texture, int width, int height) {
+        // NOTE: We do not need to lookup any previous bindings of this texture to an FBO, as
+        // multiple FBOs to a single texture is valid.
+        int fbo = GLToolbox.generateFbo();
+        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo);
+        GLToolbox.checkGlError("glBindFramebuffer");
+        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
+                                      GLES20.GL_COLOR_ATTACHMENT0,
+                                      texture.getTarget(),
+                                      texture.getTextureId(),
+                                      0);
+        GLToolbox.checkGlError("glFramebufferTexture2D");
+        return new RenderTarget(mDisplay, mContext, surface(), fbo, false, false);
+    }
+
+    public RenderTarget forSurfaceHolder(SurfaceHolder surfaceHolder) {
+        EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay);
+        EGLSurface eglSurf = null;
+        synchronized (mSurfaceSources) {
+            eglSurf = mSurfaceSources.get(surfaceHolder);
+            if (eglSurf == null) {
+                eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surfaceHolder, null);
+                mSurfaceSources.put(surfaceHolder, eglSurf);
+            }
+        }
+        checkEglError(mEgl, "eglCreateWindowSurface");
+        checkSurface(mEgl, eglSurf);
+        RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true);
+        result.addReferenceTo(eglSurf);
+        result.setSurfaceSource(surfaceHolder);
+        return result;
+    }
+
+    @TargetApi(11)
+    public RenderTarget forSurfaceTexture(SurfaceTexture surfaceTexture) {
+        EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay);
+        EGLSurface eglSurf = null;
+        synchronized (mSurfaceSources) {
+            eglSurf = mSurfaceSources.get(surfaceTexture);
+            if (eglSurf == null) {
+                eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surfaceTexture, null);
+                mSurfaceSources.put(surfaceTexture, eglSurf);
+            }
+        }
+        checkEglError(mEgl, "eglCreateWindowSurface");
+        checkSurface(mEgl, eglSurf);
+        RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true);
+        result.setSurfaceSource(surfaceTexture);
+        result.addReferenceTo(eglSurf);
+        return result;
+    }
+
+    @TargetApi(11)
+    public RenderTarget forSurface(Surface surface) {
+        EGLConfig eglConfig = chooseEglConfig(mEgl, mDisplay);
+        EGLSurface eglSurf = null;
+        synchronized (mSurfaceSources) {
+            eglSurf = mSurfaceSources.get(surface);
+            if (eglSurf == null) {
+                eglSurf = mEgl.eglCreateWindowSurface(mDisplay, eglConfig, surface, null);
+                mSurfaceSources.put(surface, eglSurf);
+            }
+        }
+        checkEglError(mEgl, "eglCreateWindowSurface");
+        checkSurface(mEgl, eglSurf);
+        RenderTarget result = new RenderTarget(mDisplay, mContext, eglSurf, 0, false, true);
+        result.setSurfaceSource(surface);
+        result.addReferenceTo(eglSurf);
+        return result;
+    }
+
+    public static RenderTarget forMediaRecorder(MediaRecorder mediaRecorder) {
+        throw new RuntimeException("Not yet implemented MediaRecorder -> RenderTarget!");
+    }
+
+    public static void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize,
+            int depthSize, int stencilSize) {
+        sRedSize = redSize;
+        sGreenSize = greenSize;
+        sBlueSize = blueSize;
+        sAlphaSize = alphaSize;
+        sDepthSize = depthSize;
+        sStencilSize = stencilSize;
+    }
+
+    public void registerAsDisplaySurface() {
+        if (!mSupportsMultipleDisplaySurfaces) {
+            // Note that while this does in effect change RenderTarget instances (by modifying
+            // their returned EGLSurface), breaking the immutability requirement, it does not modify
+            // the current target. This is important so that the instance returned in
+            // currentTarget() remains accurate.
+            EGLSurface currentSurface = mDisplaySurfaces.get(mContext);
+            if (currentSurface != null && !currentSurface.equals(mSurface)) {
+                throw new RuntimeException("This device supports only a single display surface!");
+            } else {
+                mDisplaySurfaces.put(mContext, mSurface);
+            }
+        }
+    }
+
+    public void unregisterAsDisplaySurface() {
+        if (!mSupportsMultipleDisplaySurfaces) {
+            mDisplaySurfaces.put(mContext, null);
+        }
+    }
+
+    public void focus() {
+        RenderTarget current = mCurrentTarget.get();
+        // We assume RenderTargets are immutable, so that we do not need to focus if the current
+        // RenderTarget has not changed.
+        if (current != this) {
+            mEgl.eglMakeCurrent(mDisplay, surface(), surface(), mContext);
+            mCurrentTarget.set(this);
+        }
+        if (getCurrentFbo() != mFbo) {
+            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFbo);
+            GLToolbox.checkGlError("glBindFramebuffer");
+        }
+    }
+
+    public static void focusNone() {
+        EGL10 egl = (EGL10) EGLContext.getEGL();
+        egl.eglMakeCurrent(egl.eglGetCurrentDisplay(),
+                           EGL10.EGL_NO_SURFACE,
+                           EGL10.EGL_NO_SURFACE,
+                           EGL10.EGL_NO_CONTEXT);
+        mCurrentTarget.set(null);
+        checkEglError(egl, "eglMakeCurrent");
+    }
+
+    public void swapBuffers() {
+        mEgl.eglSwapBuffers(mDisplay, surface());
+    }
+
+    public EGLContext getContext() {
+        return mContext;
+    }
+
+    public static EGLContext currentContext() {
+        RenderTarget current = RenderTarget.currentTarget();
+        return current != null ? current.getContext() : EGL10.EGL_NO_CONTEXT;
+    }
+
+    public void release() {
+        if (mOwnsContext) {
+            mEgl.eglDestroyContext(mDisplay, mContext);
+            mContext = EGL10.EGL_NO_CONTEXT;
+        }
+        if (mOwnsSurface) {
+            synchronized (mSurfaceSources) {
+                if (removeReferenceTo(mSurface)) {
+                    mEgl.eglDestroySurface(mDisplay, mSurface);
+                    mSurface = EGL10.EGL_NO_SURFACE;
+                    mSurfaceSources.remove(mSurfaceSource);
+                }
+            }
+        }
+        if (mFbo != 0) {
+           GLToolbox.deleteFbo(mFbo);
+       }
+    }
+
+    public void readPixelData(ByteBuffer pixels, int width, int height) {
+        GLToolbox.readTarget(this, pixels, width, height);
+    }
+
+    public ByteBuffer getPixelData(int width, int height) {
+        ByteBuffer pixels = ByteBuffer.allocateDirect(width * height * 4);
+        GLToolbox.readTarget(this, pixels, width, height);
+        return pixels;
+    }
+
+    /**
+     * Returns an identity shader for this context.
+     * You must not modify this shader. Use {@link ImageShader#createIdentity()} if you need to
+     * modify an identity shader.
+     */
+    public ImageShader getIdentityShader() {
+        ImageShader idShader = mIdShaders.get(mContext);
+        if (idShader == null) {
+            idShader = ImageShader.createIdentity();
+            mIdShaders.put(mContext, idShader);
+        }
+        return idShader;
+    }
+
+    @Override
+    public String toString() {
+        return "RenderTarget(" + mDisplay + ", " + mContext + ", " + mSurface + ", " + mFbo + ")";
+    }
+
+    private void setSurfaceSource(Object source) {
+        mSurfaceSource = source;
+    }
+
+    private void addReferenceTo(Object object) {
+        Integer refCount = mRefCounts.get(object);
+        if (refCount != null) {
+            mRefCounts.put(object, refCount + 1);
+        } else {
+            mRefCounts.put(object, 1);
+        }
+    }
+
+    private boolean removeReferenceTo(Object object) {
+        Integer refCount = mRefCounts.get(object);
+        if (refCount != null && refCount > 0) {
+            --refCount;
+            mRefCounts.put(object, refCount);
+            return refCount == 0;
+        } else {
+            Log.e("RenderTarget", "Removing reference of already released: " + object + "!");
+            return false;
+        }
+    }
+
+    private static EGLConfig chooseEglConfig(EGL10 egl, EGLDisplay display) {
+        if (mEglConfig == null || !display.equals(mConfiguredDisplay)) {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getDesiredConfig();
+            if (!egl.eglChooseConfig(display, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("EGL Error: eglChooseConfig failed " +
+                        getEGLErrorString(egl, egl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                mEglConfig = configs[0];
+                mConfiguredDisplay = display;
+            }
+        }
+        return mEglConfig;
+    }
+
+    private static int[] getDesiredConfig() {
+        return new int[] {
+                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                EGL10.EGL_RED_SIZE, sRedSize,
+                EGL10.EGL_GREEN_SIZE, sGreenSize,
+                EGL10.EGL_BLUE_SIZE, sBlueSize,
+                EGL10.EGL_ALPHA_SIZE, sAlphaSize,
+                EGL10.EGL_DEPTH_SIZE, sDepthSize,
+                EGL10.EGL_STENCIL_SIZE, sStencilSize,
+                EGL10.EGL_NONE
+        };
+    }
+
+    private RenderTarget(EGLDisplay display, EGLContext context, EGLSurface surface, int fbo,
+                         boolean ownsContext, boolean ownsSurface) {
+        mEgl = (EGL10) EGLContext.getEGL();
+        mDisplay = display;
+        mContext = context;
+        mSurface = surface;
+        mFbo = fbo;
+        mOwnsContext = ownsContext;
+        mOwnsSurface = ownsSurface;
+    }
+
+    private EGLSurface surface() {
+        if (mSupportsMultipleDisplaySurfaces) {
+            return mSurface;
+        } else {
+            EGLSurface displaySurface = mDisplaySurfaces.get(mContext);
+            return displaySurface != null ? displaySurface : mSurface;
+        }
+    }
+
+    private static void initEgl(EGL10 egl, EGLDisplay display) {
+        int[] version = new int[2];
+        if (!egl.eglInitialize(display, version)) {
+            throw new RuntimeException("EGL Error: eglInitialize failed " +
+                    getEGLErrorString(egl, egl.eglGetError()));
+        }
+    }
+
+    private static EGLDisplay createDefaultDisplay(EGL10 egl) {
+        EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        checkDisplay(egl, display);
+        initEgl(egl, display);
+        return display;
+    }
+
+    private static EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+        int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+        EGLContext ctxt = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list);
+        checkContext(egl, ctxt);
+        return ctxt;
+    }
+
+    private static EGLSurface createSurface(EGL10 egl, EGLDisplay display, int width, int height) {
+        EGLConfig eglConfig = chooseEglConfig(egl, display);
+        int[] attribs = { EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE };
+        return egl.eglCreatePbufferSurface(display, eglConfig, attribs);
+    }
+
+    private static int getCurrentFbo() {
+        int[] result = new int[1];
+        GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0);
+        return result[0];
+    }
+
+    private static void checkDisplay(EGL10 egl, EGLDisplay display) {
+        if (display == EGL10.EGL_NO_DISPLAY) {
+            throw new RuntimeException("EGL Error: Bad display: "
+                    + getEGLErrorString(egl, egl.eglGetError()));
+        }
+    }
+
+    private static void checkContext(EGL10 egl, EGLContext context) {
+        if (context == EGL10.EGL_NO_CONTEXT) {
+            throw new RuntimeException("EGL Error: Bad context: "
+                    + getEGLErrorString(egl, egl.eglGetError()));
+        }
+    }
+
+    private static void checkSurface(EGL10 egl, EGLSurface surface) {
+        if (surface == EGL10.EGL_NO_SURFACE) {
+            throw new RuntimeException("EGL Error: Bad surface: "
+                    + getEGLErrorString(egl, egl.eglGetError()));
+        }
+    }
+
+    private static void checkEglError(EGL10 egl, String command) {
+        int error = egl.eglGetError();
+        if (error != EGL10.EGL_SUCCESS) {
+            throw new RuntimeException("Error executing " + command + "! EGL error = 0x"
+                + Integer.toHexString(error));
+        }
+    }
+
+    private static String getEGLErrorString(EGL10 egl, int eglError) {
+        if (VERSION.SDK_INT >= 14) {
+            return getEGLErrorStringICS(egl, eglError);
+        } else {
+            return "EGL Error 0x" + Integer.toHexString(eglError);
+        }
+    }
+
+    @TargetApi(14)
+    private static String getEGLErrorStringICS(EGL10 egl, int eglError) {
+        return GLUtils.getEGLErrorString(egl.eglGetError());
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ResizeFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ResizeFilter.java
new file mode 100644
index 0000000..c334c91
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ResizeFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.transform;
+
+import androidx.media.filterfw.*;
+
+// TODO: In the future this could be done with a meta-filter that simply "hard-codes" the crop
+// parameters.
+public class ResizeFilter extends CropFilter {
+
+    public ResizeFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("outputWidth", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("outputHeight", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class))
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RotateFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RotateFilter.java
new file mode 100644
index 0000000..5db20a4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/RotateFilter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.transform;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.geometry.Quad;
+
+public class RotateFilter extends Filter {
+
+    private Quad mSourceRect = Quad.fromRect(0f, 0f, 1f, 1f);
+    private float mRotateAngle = 0;
+    private ImageShader mShader;
+
+    public RotateFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("rotateAngle", Signature.PORT_REQUIRED, FrameType.single(float.class))
+            .addInputPort("sourceRect", Signature.PORT_OPTIONAL, FrameType.single(Quad.class))
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("rotateAngle")) {
+            port.bindToFieldNamed("mRotateAngle");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("sourceRect")) {
+            port.bindToFieldNamed("mSourceRect");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onPrepare() {
+        mShader = ImageShader.createIdentity();
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("image");
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        int[] inDims = inputImage.getDimensions();
+
+        FrameImage2D outputImage = outPort.fetchAvailableFrame(inDims).asFrameImage2D();
+        mShader.setSourceQuad(mSourceRect);
+        Quad targetQuad = mSourceRect.rotated((float) (mRotateAngle / 180 * Math.PI));
+        mShader.setTargetQuad(targetQuad);
+        mShader.process(inputImage, outputImage);
+        outPort.pushFrame(outputImage);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ScaleFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ScaleFilter.java
new file mode 100644
index 0000000..1c3f328
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ScaleFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.transform;
+
+// TODO: scale filter needs to be able to specify output width and height
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.Signature;
+
+public class ScaleFilter extends ResizeFilter {
+
+    private float mScale = 1.0f;
+
+    public ScaleFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+            .addInputPort("scale", Signature.PORT_OPTIONAL, FrameType.single(float.class))
+            .addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class))
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("scale")) {
+            port.bindToFieldNamed("mScale");
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("useMipmaps")) {
+            port.bindToFieldNamed("mUseMipmaps");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected int getOutputWidth(int inWidth, int inHeight) {
+        return (int)(inWidth * mScale);
+    }
+
+    @Override
+    protected int getOutputHeight(int inWidth, int inHeight) {
+        return (int)(inHeight * mScale);
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Signature.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Signature.java
new file mode 100644
index 0000000..2c2916f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Signature.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A Signature holds the specification for a filter's input and output ports.
+ *
+ * A Signature instance must be returned by the filter's {@link Filter#getSignature()} method. It
+ * specifies the number and names of the filter's input and output ports, whether or not they
+ * are required, how data for those ports are accessed, and more. A Signature does not change over
+ * time. This makes Signatures useful for understanding how a filter can be integrated into a
+ * graph.
+ *
+ * There are a number of flags that can be specified for each input and output port. The flag
+ * {@code PORT_REQUIRED} indicates that the user must connect the specified port. On the other hand,
+ * {@code PORT_OPTIONAL} indicates that a port may be connected by the user.
+ *
+ * If ports other than the ones in the Signature are allowed, they default to the most generic
+ * format, that allows passing in any type of Frame. Thus, if more granular access is needed to
+ * a frame's data, it must be specified in the Signature.
+ */
+public class Signature {
+
+    private HashMap<String, PortInfo> mInputPorts = null;
+    private HashMap<String, PortInfo> mOutputPorts = null;
+    private boolean mAllowOtherInputs = true;
+    private boolean mAllowOtherOutputs = true;
+
+    static class PortInfo {
+        public int flags;
+        public FrameType type;
+
+        public PortInfo() {
+            flags = 0;
+            type = FrameType.any();
+        }
+
+        public PortInfo(int flags, FrameType type) {
+            this.flags = flags;
+            this.type = type;
+        }
+
+        public boolean isRequired() {
+            return (flags & PORT_REQUIRED) != 0;
+        }
+
+        public String toString(String ioMode, String name) {
+            String ioName = ioMode + " " + name;
+            String modeName = isRequired() ? "required" : "optional";
+            return modeName + " " + ioName + ": " + type.toString();
+        }
+    }
+
+    /** Indicates that the port must be connected in the graph. */
+    public static final int PORT_REQUIRED = 0x02;
+    /** Indicates that the port may be connected in the graph . */
+    public static final int PORT_OPTIONAL = 0x01;
+
+    /**
+     * Creates a new empty Signature.
+     */
+    public Signature() {
+    }
+
+    /**
+     * Adds an input port to the Signature.
+     *
+     * @param name the name of the input port. Must be unique among input port names.
+     * @param flags a combination of port flags.
+     * @param type the type of the input frame.
+     * @return this Signature instance.
+     */
+    public Signature addInputPort(String name, int flags, FrameType type) {
+        addInputPort(name, new PortInfo(flags, type));
+        return this;
+    }
+
+    /**
+     * Adds an output port to the Signature.
+     *
+     * @param name the name of the output port. Must be unique among output port names.
+     * @param flags a combination of port flags.
+     * @param type the type of the output frame.
+     * @return this Signature instance.
+     */
+    public Signature addOutputPort(String name, int flags, FrameType type) {
+        addOutputPort(name, new PortInfo(flags, type));
+        return this;
+    }
+
+    /**
+     * Disallows the user from adding any other input ports.
+     * Adding any input port not explicitly specified in this Signature will cause an error.
+     * @return this Signature instance.
+     */
+    public Signature disallowOtherInputs() {
+        mAllowOtherInputs = false;
+        return this;
+    }
+
+    /**
+     * Disallows the user from adding any other output ports.
+     * Adding any output port not explicitly specified in this Signature will cause an error.
+     * @return this Signature instance.
+     */
+    public Signature disallowOtherOutputs() {
+        mAllowOtherOutputs = false;
+        return this;
+    }
+
+    /**
+     * Disallows the user from adding any other ports.
+     * Adding any input or output port not explicitly specified in this Signature will cause an
+     * error.
+     * @return this Signature instance.
+     */
+    public Signature disallowOtherPorts() {
+        mAllowOtherInputs = false;
+        mAllowOtherOutputs = false;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer stringBuffer = new StringBuffer();
+        for (Entry<String, PortInfo> entry : mInputPorts.entrySet()) {
+            stringBuffer.append(entry.getValue().toString("input", entry.getKey()) + "\n");
+        }
+        for (Entry<String, PortInfo> entry : mOutputPorts.entrySet()) {
+            stringBuffer.append(entry.getValue().toString("output", entry.getKey()) + "\n");
+        }
+        if (!mAllowOtherInputs) {
+            stringBuffer.append("disallow other inputs\n");
+        }
+        if (!mAllowOtherOutputs) {
+            stringBuffer.append("disallow other outputs\n");
+        }
+        return stringBuffer.toString();
+    }
+
+    PortInfo getInputPortInfo(String name) {
+        PortInfo result = mInputPorts != null ? mInputPorts.get(name) : null;
+        return result != null ? result : new PortInfo();
+    }
+
+    PortInfo getOutputPortInfo(String name) {
+        PortInfo result = mOutputPorts != null ? mOutputPorts.get(name) : null;
+        return result != null ? result : new PortInfo();
+    }
+
+    void checkInputPortsConform(Filter filter) {
+        Set<String> filterInputs = new HashSet<String>();
+        filterInputs.addAll(filter.getConnectedInputPortMap().keySet());
+        if (mInputPorts != null) {
+            for (Entry<String, PortInfo> entry : mInputPorts.entrySet()) {
+                String portName = entry.getKey();
+                PortInfo portInfo = entry.getValue();
+                InputPort inputPort = filter.getConnectedInputPort(portName);
+                if (inputPort == null && portInfo.isRequired()) {
+                    throw new RuntimeException("Filter " + filter + " does not have required "
+                        + "input port '" + portName + "'!");
+                }
+                filterInputs.remove(portName);
+            }
+        }
+        if (!mAllowOtherInputs && !filterInputs.isEmpty()) {
+            throw new RuntimeException("Filter " + filter + " has invalid input ports: "
+                + filterInputs + "!");
+        }
+    }
+
+    void checkOutputPortsConform(Filter filter) {
+        Set<String> filterOutputs = new HashSet<String>();
+        filterOutputs.addAll(filter.getConnectedOutputPortMap().keySet());
+        if (mOutputPorts != null) {
+            for (Entry<String, PortInfo> entry : mOutputPorts.entrySet()) {
+                String portName = entry.getKey();
+                PortInfo portInfo = entry.getValue();
+                OutputPort outputPort = filter.getConnectedOutputPort(portName);
+                if (outputPort == null && portInfo.isRequired()) {
+                    throw new RuntimeException("Filter " + filter + " does not have required "
+                        + "output port '" + portName + "'!");
+                }
+                filterOutputs.remove(portName);
+            }
+        }
+        if (!mAllowOtherOutputs && !filterOutputs.isEmpty()) {
+            throw new RuntimeException("Filter " + filter + " has invalid output ports: "
+                + filterOutputs + "!");
+        }
+    }
+
+    HashMap<String, PortInfo> getInputPorts() {
+        return mInputPorts;
+    }
+
+    HashMap<String, PortInfo> getOutputPorts() {
+        return mOutputPorts;
+    }
+
+    private void addInputPort(String name, PortInfo portInfo) {
+        if (mInputPorts == null) {
+            mInputPorts = new HashMap<String, PortInfo>();
+        }
+        if (mInputPorts.containsKey(name)) {
+            throw new RuntimeException("Attempting to add duplicate input port '" + name + "'!");
+        }
+        mInputPorts.put(name, portInfo);
+    }
+
+    private void addOutputPort(String name, PortInfo portInfo) {
+        if (mOutputPorts == null) {
+            mOutputPorts = new HashMap<String, PortInfo>();
+        }
+        if (mOutputPorts.containsKey(name)) {
+            throw new RuntimeException("Attempting to add duplicate output port '" + name + "'!");
+        }
+        mOutputPorts.put(name, portInfo);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SimpleCache.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SimpleCache.java
new file mode 100644
index 0000000..f54621f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SimpleCache.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * This is a simple LRU cache that is used internally for managing repetitive objects.
+ */
+class SimpleCache<K, V> extends LinkedHashMap<K, V> {
+
+    private int mMaxEntries;
+
+    public SimpleCache(final int maxEntries) {
+        super(maxEntries + 1, 1f, true);
+        mMaxEntries = maxEntries;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
+        return super.size() > mMaxEntries;
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SlotFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SlotFilter.java
new file mode 100644
index 0000000..aaa87c2
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SlotFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+public abstract class SlotFilter extends Filter {
+
+    protected final String mSlotName;
+
+    protected SlotFilter(MffContext context, String name, String slotName) {
+        super(context, name);
+        mSlotName = slotName;
+    }
+
+    protected final FrameType getSlotType() {
+        return getFrameManager().getSlot(mSlotName).getType();
+    }
+
+    protected final boolean slotHasFrame() {
+        return getFrameManager().getSlot(mSlotName).hasFrame();
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SobelFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SobelFilter.java
new file mode 100644
index 0000000..a4c39a1
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SobelFilter.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2013 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 androidx.media.filterpacks.image;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+
+public class SobelFilter extends Filter {
+
+    private static final String mGradientXSource =
+              "precision mediump float;\n"
+            + "uniform sampler2D tex_sampler_0;\n"
+            + "uniform vec2 pix;\n"
+            + "varying vec2 v_texcoord;\n"
+            + "void main() {\n"
+            + "  vec4 a1 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, -pix.y));\n"
+            + "  vec4 a2 = -2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, 0.0));\n"
+            + "  vec4 a3 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, +pix.y));\n"
+            + "  vec4 b1 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, -pix.y));\n"
+            + "  vec4 b2 = +2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, 0.0));\n"
+            + "  vec4 b3 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, +pix.y));\n"
+            + "  gl_FragColor = 0.5 + (a1 + a2 + a3 + b1 + b2 + b3) / 8.0;\n"
+            + "}\n";
+
+    private static final String mGradientYSource =
+              "precision mediump float;\n"
+            + "uniform sampler2D tex_sampler_0;\n"
+            + "uniform vec2 pix;\n"
+            + "varying vec2 v_texcoord;\n"
+            + "void main() {\n"
+            + "  vec4 a1 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, -pix.y));\n"
+            + "  vec4 a2 = -2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(0.0,    -pix.y));\n"
+            + "  vec4 a3 = -1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, -pix.y));\n"
+            + "  vec4 b1 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(-pix.x, +pix.y));\n"
+            + "  vec4 b2 = +2.0 * texture2D(tex_sampler_0, v_texcoord + vec2(0.0,    +pix.y));\n"
+            + "  vec4 b3 = +1.0 * texture2D(tex_sampler_0, v_texcoord + vec2(+pix.x, +pix.y));\n"
+            + "  gl_FragColor = 0.5 + (a1 + a2 + a3 + b1 + b2 + b3) / 8.0;\n"
+            + "}\n";
+
+    private static final String mMagnitudeSource =
+            "precision mediump float;\n"
+          + "uniform sampler2D tex_sampler_0;\n"
+          + "uniform sampler2D tex_sampler_1;\n"
+          + "varying vec2 v_texcoord;\n"
+          + "void main() {\n"
+          + "  vec4 gx = 2.0 * texture2D(tex_sampler_0, v_texcoord) - 1.0;\n"
+          + "  vec4 gy = 2.0 * texture2D(tex_sampler_1, v_texcoord) - 1.0;\n"
+          + "  gl_FragColor = vec4(sqrt(gx.rgb * gx.rgb + gy.rgb * gy.rgb), 1.0);\n"
+          + "}\n";
+
+    private static final String mDirectionSource =
+            "precision mediump float;\n"
+          + "uniform sampler2D tex_sampler_0;\n"
+          + "uniform sampler2D tex_sampler_1;\n"
+          + "varying vec2 v_texcoord;\n"
+          + "void main() {\n"
+          + "  vec4 gy = 2.0 * texture2D(tex_sampler_1, v_texcoord) - 1.0;\n"
+          + "  vec4 gx = 2.0 * texture2D(tex_sampler_0, v_texcoord) - 1.0;\n"
+          + "  gl_FragColor = vec4((atan(gy.rgb, gx.rgb) + 3.14) / (2.0 * 3.14), 1.0);\n"
+          + "}\n";
+
+    private ImageShader mGradientXShader;
+    private ImageShader mGradientYShader;
+    private ImageShader mMagnitudeShader;
+    private ImageShader mDirectionShader;
+
+    private FrameType mImageType;
+
+    public SobelFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        // TODO: we will address the issue of READ_GPU / WRITE_GPU when using CPU filters later.
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+        return new Signature().addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+                .addOutputPort("direction", Signature.PORT_OPTIONAL, imageOut)
+                .addOutputPort("magnitude", Signature.PORT_OPTIONAL, imageOut).disallowOtherPorts();
+    }
+
+    @Override
+    protected void onPrepare() {
+        if (isOpenGLSupported()) {
+            mGradientXShader = new ImageShader(mGradientXSource);
+            mGradientYShader = new ImageShader(mGradientYSource);
+            mMagnitudeShader = new ImageShader(mMagnitudeSource);
+            mDirectionShader = new ImageShader(mDirectionSource);
+            mImageType = FrameType.image2D(
+                    FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU | FrameType.WRITE_GPU);
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort magnitudePort = getConnectedOutputPort("magnitude");
+        OutputPort directionPort = getConnectedOutputPort("direction");
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        int[] inputDims = inputImage.getDimensions();
+
+        FrameImage2D magImage = (magnitudePort != null) ?
+                magnitudePort.fetchAvailableFrame(inputDims).asFrameImage2D() : null;
+        FrameImage2D dirImage = (directionPort != null) ?
+                directionPort.fetchAvailableFrame(inputDims).asFrameImage2D() : null;
+        if (isOpenGLSupported()) {
+            FrameImage2D gxFrame = Frame.create(mImageType, inputDims).asFrameImage2D();
+            FrameImage2D gyFrame = Frame.create(mImageType, inputDims).asFrameImage2D();
+            mGradientXShader.setUniformValue("pix", new float[] {1f/inputDims[0], 1f/inputDims[1]});
+            mGradientYShader.setUniformValue("pix", new float[] {1f/inputDims[0], 1f/inputDims[1]});
+            mGradientXShader.process(inputImage, gxFrame);
+            mGradientYShader.process(inputImage, gyFrame);
+            FrameImage2D[] gradientFrames = new FrameImage2D[] { gxFrame, gyFrame };
+            if (magnitudePort != null) {
+                mMagnitudeShader.processMulti(gradientFrames, magImage);
+            }
+            if (directionPort != null) {
+                mDirectionShader.processMulti(gradientFrames, dirImage);
+            }
+            gxFrame.release();
+            gyFrame.release();
+        } else {
+            ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
+            ByteBuffer magBuffer  = (magImage != null) ?
+                    magImage.lockBytes(Frame.MODE_WRITE) : null;
+            ByteBuffer dirBuffer  = (dirImage != null) ?
+                    dirImage.lockBytes(Frame.MODE_WRITE) : null;
+            sobelOperator(inputImage.getWidth(), inputImage.getHeight(),
+                    inputBuffer, magBuffer, dirBuffer);
+            inputImage.unlock();
+            if (magImage != null) {
+                magImage.unlock();
+            }
+            if (dirImage != null) {
+                dirImage.unlock();
+            }
+        }
+        if (magImage != null) {
+            magnitudePort.pushFrame(magImage);
+        }
+        if (dirImage != null) {
+            directionPort.pushFrame(dirImage);
+        }
+    }
+
+    private static native boolean sobelOperator(int width, int height,
+            ByteBuffer imageBuffer, ByteBuffer magBuffer, ByteBuffer dirBudder);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/StatsFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/StatsFilter.java
new file mode 100644
index 0000000..94030c3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/StatsFilter.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+// Calculates the mean and standard deviation of the values in the input image.
+// It takes in an RGBA image, but assumes that r, g, b, a are all the same values.
+
+package androidx.media.filterpacks.numeric;
+
+import android.util.Log;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.geometry.Quad;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Get the sample mean and variance of a 2-D buffer of bytes over a given rectangle.
+ * TODO: Add more statistics as needed.
+ * TODO: Check if crop rectangle is necessary to be included in this filter.
+ */
+public class StatsFilter extends Filter {
+
+    private static final int MEAN_INDEX = 0;
+    private static final int STDEV_INDEX = 1;
+
+    private final float[] mStats = new float[2];
+
+    private Quad mCropRect = Quad.fromRect(0f, 0f, 1f, 1f);
+    private static final String TAG = "StatsFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * @param context
+     * @param name
+     */
+    public StatsFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType inputFrame = FrameType.buffer2D(FrameType.ELEMENT_INT8);
+        FrameType floatT = FrameType.single(float.class);
+        return new Signature()
+                .addInputPort("buffer", Signature.PORT_REQUIRED, inputFrame)
+                .addInputPort("cropRect", Signature.PORT_OPTIONAL, FrameType.single(Quad.class))
+                .addOutputPort("mean", Signature.PORT_REQUIRED, floatT)
+                .addOutputPort("stdev", Signature.PORT_REQUIRED, floatT)
+                .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("cropRect")) {
+            port.bindToFieldNamed("mCropRect");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    private void calcMeanAndStd(ByteBuffer pixelBuffer, int width, int height, Quad quad) {
+        // Native
+        pixelBuffer.rewind();
+        regionscore(pixelBuffer, width, height, quad.topLeft().x, quad.topLeft().y,
+                quad.bottomRight().x, quad.bottomRight().y, mStats);
+        if (mLogVerbose) {
+            Log.v(TAG, "Native calc stats: Mean = " + mStats[MEAN_INDEX] + ", Stdev = "
+                    + mStats[STDEV_INDEX]);
+        }
+    }
+
+    /**
+     * @see androidx.media.filterfw.Filter#onProcess()
+     */
+    @Override
+    protected void onProcess() {
+        FrameBuffer2D inputFrame = getConnectedInputPort("buffer").pullFrame().asFrameImage2D();
+        ByteBuffer pixelBuffer = inputFrame.lockBytes(Frame.MODE_READ);
+
+        calcMeanAndStd(pixelBuffer, inputFrame.getWidth(), inputFrame.getHeight(), mCropRect);
+        inputFrame.unlock();
+
+        OutputPort outPort = getConnectedOutputPort("mean");
+        FrameValue outFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        outFrame.setValue(mStats[MEAN_INDEX]);
+        outPort.pushFrame(outFrame);
+
+        OutputPort outPortStdev = getConnectedOutputPort("stdev");
+        FrameValue outFrameStdev = outPortStdev.fetchAvailableFrame(null).asFrameValue();
+        outFrameStdev.setValue(mStats[STDEV_INDEX]);
+        outPortStdev.pushFrame(outFrameStdev);
+    }
+
+    private native void regionscore(ByteBuffer imageBuffer, int width, int height, float left,
+            float top, float right, float bottom, float[] statsArray);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SurfaceHolderTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SurfaceHolderTarget.java
new file mode 100644
index 0000000..dac723b
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/SurfaceHolderTarget.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.image;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.RenderTarget;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.ViewFilter;
+
+public class SurfaceHolderTarget extends ViewFilter {
+
+    private SurfaceHolder mSurfaceHolder = null;
+    private RenderTarget mRenderTarget = null;
+    private ImageShader mShader = null;
+    private boolean mHasSurface = false;
+
+    private SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            // This just makes sure the holder is still the one we expect.
+            onSurfaceCreated(holder);
+        }
+
+        @Override
+        public void surfaceCreated (SurfaceHolder holder) {
+            onSurfaceCreated(holder);
+        }
+
+        @Override
+        public void surfaceDestroyed (SurfaceHolder holder) {
+            onDestroySurface();
+        }
+    };
+
+    public SurfaceHolderTarget(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public void onBindToView(View view) {
+        if (view instanceof SurfaceView) {
+            SurfaceHolder holder = ((SurfaceView)view).getHolder();
+            if (holder == null) {
+                throw new RuntimeException("Could not get SurfaceHolder from SurfaceView "
+                    + view + "!");
+            }
+            setSurfaceHolder(holder);
+        } else {
+            throw new IllegalArgumentException("View must be a SurfaceView!");
+        }
+    }
+
+    public void setSurfaceHolder(SurfaceHolder holder) {
+        if (isRunning()) {
+            throw new IllegalStateException("Cannot set SurfaceHolder while running!");
+        }
+        mSurfaceHolder = holder;
+    }
+
+    public synchronized void onDestroySurface() {
+        if (mRenderTarget != null) {
+            mRenderTarget.release();
+            mRenderTarget = null;
+        }
+        mHasSurface = false;
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        return super.getSignature()
+            .addInputPort("image", Signature.PORT_REQUIRED, imageType)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onInputPortOpen(InputPort port) {
+        super.connectViewInputs(port);
+    }
+
+    @Override
+    protected synchronized void onPrepare() {
+        if (isOpenGLSupported()) {
+            mShader = ImageShader.createIdentity();
+        }
+    }
+
+    @Override
+    protected synchronized void onOpen() {
+        mSurfaceHolder.addCallback(mSurfaceHolderListener);
+        Surface surface = mSurfaceHolder.getSurface();
+        mHasSurface = (surface != null) && surface.isValid();
+    }
+
+    @Override
+    protected synchronized void onProcess() {
+        FrameImage2D image = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        if (mHasSurface) {
+            // Synchronize the surface holder in case another filter is accessing this surface.
+            synchronized (mSurfaceHolder) {
+                if (isOpenGLSupported()) {
+                    renderGL(image);
+                } else {
+                    renderCanvas(image);
+                }
+            }
+        }
+    }
+
+    /**
+     * Renders the given frame to the screen using GLES2.
+     * @param image the image to render
+     */
+    private void renderGL(FrameImage2D image) {
+        if (mRenderTarget == null) {
+            mRenderTarget = RenderTarget.currentTarget().forSurfaceHolder(mSurfaceHolder);
+            mRenderTarget.registerAsDisplaySurface();
+        }
+        Rect frameRect = new Rect(0, 0, image.getWidth(), image.getHeight());
+        Rect surfRect = mSurfaceHolder.getSurfaceFrame();
+        setupShader(mShader, frameRect, surfRect);
+        mShader.process(image.lockTextureSource(),
+                        mRenderTarget,
+                        surfRect.width(),
+                        surfRect.height());
+        image.unlock();
+        mRenderTarget.swapBuffers();
+    }
+
+    /**
+     * Renders the given frame to the screen using a Canvas.
+     * @param image the image to render
+     */
+    private void renderCanvas(FrameImage2D image) {
+        Canvas canvas = mSurfaceHolder.lockCanvas();
+        Bitmap bitmap = image.toBitmap();
+        Rect sourceRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        Rect surfaceRect = mSurfaceHolder.getSurfaceFrame();
+        RectF targetRect = getTargetRect(sourceRect, surfaceRect);
+        canvas.drawColor(Color.BLACK);
+        if (targetRect.width() > 0 && targetRect.height() > 0) {
+            canvas.scale(surfaceRect.width(), surfaceRect.height());
+            canvas.drawBitmap(bitmap, sourceRect, targetRect, new Paint());
+        }
+        mSurfaceHolder.unlockCanvasAndPost(canvas);
+    }
+
+    @Override
+    protected synchronized void onClose() {
+        if (mRenderTarget != null) {
+            mRenderTarget.unregisterAsDisplaySurface();
+            mRenderTarget.release();
+            mRenderTarget = null;
+        }
+        if (mSurfaceHolder != null) {
+            mSurfaceHolder.removeCallback(mSurfaceHolderListener);
+        }
+    }
+
+    private synchronized void onSurfaceCreated(SurfaceHolder holder) {
+        if (mSurfaceHolder != holder) {
+            throw new RuntimeException("Unexpected Holder!");
+        }
+        mHasSurface = true;
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextViewTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextViewTarget.java
new file mode 100644
index 0000000..5aafced
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextViewTarget.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.text;
+
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.ViewFilter;
+
+public class TextViewTarget extends ViewFilter {
+
+    private TextView mTextView = null;
+
+    public TextViewTarget(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public void onBindToView(View view) {
+        if (view instanceof TextView) {
+            mTextView = (TextView)view;
+        } else {
+            throw new IllegalArgumentException("View must be a TextView!");
+        }
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("text", Signature.PORT_REQUIRED, FrameType.single(String.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameValue textFrame = getConnectedInputPort("text").pullFrame().asFrameValue();
+        final String text = (String)textFrame.getValue();
+        if (mTextView != null) {
+            mTextView.post(new Runnable() {
+                @Override
+                public void run() {
+                    mTextView.setText(text);
+                }
+            });
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextureSource.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextureSource.java
new file mode 100644
index 0000000..30fda82
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TextureSource.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.graphics.Bitmap;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+
+import java.nio.ByteBuffer;
+
+public class TextureSource {
+
+    private int mTexId;
+    private int mTarget;
+    private boolean mIsOwner;
+    private boolean mIsAllocated = false;
+
+    public static TextureSource fromTexture(int texId, int target) {
+        return new TextureSource(texId, target, false);
+    }
+
+    public static TextureSource fromTexture(int texId) {
+        return new TextureSource(texId, GLES20.GL_TEXTURE_2D, false);
+    }
+
+    public static TextureSource newTexture() {
+        return new TextureSource(GLToolbox.generateTexture(), GLES20.GL_TEXTURE_2D, true);
+    }
+
+    public static TextureSource newExternalTexture() {
+        return new TextureSource(GLToolbox.generateTexture(),
+                                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+                                 true);
+    }
+
+    public int getTextureId() {
+        return mTexId;
+    }
+
+    public int getTarget() {
+        return mTarget;
+    }
+
+    public void bind() {
+        GLES20.glBindTexture(mTarget, mTexId);
+        GLToolbox.checkGlError("glBindTexture");
+    }
+
+    public void allocate(int width, int height) {
+        //Log.i("TextureSource", "Allocating empty texture " + mTexId + ": " + width + "x" + height + ".");
+        GLToolbox.allocateTexturePixels(mTexId, mTarget, width, height);
+        mIsAllocated = true;
+    }
+
+    public void allocateWithPixels(ByteBuffer pixels, int width, int height) {
+        //Log.i("TextureSource", "Uploading pixels to texture " + mTexId + ": " + width + "x" + height + ".");
+        GLToolbox.setTexturePixels(mTexId, mTarget, pixels, width, height);
+        mIsAllocated = true;
+    }
+
+    public void allocateWithBitmapPixels(Bitmap bitmap) {
+        //Log.i("TextureSource", "Uploading pixels to texture " + mTexId + "!");
+        GLToolbox.setTexturePixels(mTexId, mTarget, bitmap);
+        mIsAllocated = true;
+    }
+
+    public void generateMipmaps() {
+        GLES20.glBindTexture(mTarget, mTexId);
+        GLES20.glTexParameteri(mTarget,
+                               GLES20.GL_TEXTURE_MIN_FILTER,
+                               GLES20.GL_LINEAR_MIPMAP_LINEAR);
+        GLES20.glGenerateMipmap(mTarget);
+        GLES20.glBindTexture(mTarget, 0);
+    }
+
+    public void setParameter(int parameter, int value) {
+        GLES20.glBindTexture(mTarget, mTexId);
+        GLES20.glTexParameteri(mTarget, parameter, value);
+        GLES20.glBindTexture(mTarget, 0);
+    }
+
+    /**
+     * @hide
+     */
+    public void release() {
+        if (GLToolbox.isTexture(mTexId) && mIsOwner) {
+            GLToolbox.deleteTexture(mTexId);
+        }
+        mTexId = GLToolbox.textureNone();
+    }
+
+    @Override
+    public String toString() {
+        return "TextureSource(id=" + mTexId + ", target=" + mTarget + ")";
+    }
+
+    boolean isAllocated() {
+        return mIsAllocated;
+    }
+
+    private TextureSource(int texId, int target, boolean isOwner) {
+        mTexId = texId;
+        mTarget = target;
+        mIsOwner = isOwner;
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Throughput.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Throughput.java
new file mode 100644
index 0000000..c16aae0
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/Throughput.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.performance;
+
+public class Throughput {
+
+    private final int mTotalFrames;
+    private final int mPeriodFrames;
+    private final long mPeriodTime;
+    
+    public Throughput(int totalFrames, int periodFrames, long periodTime, int size) {
+        mTotalFrames = totalFrames;
+        mPeriodFrames = periodFrames;
+        mPeriodTime = periodTime;
+    }
+
+    public int getTotalFrameCount() {
+        return mTotalFrames;
+    }
+
+    public int getPeriodFrameCount() {
+        return mPeriodFrames;
+    }
+
+    public long getPeriodTime() {
+        return mPeriodTime;
+    }
+
+    public float getFramesPerSecond() {
+        return mPeriodFrames / (mPeriodTime / 1000.0f);
+    }
+
+    @Override
+    public String toString() {
+        return Math.round(getFramesPerSecond()) + " FPS";
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ThroughputFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ThroughputFilter.java
new file mode 100644
index 0000000..25243a7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ThroughputFilter.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.performance;
+
+import android.util.Log;
+import android.os.SystemClock;
+
+import androidx.media.filterfw.*;
+
+public class ThroughputFilter extends Filter {
+
+    private int mPeriod = 3;
+    private long mLastTime = 0;
+    private int mTotalFrameCount = 0;
+    private int mPeriodFrameCount = 0;
+
+    public ThroughputFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+
+    @Override
+    public Signature getSignature() {
+        FrameType throughputType = FrameType.single(Throughput.class);
+        return new Signature()
+            .addInputPort("frame", Signature.PORT_REQUIRED, FrameType.any())
+            .addOutputPort("throughput", Signature.PORT_REQUIRED, throughputType)
+            .addOutputPort("frame", Signature.PORT_REQUIRED, FrameType.any())
+            .addInputPort("period", Signature.PORT_OPTIONAL, FrameType.single(int.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("period")) {
+            port.bindToFieldNamed("mPeriod");
+        } else {
+            port.attachToOutputPort(getConnectedOutputPort("frame"));
+        }
+    }
+
+    @Override
+    protected void onOpen() {
+        mTotalFrameCount = 0;
+        mPeriodFrameCount = 0;
+        mLastTime = 0;
+    }
+
+    @Override
+    protected synchronized void onProcess() {
+        Frame inputFrame = getConnectedInputPort("frame").pullFrame();
+
+        // Update stats
+        ++mTotalFrameCount;
+        ++mPeriodFrameCount;
+
+        // Check clock
+        if (mLastTime == 0) {
+            mLastTime = SystemClock.elapsedRealtime();
+        }
+        long curTime = SystemClock.elapsedRealtime();
+
+        // Output throughput info if time period is up
+        if ((curTime - mLastTime) >= (mPeriod * 1000)) {
+            Log.i("Thru", "It is time!");
+            OutputPort tpPort = getConnectedOutputPort("throughput");
+            Throughput throughput = new Throughput(mTotalFrameCount,
+                                                   mPeriodFrameCount,
+                                                   curTime - mLastTime,
+                                                   inputFrame.getElementCount());
+            FrameValue throughputFrame = tpPort.fetchAvailableFrame(null).asFrameValue();
+            throughputFrame.setValue(throughput);
+            tpPort.pushFrame(throughputFrame);
+            mLastTime = curTime;
+            mPeriodFrameCount = 0;
+        }
+
+        getConnectedOutputPort("frame").pushFrame(inputFrame);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToGrayValuesFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToGrayValuesFilter.java
new file mode 100644
index 0000000..8e0fd6c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToGrayValuesFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013 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 androidx.media.filterpacks.image;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.RenderTarget;
+import androidx.media.filterfw.Signature;
+import androidx.media.filterfw.geometry.Quad;
+
+import java.nio.ByteBuffer;
+
+public class ToGrayValuesFilter extends Filter {
+
+    private final static String mGrayPackFragment =
+        "precision mediump float;\n" +
+        "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
+        "uniform sampler2D tex_sampler_0;\n" +
+        "uniform float pix_stride;\n" +
+        "varying vec2 v_texcoord;\n" +
+        "void main() {\n" +
+        "  for (int i = 0; i < 4; i++) {\n" +
+        // Here is an example showing how this works:
+        // Assuming the input texture is 1x4 while the output texture is 1x1
+        // the coordinates of the 4 input pixels will be:
+        // { (0.125, 0.5), (0.375, 0.5), (0.625, 0.5), (0.875, 0.5) }
+        // and the coordinates of the 1 output pixels will be:
+        // { (0.5, 0.5) }
+        // the equation below locates the 4 input pixels from the coordinate of the output pixel
+        "    vec4 p = texture2D(tex_sampler_0,\n" +
+        "                       v_texcoord + vec2(pix_stride * (float(i) - 1.5), 0.0));\n" +
+        "    gl_FragColor[i] = dot(p, coeff_y);\n" +
+        "  }\n" +
+        "}\n";
+
+    private ImageShader mShader;
+
+    private FrameType mImageInType;
+
+    public ToGrayValuesFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        mImageInType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.buffer2D(FrameType.ELEMENT_INT8);
+        return new Signature()
+            .addInputPort("image", Signature.PORT_REQUIRED, mImageInType)
+            .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onPrepare() {
+        if (isOpenGLSupported()) {
+            mShader = new ImageShader(mGrayPackFragment);
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("image");
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        int[] dim = inputImage.getDimensions();
+        FrameBuffer2D outputFrame;
+        ByteBuffer grayBuffer;
+
+        if (isOpenGLSupported()) {
+            // crop out the portion of inputImage that will be used to generate outputFrame.
+            int modular = dim[0] % 4;
+            int[] outDim = new int[] {dim[0] - modular, dim[1]};
+            outputFrame = outPort.fetchAvailableFrame(outDim).asFrameBuffer2D();
+            grayBuffer = outputFrame.lockBytes(Frame.MODE_WRITE);
+
+            int[] targetDims = new int[] { outDim[0] / 4, outDim[1] };
+            FrameImage2D targetFrame = Frame.create(mImageInType, targetDims).asFrameImage2D();
+            mShader.setSourceQuad(Quad.fromRect(0f, 0f, ((float)outDim[0])/dim[0], 1f));
+            mShader.setUniformValue("pix_stride", 1f / outDim[0]);
+            mShader.process(inputImage, targetFrame);
+            RenderTarget grayTarget = targetFrame.lockRenderTarget();
+            grayTarget.readPixelData(grayBuffer, targetDims[0], targetDims[1]);
+            targetFrame.unlock();
+            targetFrame.release();
+        } else {
+            outputFrame = outPort.fetchAvailableFrame(dim).asFrameBuffer2D();
+            grayBuffer = outputFrame.lockBytes(Frame.MODE_WRITE);
+            ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
+            if (!toGrayValues(inputBuffer, grayBuffer)) {
+                throw new RuntimeException(
+                        "Native implementation encountered an error during processing!");
+            }
+            inputImage.unlock();
+        }
+        outputFrame.unlock();
+        outPort.pushFrame(outputFrame);
+    }
+
+    private static native boolean toGrayValues(ByteBuffer imageBuffer, ByteBuffer grayBuffer);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToStringFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToStringFilter.java
new file mode 100644
index 0000000..7306b61
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ToStringFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.text;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public class ToStringFilter extends Filter {
+
+    public ToStringFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("object", Signature.PORT_REQUIRED, FrameType.single())
+            .addOutputPort("string", Signature.PORT_REQUIRED, FrameType.single(String.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameValue objectFrame = getConnectedInputPort("object").pullFrame().asFrameValue();
+        String outStr = objectFrame.getValue().toString();
+        OutputPort outPort = getConnectedOutputPort("string");
+        FrameValue stringFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        stringFrame.setValue(outStr);
+        outPort.pushFrame(stringFrame);
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TransformUtils.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TransformUtils.java
new file mode 100644
index 0000000..8dd1949
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/TransformUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.transform;
+
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.TextureSource;
+
+import java.util.Arrays;
+
+/** Internal class that contains utility functions used by the transform filters. **/
+class TransformUtils {
+
+    public static int powOf2(int x) {
+        --x;
+        // Fill with 1s
+        x |= x >> 1;
+        x |= x >> 2;
+        x |= x >> 4;
+        x |= x >> 8;
+        x |= x >> 16;
+        // Next int is now pow-of-2
+        return x + 1;
+    }
+
+    public static FrameImage2D makeMipMappedFrame(FrameImage2D current, int[] dimensions) {
+        // Note: Future versions of GLES will support NPOT mipmapping. When these become more
+        // widely used, we can add a check here to disable frame expansion on such devices.
+        int[] pow2Dims = new int[] { powOf2(dimensions[0]), powOf2(dimensions[1]) };
+        if (current == null) {
+            FrameType imageType = FrameType.image2D(FrameType.ELEMENT_RGBA8888,
+                                                    FrameType.READ_GPU | FrameType.WRITE_GPU);
+            current = Frame.create(imageType, pow2Dims).asFrameImage2D();
+        } else if (!Arrays.equals(dimensions, current.getDimensions())) {
+            current.resize(pow2Dims);
+        }
+        return current;
+    }
+
+    public static FrameImage2D makeTempFrame(FrameImage2D current, int[] dimensions) {
+        if (current == null) {
+            FrameType imageType = FrameType.image2D(FrameType.ELEMENT_RGBA8888,
+                                                    FrameType.READ_GPU | FrameType.WRITE_GPU);
+            current = Frame.create(imageType, dimensions).asFrameImage2D();
+        } else if (!Arrays.equals(dimensions, current.getDimensions())) {
+            current.resize(dimensions);
+        }
+        return current;
+    }
+
+    public static void generateMipMaps(FrameImage2D frame) {
+        TextureSource texture = frame.lockTextureSource();
+        texture.generateMipmaps();
+        frame.unlock();
+    }
+
+    public static void setTextureParameter(FrameImage2D frame, int param, int value) {
+        TextureSource texture = frame.lockTextureSource();
+        texture.setParameter(param, value);
+        frame.unlock();
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ValueTarget.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ValueTarget.java
new file mode 100644
index 0000000..e2db8af
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ValueTarget.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.base;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.media.filterfw.*;
+
+public final class ValueTarget extends Filter {
+
+    public static interface ValueListener {
+        public void onReceivedValue(Object value);
+    }
+
+    private ValueListener mListener = null;
+    private Handler mHandler = null;
+
+    public ValueTarget(MffContext context, String name) {
+        super(context, name);
+    }
+
+    public void setListener(ValueListener listener, boolean onCallerThread) {
+        if (isRunning()) {
+            throw new IllegalStateException("Attempting to bind filter to callback while it is "
+                + "running!");
+        }
+        mListener = listener;
+        if (onCallerThread) {
+            if (Looper.myLooper() == null) {
+                throw new IllegalArgumentException("Attempting to set callback on thread which "
+                    + "has no looper!");
+            }
+            mHandler = new Handler();
+        }
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("value", Signature.PORT_REQUIRED, FrameType.single())
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameValue valueFrame = getConnectedInputPort("value").pullFrame().asFrameValue();
+        if (mListener != null) {
+            if (mHandler != null) {
+                postValueToUiThread(valueFrame.getValue());
+            } else {
+                mListener.onReceivedValue(valueFrame.getValue());
+            }
+        }
+    }
+
+    private void postValueToUiThread(final Object value) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onReceivedValue(value);
+            }
+        });
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/VariableSource.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/VariableSource.java
new file mode 100644
index 0000000..69060cb
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/VariableSource.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterpacks.base;
+
+import androidx.media.filterfw.*;
+
+// TODO: Rename back to ValueSource? Seems to make more sense even if we use it as a Variable
+// in some contexts.
+public final class VariableSource extends Filter {
+
+    private Object mValue = null;
+    private OutputPort mOutputPort = null;
+
+    public VariableSource(MffContext context, String name) {
+        super(context, name);
+    }
+
+    public synchronized void setValue(Object value) {
+        mValue = value;
+    }
+
+    public synchronized Object getValue() {
+        return mValue;
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addOutputPort("value", Signature.PORT_REQUIRED, FrameType.single())
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onPrepare() {
+        mOutputPort = getConnectedOutputPort("value");
+    }
+
+    @Override
+    protected synchronized void onProcess() {
+        FrameValue frame = mOutputPort.fetchAvailableFrame(null).asFrameValue();
+        frame.setValue(mValue);
+        mOutputPort.pushFrame(frame);
+    }
+
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ViewFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ViewFilter.java
new file mode 100644
index 0000000..ddb7222
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/ViewFilter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.View;
+
+/**
+ * TODO: Move this to filterpacks/base?
+ */
+public abstract class ViewFilter extends Filter {
+
+    public static final int SCALE_STRETCH = 1;
+    public static final int SCALE_FIT = 2;
+    public static final int SCALE_FILL = 3;
+
+    protected int mScaleMode = SCALE_FIT;
+    protected float[] mClearColor = new float[] { 0f, 0f, 0f, 1f };
+    protected boolean mFlipVertically = true;
+
+    private String mRequestedScaleMode = null;
+
+    protected ViewFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    /**
+     * Binds the filter to a view.
+     * View filters support visualizing data to a view. Check the specific filter documentation
+     * for details. The view may be bound only if the filter's graph is not running.
+     *
+     * @param view the view to bind to.
+     * @throws IllegalStateException if the method is called while the graph is running.
+     */
+    public void bindToView(View view) {
+        if (isRunning()) {
+            throw new IllegalStateException("Attempting to bind filter to view while it is "
+                + "running!");
+        }
+        onBindToView(view);
+    }
+
+    public void setScaleMode(int scaleMode) {
+        if (isRunning()) {
+            throw new IllegalStateException("Attempting to change scale mode while filter is "
+                + "running!");
+        }
+        mScaleMode = scaleMode;
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addInputPort("scaleMode", Signature.PORT_OPTIONAL, FrameType.single(String.class))
+            .addInputPort("flip", Signature.PORT_OPTIONAL, FrameType.single(boolean.class));
+    }
+
+    /**
+     * Subclasses must override this method to bind their filter to the specified view.
+     *
+     * When this method is called, Filter implementations may assume that the graph is not
+     * currently running.
+     */
+    protected abstract void onBindToView(View view);
+
+    /**
+     * TODO: Document.
+     */
+    protected RectF getTargetRect(Rect frameRect, Rect bufferRect) {
+        RectF result = new RectF();
+        if (bufferRect.width() > 0 && bufferRect.height() > 0) {
+            float frameAR = (float)frameRect.width() / frameRect.height();
+            float bufferAR = (float)bufferRect.width() / bufferRect.height();
+            float relativeAR = bufferAR / frameAR;
+            switch (mScaleMode) {
+                case SCALE_STRETCH:
+                    result.set(0f, 0f, 1f, 1f);
+                    break;
+                case SCALE_FIT:
+                    if (relativeAR > 1.0f) {
+                        float x = 0.5f - 0.5f / relativeAR;
+                        float y = 0.0f;
+                        result.set(x, y, x + 1.0f / relativeAR, y + 1.0f);
+                    } else {
+                        float x = 0.0f;
+                        float y = 0.5f - 0.5f * relativeAR;
+                        result.set(x, y, x + 1.0f, y + relativeAR);
+                    }
+                    break;
+                case SCALE_FILL:
+                    if (relativeAR > 1.0f) {
+                        float x = 0.0f;
+                        float y = 0.5f - 0.5f * relativeAR;
+                        result.set(x, y, x + 1.0f, y + relativeAR);
+                    } else {
+                        float x = 0.5f - 0.5f / relativeAR;
+                        float y = 0.0f;
+                        result.set(x, y, x + 1.0f / relativeAR, y + 1.0f);
+                    }
+                    break;
+            }
+        }
+        return result;
+    }
+
+    protected void connectViewInputs(InputPort port) {
+        if (port.getName().equals("scaleMode")) {
+            port.bindToListener(mScaleModeListener);
+            port.setAutoPullEnabled(true);
+        } else if (port.getName().equals("flip")) {
+            port.bindToFieldNamed("mFlipVertically");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    protected void setupShader(ImageShader shader, Rect frameRect, Rect outputRect) {
+        shader.setTargetRect(getTargetRect(frameRect, outputRect));
+        shader.setClearsOutput(true);
+        shader.setClearColor(mClearColor);
+        if (mFlipVertically) {
+            shader.setSourceRect(0f, 1f, 1f, -1f);
+        }
+    }
+
+    private InputPort.FrameListener mScaleModeListener = new InputPort.FrameListener() {
+        @Override
+        public void onFrameReceived(InputPort port, Frame frame) {
+            String scaleMode = (String)frame.asFrameValue().getValue();
+            if (!scaleMode.equals(mRequestedScaleMode)) {
+                mRequestedScaleMode = scaleMode;
+                if (scaleMode.equals("stretch")) {
+                    mScaleMode = SCALE_STRETCH;
+                } else if (scaleMode.equals("fit")) {
+                    mScaleMode = SCALE_FIT;
+                } else if (scaleMode.equals("fill")) {
+                    mScaleMode = SCALE_FILL;
+                } else {
+                    throw new RuntimeException("Unknown scale-mode '" + scaleMode + "'!");
+                }
+            }
+        }
+    };
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioSample.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioSample.java
new file mode 100644
index 0000000..c7eec26
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioSample.java
@@ -0,0 +1,31 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+public class AudioSample {
+
+    public final int sampleRate;
+    public final int channelCount;
+    public final byte[] bytes;
+
+    public AudioSample(int sampleRate, int channelCount, byte[] bytes) {
+        this.sampleRate = sampleRate;
+        this.channelCount = channelCount;
+        this.bytes = bytes;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioTrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioTrackDecoder.java
new file mode 100644
index 0000000..0219fd7
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/AudioTrackDecoder.java
@@ -0,0 +1,119 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+
+import androidx.media.filterfw.FrameValue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * {@link TrackDecoder} for decoding audio tracks.
+ *
+ * TODO: find out if we always get 16 bits per channel and document.
+ */
+@TargetApi(16)
+public class AudioTrackDecoder extends TrackDecoder {
+
+    private final ByteArrayOutputStream mAudioByteStream; // Guarded by mAudioByteStreamLock.
+    private final Object mAudioByteStreamLock;
+
+    private int mAudioSampleRate;
+    private int mAudioChannelCount;
+    private long mAudioPresentationTimeUs;
+
+    public AudioTrackDecoder(int trackIndex, MediaFormat format, Listener listener) {
+        super(trackIndex, format, listener);
+
+        if (!DecoderUtil.isAudioFormat(format)) {
+            throw new IllegalArgumentException(
+                    "AudioTrackDecoder can only be used with audio formats");
+        }
+
+        mAudioByteStream = new ByteArrayOutputStream();
+        mAudioByteStreamLock = new Object();
+
+        mAudioSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+        mAudioChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+    }
+
+    @Override
+    protected MediaCodec initMediaCodec(MediaFormat format) {
+        MediaCodec mediaCodec = MediaCodec.createDecoderByType(
+                format.getString(MediaFormat.KEY_MIME));
+        mediaCodec.configure(format, null, null, 0);
+        return mediaCodec;
+    }
+
+    @Override
+    protected boolean onDataAvailable(
+            MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) {
+        ByteBuffer buffer = buffers[bufferIndex];
+        byte[] data = new byte[info.size];
+        buffer.position(info.offset);
+        buffer.get(data, 0, info.size);
+
+        synchronized (mAudioByteStreamLock) {
+            try {
+                if (mAudioByteStream.size() == 0 && data.length > 0) {
+                    mAudioPresentationTimeUs = info.presentationTimeUs;
+                }
+
+                mAudioByteStream.write(data);
+            } catch (IOException e) {
+                // Just drop the audio sample.
+            }
+        }
+
+        buffer.clear();
+        codec.releaseOutputBuffer(bufferIndex, false);
+        notifyListener();
+        return true;
+    }
+
+    /**
+     * Fills the argument {@link FrameValue} with an audio sample containing the audio that was
+     * decoded since the last call of this method. The decoder's buffer is cleared as a result.
+     */
+    public void grabSample(FrameValue audioFrame) {
+        synchronized (mAudioByteStreamLock) {
+            if (audioFrame != null) {
+                AudioSample sample = new AudioSample(
+                        mAudioSampleRate, mAudioChannelCount, mAudioByteStream.toByteArray());
+                audioFrame.setValue(sample);
+                audioFrame.setTimestamp(mAudioPresentationTimeUs * 1000);
+            }
+            clearBuffer();
+        }
+    }
+
+    /**
+     * Clears the decoder's buffer.
+     */
+    public void clearBuffer() {
+        synchronized (mAudioByteStreamLock) {
+            mAudioByteStream.reset();
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/CpuVideoTrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/CpuVideoTrackDecoder.java
new file mode 100644
index 0000000..96f3059
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/CpuVideoTrackDecoder.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.util.SparseIntArray;
+import androidx.media.filterfw.ColorSpace;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.PixelUtils;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * {@link TrackDecoder} that decodes a video track and renders the frames onto a
+ * {@link SurfaceTexture}.
+ *
+ * This implementation purely uses CPU based methods to decode and color-convert the frames.
+ */
+@TargetApi(16)
+public class CpuVideoTrackDecoder extends VideoTrackDecoder {
+
+    private static final int COLOR_FORMAT_UNSET = -1;
+
+    private final int mWidth;
+    private final int mHeight;
+
+    private int mColorFormat = COLOR_FORMAT_UNSET;
+    private long mCurrentPresentationTimeUs;
+    private ByteBuffer mDecodedBuffer;
+    private ByteBuffer mUnrotatedBytes;
+
+    protected CpuVideoTrackDecoder(int trackIndex, MediaFormat format, Listener listener) {
+        super(trackIndex, format, listener);
+
+        mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+        mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+    }
+
+    @Override
+    protected MediaCodec initMediaCodec(MediaFormat format) {
+        // Find a codec for our video that can output to one of our supported color-spaces
+        MediaCodec mediaCodec = findDecoderCodec(format, new int[] {
+                CodecCapabilities.COLOR_Format32bitARGB8888,
+                CodecCapabilities.COLOR_FormatYUV420Planar});
+        if (mediaCodec == null) {
+            throw new RuntimeException(
+                    "Could not find a suitable decoder for format: " + format + "!");
+        }
+        mediaCodec.configure(format, null, null, 0);
+        return mediaCodec;
+    }
+
+    @Override
+    protected boolean onDataAvailable(
+            MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) {
+
+        mCurrentPresentationTimeUs = info.presentationTimeUs;
+        mDecodedBuffer = buffers[bufferIndex];
+
+        if (mColorFormat == -1) {
+            mColorFormat = codec.getOutputFormat().getInteger(MediaFormat.KEY_COLOR_FORMAT);
+        }
+
+        markFrameAvailable();
+        notifyListener();
+
+        // Wait for the grab before we release this buffer.
+        waitForFrameGrab();
+
+        codec.releaseOutputBuffer(bufferIndex, false);
+
+        return false;
+    }
+
+    @Override
+    protected void copyFrameDataTo(FrameImage2D outputVideoFrame, int rotation) {
+        // Calculate output dimensions
+        int outputWidth = mWidth;
+        int outputHeight = mHeight;
+        if (needSwapDimension(rotation)) {
+            outputWidth = mHeight;
+            outputHeight = mWidth;
+        }
+
+        // Create output frame
+        outputVideoFrame.resize(new int[] {outputWidth, outputHeight});
+        outputVideoFrame.setTimestamp(mCurrentPresentationTimeUs * 1000);
+        ByteBuffer outBytes = outputVideoFrame.lockBytes(Frame.MODE_WRITE);
+
+        // Set data
+        if (rotation == MediaDecoder.ROTATE_NONE) {
+            convertImage(mDecodedBuffer, outBytes, mColorFormat, mWidth, mHeight);
+        } else {
+            if (mUnrotatedBytes == null) {
+                mUnrotatedBytes = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
+            }
+            // TODO: This could be optimized by including the rotation in the color conversion.
+            convertImage(mDecodedBuffer, mUnrotatedBytes, mColorFormat, mWidth, mHeight);
+            copyRotate(mUnrotatedBytes, outBytes, rotation);
+        }
+        outputVideoFrame.unlock();
+    }
+
+    /**
+     * Copy the input data to the output data applying the specified rotation.
+     *
+     * @param input The input image data
+     * @param output Buffer for the output image data
+     * @param rotation The rotation to apply
+     */
+    private void copyRotate(ByteBuffer input, ByteBuffer output, int rotation) {
+        int offset;
+        int pixStride;
+        int rowStride;
+        switch (rotation) {
+            case MediaDecoder.ROTATE_NONE:
+                offset = 0;
+                pixStride = 1;
+                rowStride = mWidth;
+                break;
+            case MediaDecoder.ROTATE_90_LEFT:
+                offset = (mWidth - 1) * mHeight;
+                pixStride = -mHeight;
+                rowStride = 1;
+                break;
+            case MediaDecoder.ROTATE_90_RIGHT:
+                offset = mHeight - 1;
+                pixStride = mHeight;
+                rowStride = -1;
+                break;
+            case MediaDecoder.ROTATE_180:
+                offset = mWidth * mHeight - 1;
+                pixStride = -1;
+                rowStride = -mWidth;
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported rotation " + rotation + "!");
+        }
+        PixelUtils.copyPixels(input, output, mWidth, mHeight, offset, pixStride, rowStride);
+    }
+
+    /**
+     * Looks for a codec with the specified requirements.
+     *
+     * The set of codecs will be filtered down to those that meet the following requirements:
+     * <ol>
+     *   <li>The codec is a decoder.</li>
+     *   <li>The codec can decode a video of the specified format.</li>
+     *   <li>The codec can decode to one of the specified color formats.</li>
+     * </ol>
+     * If multiple codecs are found, the one with the preferred color-format is taken. Color format
+     * preference is determined by the order of their appearance in the color format array.
+     *
+     * @param format The format the codec must decode.
+     * @param requiredColorFormats Array of target color spaces ordered by preference.
+     * @return A codec that meets the requirements, or null if no such codec was found.
+     */
+    private static MediaCodec findDecoderCodec(MediaFormat format, int[] requiredColorFormats) {
+        TreeMap<Integer, String> candidateCodecs = new TreeMap<Integer, String>();
+        SparseIntArray colorPriorities = intArrayToPriorityMap(requiredColorFormats);
+        for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
+            // Get next codec
+            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+
+            // Check that this is a decoder
+            if (info.isEncoder()) {
+                continue;
+            }
+
+            // Check if this codec can decode the video in question
+            String requiredType = format.getString(MediaFormat.KEY_MIME);
+            String[] supportedTypes = info.getSupportedTypes();
+            Set<String> typeSet = new HashSet<String>(Arrays.asList(supportedTypes));
+
+            // Check if it can decode to one of the required color formats
+            if (typeSet.contains(requiredType)) {
+                CodecCapabilities capabilities = info.getCapabilitiesForType(requiredType);
+                for (int supportedColorFormat : capabilities.colorFormats) {
+                    if (colorPriorities.indexOfKey(supportedColorFormat) >= 0) {
+                        int priority = colorPriorities.get(supportedColorFormat);
+                        candidateCodecs.put(priority, info.getName());
+                    }
+                }
+            }
+        }
+
+        // Pick the best codec (with the highest color priority)
+        if (candidateCodecs.isEmpty()) {
+            return null;
+        } else {
+            String bestCodec = candidateCodecs.firstEntry().getValue();
+            return MediaCodec.createByCodecName(bestCodec);
+        }
+    }
+
+    private static SparseIntArray intArrayToPriorityMap(int[] values) {
+        SparseIntArray result = new SparseIntArray();
+        for (int priority = 0; priority < values.length; ++priority) {
+            result.append(values[priority], priority);
+        }
+        return result;
+    }
+
+    private static void convertImage(
+            ByteBuffer input, ByteBuffer output, int colorFormat, int width, int height) {
+        switch (colorFormat) {
+            case CodecCapabilities.COLOR_Format32bitARGB8888:
+                ColorSpace.convertArgb8888ToRgba8888(input, output, width, height);
+                break;
+            case CodecCapabilities.COLOR_FormatYUV420Planar:
+                ColorSpace.convertYuv420pToRgba8888(input, output, width, height);
+                break;
+            default:
+                throw new RuntimeException("Unsupported color format: " + colorFormat + "!");
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/DecoderUtil.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/DecoderUtil.java
new file mode 100644
index 0000000..ec0ead0
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/DecoderUtil.java
@@ -0,0 +1,33 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+
+@TargetApi(16)
+public class DecoderUtil {
+
+    public static boolean isAudioFormat(MediaFormat format) {
+        return format.getString(MediaFormat.KEY_MIME).startsWith("audio/");
+    }
+
+    public static boolean isVideoFormat(MediaFormat format) {
+        return format.getString(MediaFormat.KEY_MIME).startsWith("video/");
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/GpuVideoTrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/GpuVideoTrackDecoder.java
new file mode 100644
index 0000000..bbba9d8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/GpuVideoTrackDecoder.java
@@ -0,0 +1,210 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.graphics.SurfaceTexture.OnFrameAvailableListener;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+import android.view.Surface;
+
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.ImageShader;
+import androidx.media.filterfw.TextureSource;
+
+import java.nio.ByteBuffer;
+
+/**
+ * {@link TrackDecoder} that decodes a video track and renders the frames onto a
+ * {@link SurfaceTexture}.
+ *
+ * This implementation uses the GPU for image operations such as copying
+ * and color-space conversion.
+ */
+@TargetApi(16)
+public class GpuVideoTrackDecoder extends VideoTrackDecoder {
+
+    /**
+     * Identity fragment shader for external textures.
+     */
+    private static final String COPY_FRAGMENT_SHADER =
+            "#extension GL_OES_EGL_image_external : require\n" +
+            "precision mediump float;\n" +
+            "uniform samplerExternalOES tex_sampler_0;\n" +
+            "varying vec2 v_texcoord;\n" +
+            "void main() {\n" +
+            "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+            "}\n";
+
+    private final TextureSource mTextureSource;
+    private final SurfaceTexture mSurfaceTexture; // Access guarded by mFrameMonitor.
+    private final float[] mTransformMatrix;
+
+    private final int mOutputWidth;
+    private final int mOutputHeight;
+
+    private ImageShader mImageShader;
+
+    private long mCurrentPresentationTimeUs;
+
+    public GpuVideoTrackDecoder(
+            int trackIndex, MediaFormat format, Listener listener) {
+        super(trackIndex, format, listener);
+
+        // Create a surface texture to be used by the video track decoder.
+        mTextureSource = TextureSource.newExternalTexture();
+        mSurfaceTexture = new SurfaceTexture(mTextureSource.getTextureId());
+        mSurfaceTexture.detachFromGLContext();
+        mSurfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {
+            @Override
+            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+                markFrameAvailable();
+            }
+        });
+
+        mOutputWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+        mOutputHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+
+        mTransformMatrix = new float[16];
+    }
+
+    @Override
+    protected MediaCodec initMediaCodec(MediaFormat format) {
+        Surface surface = new Surface(mSurfaceTexture);
+        MediaCodec mediaCodec = MediaCodec.createDecoderByType(
+                format.getString(MediaFormat.KEY_MIME));
+        mediaCodec.configure(format, surface, null, 0);
+        surface.release();
+        return mediaCodec;
+    }
+
+    @Override
+    protected boolean onDataAvailable(
+            MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) {
+        boolean textureAvailable = waitForFrameGrab();
+
+        mCurrentPresentationTimeUs = info.presentationTimeUs;
+
+        // Only render the next frame if we weren't interrupted.
+        codec.releaseOutputBuffer(bufferIndex, textureAvailable);
+
+        if (textureAvailable) {
+            if (updateTexture()) {
+                notifyListener();
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Waits for the texture's {@link OnFrameAvailableListener} to be notified and then updates
+     * the internal {@link SurfaceTexture}.
+     */
+    private boolean updateTexture() {
+        // Wait for the frame we just released to appear in the texture.
+        synchronized (mFrameMonitor) {
+            try {
+                while (!mFrameAvailable) {
+                    mFrameMonitor.wait();
+                }
+                mSurfaceTexture.attachToGLContext(mTextureSource.getTextureId());
+                mSurfaceTexture.updateTexImage();
+                mSurfaceTexture.detachFromGLContext();
+                return true;
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+    }
+
+    @Override
+    protected void copyFrameDataTo(FrameImage2D outputVideoFrame, int rotation) {
+        TextureSource targetTexture = TextureSource.newExternalTexture();
+        mSurfaceTexture.attachToGLContext(targetTexture.getTextureId());
+        mSurfaceTexture.getTransformMatrix(mTransformMatrix);
+
+        ImageShader imageShader = getImageShader();
+        imageShader.setSourceTransform(mTransformMatrix);
+
+        int outputWidth = mOutputWidth;
+        int outputHeight = mOutputHeight;
+        if (rotation != 0) {
+            float[] targetCoords = getRotationCoords(rotation);
+            imageShader.setTargetCoords(targetCoords);
+            if (needSwapDimension(rotation)) {
+                outputWidth = mOutputHeight;
+                outputHeight = mOutputWidth;
+            }
+        }
+        outputVideoFrame.resize(new int[] { outputWidth, outputHeight });
+        imageShader.process(
+                targetTexture,
+                outputVideoFrame.lockRenderTarget(),
+                outputWidth,
+                outputHeight);
+        outputVideoFrame.setTimestamp(mCurrentPresentationTimeUs * 1000);
+        outputVideoFrame.unlock();
+        targetTexture.release();
+
+        mSurfaceTexture.detachFromGLContext();
+    }
+
+    @Override
+    public void release() {
+        super.release();
+        synchronized (mFrameMonitor) {
+            mTextureSource.release();
+            mSurfaceTexture.release();
+        }
+    }
+
+    /*
+     * This method has to be called on the MFF processing thread.
+     */
+    private ImageShader getImageShader() {
+        if (mImageShader == null) {
+            mImageShader = new ImageShader(COPY_FRAGMENT_SHADER);
+            mImageShader.setTargetRect(0f, 1f, 1f, -1f);
+        }
+        return mImageShader;
+    }
+
+    /**
+     * Get the quad coords for rotation.
+     * @param rotation applied to the frame, value is one of
+     *   {ROTATE_NONE, ROTATE_90_RIGHT, ROTATE_180, ROTATE_90_LEFT}
+     * @return coords the calculated quad coords for the given rotation
+     */
+    private static float[] getRotationCoords(int rotation) {
+         switch(rotation) {
+             case MediaDecoder.ROTATE_90_RIGHT:
+                 return new float[] { 0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f };
+             case MediaDecoder.ROTATE_180:
+                 return new float[] { 1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f };
+             case MediaDecoder.ROTATE_90_LEFT:
+                 return new float[] { 1f, 1f, 1f, 0f, 0f, 1f, 0f, 0f };
+             case MediaDecoder.ROTATE_NONE:
+                 return new float[] { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f };
+             default:
+                 throw new IllegalArgumentException("Unsupported rotation angle.");
+         }
+     }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java
new file mode 100644
index 0000000..aa57394
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java
@@ -0,0 +1,426 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.RenderTarget;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+@TargetApi(16)
+public class MediaDecoder implements
+        Runnable,
+        TrackDecoder.Listener {
+
+    public interface Listener {
+        /**
+         * Notifies a listener when a decoded video frame is available. The listener should use
+         * {@link MediaDecoder#grabVideoFrame(FrameImage2D, int)} to grab the video data for this
+         * frame.
+         */
+        void onVideoFrameAvailable();
+
+        /**
+         * Notifies a listener when one or more audio samples are available. The listener should use
+         * {@link MediaDecoder#grabAudioSamples(FrameValue)} to grab the audio samples.
+         */
+        void onAudioSamplesAvailable();
+
+        /**
+         * Notifies a listener that decoding has started. This method is called on the decoder
+         * thread.
+         */
+        void onDecodingStarted();
+
+        /**
+         * Notifies a listener that decoding has stopped. This method is called on the decoder
+         * thread.
+         */
+        void onDecodingStopped();
+
+        /**
+         * Notifies a listener that an error occurred. If an error occurs, {@link MediaDecoder} is
+         * stopped and no more events are reported to this {@link Listener}'s callbacks.
+         * This method is called on the decoder thread.
+         */
+        void onError(Exception e);
+    }
+
+    public static final int ROTATE_NONE = 0;
+    public static final int ROTATE_90_RIGHT = 90;
+    public static final int ROTATE_180 = 180;
+    public static final int ROTATE_90_LEFT = 270;
+
+    private static final String LOG_TAG = "MediaDecoder";
+    private static final boolean DEBUG = false;
+
+    private static final int MAX_EVENTS = 32;
+    private static final int EVENT_START = 0;
+    private static final int EVENT_STOP = 1;
+    private static final int EVENT_EOF = 2;
+
+    private final Listener mListener;
+    private final Uri mUri;
+    private final Context mContext;
+
+    private final LinkedBlockingQueue<Integer> mEventQueue;
+
+    private final Thread mDecoderThread;
+
+    private MediaExtractor mMediaExtractor;
+
+    private RenderTarget mRenderTarget;
+
+    private int mDefaultRotation;
+    private int mVideoTrackIndex;
+    private int mAudioTrackIndex;
+
+    private VideoTrackDecoder mVideoTrackDecoder;
+    private AudioTrackDecoder mAudioTrackDecoder;
+
+    private boolean mStarted;
+
+    private long mStartMicros;
+
+    private boolean mOpenGLEnabled = true;
+
+    private boolean mSignaledEndOfInput;
+    private boolean mSeenEndOfAudioOutput;
+    private boolean mSeenEndOfVideoOutput;
+
+    public MediaDecoder(Context context, Uri uri, Listener listener) {
+        this(context, uri, 0, listener);
+    }
+
+    public MediaDecoder(Context context, Uri uri, long startMicros, Listener listener) {
+        if (context == null) {
+            throw new NullPointerException("context cannot be null");
+        }
+        mContext = context;
+
+        if (uri == null) {
+            throw new NullPointerException("uri cannot be null");
+        }
+        mUri = uri;
+
+        if (startMicros < 0) {
+            throw new IllegalArgumentException("startMicros cannot be negative");
+        }
+        mStartMicros = startMicros;
+
+        if (listener == null) {
+            throw new NullPointerException("listener cannot be null");
+        }
+        mListener = listener;
+
+        mEventQueue = new LinkedBlockingQueue<Integer>(MAX_EVENTS);
+        mDecoderThread = new Thread(this);
+    }
+
+    /**
+     * Set whether decoder may use OpenGL for decoding.
+     *
+     * This must be called before {@link #start()}.
+     *
+     * @param enabled flag whether to enable OpenGL decoding (default is true).
+     */
+    public void setOpenGLEnabled(boolean enabled) {
+        // If event-queue already has events, we have started already.
+        if (mEventQueue.isEmpty()) {
+            mOpenGLEnabled = enabled;
+        } else {
+            throw new IllegalStateException(
+                    "Must call setOpenGLEnabled() before calling start()!");
+        }
+    }
+
+    /**
+     * Returns whether OpenGL is enabled for decoding.
+     *
+     * @return whether OpenGL is enabled for decoding.
+     */
+    public boolean isOpenGLEnabled() {
+        return mOpenGLEnabled;
+    }
+
+    public void start() {
+        mEventQueue.offer(EVENT_START);
+        mDecoderThread.start();
+    }
+
+    public void stop() {
+        stop(true);
+    }
+
+    private void stop(boolean manual) {
+        if (manual) {
+            mEventQueue.offer(EVENT_STOP);
+            mDecoderThread.interrupt();
+        } else {
+            mEventQueue.offer(EVENT_EOF);
+        }
+    }
+
+    @Override
+    public void run() {
+        Integer event;
+        try {
+            while (true) {
+                event = mEventQueue.poll();
+                boolean shouldStop = false;
+                if (event != null) {
+                    switch (event) {
+                        case EVENT_START:
+                            onStart();
+                            break;
+                        case EVENT_EOF:
+                            if (mVideoTrackDecoder != null) {
+                                mVideoTrackDecoder.waitForFrameGrab();
+                            }
+                            // once the last frame has been grabbed, fall through and stop
+                        case EVENT_STOP:
+                            onStop(true);
+                            shouldStop = true;
+                            break;
+                    }
+                } else if (mStarted) {
+                    decode();
+                }
+                if (shouldStop) {
+                    break;
+                }
+
+            }
+        } catch (Exception e) {
+            mListener.onError(e);
+            onStop(false);
+        }
+    }
+
+    private void onStart() throws Exception {
+        if (mOpenGLEnabled) {
+            getRenderTarget().focus();
+        }
+
+        mMediaExtractor = new MediaExtractor();
+        mMediaExtractor.setDataSource(mContext, mUri, null);
+
+        mVideoTrackIndex = -1;
+        mAudioTrackIndex = -1;
+
+        for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
+            MediaFormat format = mMediaExtractor.getTrackFormat(i);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Uri " + mUri + ", track " + i + ": " + format);
+            }
+            if (DecoderUtil.isVideoFormat(format) && mVideoTrackIndex == -1) {
+                mVideoTrackIndex = i;
+            } else if (DecoderUtil.isAudioFormat(format) && mAudioTrackIndex == -1) {
+                mAudioTrackIndex = i;
+            }
+        }
+
+        if (mVideoTrackIndex == -1 && mAudioTrackIndex == -1) {
+            throw new IllegalArgumentException(
+                    "Couldn't find a video or audio track in the provided file");
+        }
+
+        if (mVideoTrackIndex != -1) {
+            MediaFormat videoFormat = mMediaExtractor.getTrackFormat(mVideoTrackIndex);
+            mVideoTrackDecoder = mOpenGLEnabled
+                    ? new GpuVideoTrackDecoder(mVideoTrackIndex, videoFormat, this)
+                    : new CpuVideoTrackDecoder(mVideoTrackIndex, videoFormat, this);
+            mVideoTrackDecoder.init();
+            mMediaExtractor.selectTrack(mVideoTrackIndex);
+            if (Build.VERSION.SDK_INT >= 17) {
+                retrieveDefaultRotation();
+            }
+        }
+
+        if (mAudioTrackIndex != -1) {
+            MediaFormat audioFormat = mMediaExtractor.getTrackFormat(mAudioTrackIndex);
+            mAudioTrackDecoder = new AudioTrackDecoder(mAudioTrackIndex, audioFormat, this);
+            mAudioTrackDecoder.init();
+            mMediaExtractor.selectTrack(mAudioTrackIndex);
+        }
+
+        if (mStartMicros > 0) {
+            mMediaExtractor.seekTo(mStartMicros, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
+        }
+
+        mStarted = true;
+        mListener.onDecodingStarted();
+    }
+
+    @TargetApi(17)
+    private void retrieveDefaultRotation() {
+        MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+        metadataRetriever.setDataSource(mContext, mUri);
+        String rotationString = metadataRetriever.extractMetadata(
+                MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+        mDefaultRotation = rotationString == null ? 0 : Integer.parseInt(rotationString);
+    }
+
+    private void onStop(boolean notifyListener) {
+        mMediaExtractor.release();
+        mMediaExtractor = null;
+
+        if (mVideoTrackDecoder != null) {
+            mVideoTrackDecoder.release();
+            mVideoTrackDecoder = null;
+        }
+
+        if (mAudioTrackDecoder != null) {
+            mAudioTrackDecoder.release();
+            mAudioTrackDecoder = null;
+        }
+
+        if (mOpenGLEnabled) {
+            if (mRenderTarget != null) {
+                getRenderTarget().release();
+            }
+            RenderTarget.focusNone();
+        }
+
+        mVideoTrackIndex = -1;
+        mAudioTrackIndex = -1;
+
+        mEventQueue.clear();
+        mStarted = false;
+        if (notifyListener) {
+            mListener.onDecodingStopped();
+        }
+    }
+
+    private void decode() {
+        int sampleTrackIndex = mMediaExtractor.getSampleTrackIndex();
+        if (sampleTrackIndex >= 0) {
+            if (sampleTrackIndex == mVideoTrackIndex) {
+                mVideoTrackDecoder.feedInput(mMediaExtractor);
+            } else if (sampleTrackIndex == mAudioTrackIndex) {
+                mAudioTrackDecoder.feedInput(mMediaExtractor);
+            }
+        } else if (!mSignaledEndOfInput) {
+            if (mVideoTrackDecoder != null) {
+                mVideoTrackDecoder.signalEndOfInput();
+            }
+            if (mAudioTrackDecoder != null) {
+                mAudioTrackDecoder.signalEndOfInput();
+            }
+            mSignaledEndOfInput = true;
+        }
+
+        if (mVideoTrackDecoder != null) {
+            mVideoTrackDecoder.drainOutputBuffer();
+        }
+        if (mAudioTrackDecoder != null) {
+            mAudioTrackDecoder.drainOutputBuffer();
+        }
+    }
+
+    /**
+     * Fills the argument frame with the video data, using the rotation hint obtained from the
+     * file's metadata, if any.
+     *
+     * @see #grabVideoFrame(FrameImage2D, int)
+     */
+    public void grabVideoFrame(FrameImage2D outputVideoFrame) {
+        grabVideoFrame(outputVideoFrame, mDefaultRotation);
+    }
+
+    /**
+     * Fills the argument frame with the video data, the frame will be returned with the given
+     * rotation applied.
+     *
+     * @param outputVideoFrame the output video frame.
+     * @param videoRotation the rotation angle that is applied to the raw decoded frame.
+     *   Value is one of {ROTATE_NONE, ROTATE_90_RIGHT, ROTATE_180, ROTATE_90_LEFT}.
+     */
+    public void grabVideoFrame(FrameImage2D outputVideoFrame, int videoRotation) {
+        if (mVideoTrackDecoder != null && outputVideoFrame != null) {
+            mVideoTrackDecoder.grabFrame(outputVideoFrame, videoRotation);
+        }
+    }
+
+    /**
+     * Fills the argument frame with the audio data.
+     *
+     * @param outputAudioFrame the output audio frame.
+     */
+    public void grabAudioSamples(FrameValue outputAudioFrame) {
+        if (mAudioTrackDecoder != null) {
+            if (outputAudioFrame != null) {
+                mAudioTrackDecoder.grabSample(outputAudioFrame);
+            } else {
+                mAudioTrackDecoder.clearBuffer();
+            }
+        }
+    }
+
+    /**
+     * Gets the duration, in nanoseconds.
+     */
+    public long getDuration() {
+        if (!mStarted) {
+            throw new IllegalStateException("MediaDecoder has not been started");
+        }
+
+        MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(
+                mVideoTrackIndex != -1 ? mVideoTrackIndex : mAudioTrackIndex);
+        return mediaFormat.getLong(MediaFormat.KEY_DURATION) * 1000;
+    }
+
+    private RenderTarget getRenderTarget() {
+        if (mRenderTarget == null) {
+            mRenderTarget = RenderTarget.newTarget(1, 1);
+        }
+        return mRenderTarget;
+    }
+
+    @Override
+    public void onDecodedOutputAvailable(TrackDecoder decoder) {
+        if (decoder == mVideoTrackDecoder) {
+            mListener.onVideoFrameAvailable();
+        } else if (decoder == mAudioTrackDecoder) {
+            mListener.onAudioSamplesAvailable();
+        }
+    }
+
+    @Override
+    public void onEndOfStream(TrackDecoder decoder) {
+        if (decoder == mAudioTrackDecoder) {
+            mSeenEndOfAudioOutput = true;
+        } else if (decoder == mVideoTrackDecoder) {
+            mSeenEndOfVideoOutput = true;
+        }
+
+        if ((mAudioTrackDecoder == null || mSeenEndOfAudioOutput)
+                && (mVideoTrackDecoder == null || mSeenEndOfVideoOutput)) {
+            stop(false);
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java
new file mode 100644
index 0000000..c81e8b4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java
@@ -0,0 +1,178 @@
+/*
+ * 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+@TargetApi(16)
+abstract class TrackDecoder {
+
+    interface Listener {
+        void onDecodedOutputAvailable(TrackDecoder decoder);
+
+        void onEndOfStream(TrackDecoder decoder);
+    }
+
+    private static final String LOG_TAG = "TrackDecoder";
+
+    private static final long TIMEOUT_US = 50; // Timeout for en-queueing and de-queueing buffers.
+
+    private static final int NO_INPUT_BUFFER = -1;
+
+    private final int mTrackIndex;
+    private final MediaFormat mMediaFormat;
+    private final Listener mListener;
+
+    private MediaCodec mMediaCodec;
+    private MediaFormat mOutputFormat;
+
+    private ByteBuffer[] mCodecInputBuffers;
+    private ByteBuffer[] mCodecOutputBuffers;
+
+    private boolean mShouldEnqueueEndOfStream;
+
+    /**
+     * @return a configured {@link MediaCodec}.
+     */
+    protected abstract MediaCodec initMediaCodec(MediaFormat format);
+
+    /**
+     * Called when decoded output is available. The implementer is responsible for releasing the
+     * assigned buffer.
+     *
+     * @return {@code true} if any further decoding should be attempted at the moment.
+     */
+    protected abstract boolean onDataAvailable(
+            MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info);
+
+    protected TrackDecoder(int trackIndex, MediaFormat mediaFormat, Listener listener) {
+        mTrackIndex = trackIndex;
+
+        if (mediaFormat == null) {
+            throw new NullPointerException("mediaFormat cannot be null");
+        }
+        mMediaFormat = mediaFormat;
+
+        if (listener == null) {
+            throw new NullPointerException("listener cannot be null");
+        }
+        mListener = listener;
+    }
+
+    public void init() {
+        mMediaCodec = initMediaCodec(mMediaFormat);
+        mMediaCodec.start();
+        mCodecInputBuffers = mMediaCodec.getInputBuffers();
+        mCodecOutputBuffers = mMediaCodec.getOutputBuffers();
+    }
+
+    public void signalEndOfInput() {
+        mShouldEnqueueEndOfStream = true;
+        tryEnqueueEndOfStream();
+    }
+
+    public void release() {
+        if (mMediaCodec != null) {
+            mMediaCodec.stop();
+            mMediaCodec.release();
+        }
+    }
+
+    protected MediaCodec getMediaCodec() {
+        return mMediaCodec;
+    }
+
+    protected void notifyListener() {
+        mListener.onDecodedOutputAvailable(this);
+    }
+
+    public boolean feedInput(MediaExtractor mediaExtractor) {
+        long presentationTimeUs = 0;
+
+        int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_US);
+        if (inputBufferIndex != NO_INPUT_BUFFER) {
+            ByteBuffer destinationBuffer = mCodecInputBuffers[inputBufferIndex];
+            int sampleSize = mediaExtractor.readSampleData(destinationBuffer, 0);
+            // We don't expect to get a sample without any data, so this should never happen.
+            if (sampleSize < 0) {
+                Log.w(LOG_TAG, "Media extractor had sample but no data.");
+
+                // Signal the end of the track immediately anyway, using the buffer.
+                mMediaCodec.queueInputBuffer(
+                        inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                return false;
+            }
+
+            presentationTimeUs = mediaExtractor.getSampleTime();
+            mMediaCodec.queueInputBuffer(
+                    inputBufferIndex,
+                    0,
+                    sampleSize,
+                    presentationTimeUs,
+                    0);
+
+            return mediaExtractor.advance()
+                    && mediaExtractor.getSampleTrackIndex() == mTrackIndex;
+        }
+        return false;
+    }
+
+    private void tryEnqueueEndOfStream() {
+        int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_US);
+        // We will always eventually have an input buffer, because we keep trying until the last
+        // decoded frame is output.
+        // The EoS does not need to be signaled if the application stops decoding.
+        if (inputBufferIndex != NO_INPUT_BUFFER) {
+            mMediaCodec.queueInputBuffer(
+                    inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+            mShouldEnqueueEndOfStream = false;
+        }
+    }
+
+    public boolean drainOutputBuffer() {
+        BufferInfo outputInfo = new BufferInfo();
+        int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(outputInfo, TIMEOUT_US);
+
+        if ((outputInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mListener.onEndOfStream(this);
+            return false;
+        }
+        if (mShouldEnqueueEndOfStream) {
+            tryEnqueueEndOfStream();
+        }
+        if (outputBufferIndex >= 0) {
+            return onDataAvailable(
+                    mMediaCodec, mCodecOutputBuffers, outputBufferIndex, outputInfo);
+        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+            mCodecOutputBuffers = mMediaCodec.getOutputBuffers();
+            return true;
+        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+            mOutputFormat = mMediaCodec.getOutputFormat();
+            Log.d(LOG_TAG, "Output format has changed to " + mOutputFormat);
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/VideoTrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/VideoTrackDecoder.java
new file mode 100644
index 0000000..06a4305
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/VideoTrackDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.decoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import androidx.media.filterfw.FrameImage2D;
+
+/**
+ * Base class for all {@link TrackDecoder} classes that decode video.
+ */
+@TargetApi(16)
+public abstract class VideoTrackDecoder extends TrackDecoder {
+
+    private static final String LOG_TAG = "VideoTrackDecoder";
+
+    protected final Object mFrameMonitor = new Object();
+    protected volatile boolean mFrameAvailable; // Access guarded by mFrameMonitor.
+
+    protected VideoTrackDecoder(int trackIndex, MediaFormat format, Listener listener) {
+        super(trackIndex, format, listener);
+        if (!DecoderUtil.isVideoFormat(format)) {
+            throw new IllegalArgumentException(
+                    "VideoTrackDecoder can only be used with video formats");
+        }
+    }
+
+    public void grabFrame(FrameImage2D outputVideoFrame, int rotation) {
+        synchronized (mFrameMonitor) {
+            if (!mFrameAvailable) {
+                Log.w(LOG_TAG, "frame is not ready - the caller has to wait for a corresponding " +
+                        "onDecodedFrameAvailable() call");
+                return;
+            }
+
+            copyFrameDataTo(outputVideoFrame, rotation);
+
+            mFrameAvailable = false;
+            mFrameMonitor.notifyAll();
+        }
+    }
+
+
+    /**
+     * Waits for the frame to be picked up by the MFF thread, i.e. blocks until the
+     * {@link #grabFrame(FrameImage2D, int)}) method is called.
+     */
+    public boolean waitForFrameGrab() {
+        synchronized (mFrameMonitor) {
+            try {
+                while (mFrameAvailable) {
+                    mFrameMonitor.wait();
+                }
+                return true;
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+    }
+
+    protected final void markFrameAvailable() {
+        synchronized (mFrameMonitor) {
+            mFrameAvailable = true;
+            mFrameMonitor.notifyAll();
+        }
+    }
+
+    /**
+     * @return if the frame dimension needs to be swapped,
+     *   i.e. (width,height) becomes (height, width)
+     */
+    protected static boolean needSwapDimension(int rotation) {
+        switch(rotation) {
+            case MediaDecoder.ROTATE_90_RIGHT:
+            case MediaDecoder.ROTATE_90_LEFT:
+                return true;
+            case MediaDecoder.ROTATE_NONE:
+            case MediaDecoder.ROTATE_180:
+                return false;
+            default:
+                throw new IllegalArgumentException("Unsupported rotation angle.");
+        }
+    }
+
+    /**
+     * Subclasses must implement this to copy the video frame data to an MFF frame.
+     *
+     * @param outputVideoFrame The destination frame
+     * @param rotation The desired rotation of the frame
+     */
+    protected abstract void copyFrameDataTo(FrameImage2D outputVideoFrame, int rotation);
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/geometry/Quad.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/geometry/Quad.java
new file mode 100644
index 0000000..4035f7f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/geometry/Quad.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2011 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 androidx.media.filterfw.geometry;
+
+import android.annotation.SuppressLint;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+/**
+ * The Quad class specifies a (possibly affine transformed) rectangle.
+ *
+ * A Quad instance holds 4 points that define its shape. The points may represent any rectangle that
+ * has been transformed by an affine transformation. This means that Quads can represent translated,
+ * scaled, rotated and sheared/skewed rectangles. As such, Quads are restricted to the set of
+ * parallelograms.
+ *
+ * Each point in the Quad represents a specific corner of the Quad. These are top-left, top-right,
+ * bottom-left, and bottom-right. These labels allow mapping a transformed Quad back to an up-right
+ * Quad, with the point-to-point mapping well-defined. They do not necessarily indicate that e.g.
+ * the top-left corner is actually at the top-left of coordinate space.
+ */
+@SuppressLint("FloatMath")
+public class Quad {
+
+    private final PointF mTopLeft;
+    private final PointF mTopRight;
+    private final PointF mBottomLeft;
+    private final PointF mBottomRight;
+
+    /**
+     * Returns the unit Quad.
+     * The unit Quad has its top-left point at (0, 0) and bottom-right point at (1, 1).
+     * @return the unit Quad.
+     */
+    public static Quad unitQuad() {
+        return new Quad(0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f);
+    }
+
+    /**
+     * Return a Quad from the specified rectangle.
+     *
+     * @param rect a RectF instance.
+     * @return Quad that represents the passed rectangle.
+     */
+    public static Quad fromRect(RectF rect) {
+        return new Quad(new PointF(rect.left, rect.top),
+                        new PointF(rect.right, rect.top),
+                        new PointF(rect.left, rect.bottom),
+                        new PointF(rect.right, rect.bottom));
+    }
+
+    /**
+     * Return a Quad from the specified rectangle coordinates.
+     *
+     * @param x the top left x coordinate
+     * @param y the top left y coordinate
+     * @param width the width of the rectangle
+     * @param height the height of the rectangle
+     * @return Quad that represents the passed rectangle.
+     */
+    public static Quad fromRect(float x, float y, float width, float height) {
+        return new Quad(new PointF(x, y),
+                        new PointF(x + width, y),
+                        new PointF(x, y + height),
+                        new PointF(x + width, y + height));
+    }
+
+    /**
+     * Return a Quad that spans the specified points and height.
+     *
+     * The returned Quad has the specified top-left and top-right points, and the specified height
+     * while maintaining 90 degree angles on all 4 corners.
+     *
+     * @param topLeft the top-left of the quad
+     * @param topRight the top-right of the quad
+     * @param height the height of the quad
+     * @return Quad that spans the specified points and height.
+     */
+    public static Quad fromLineAndHeight(PointF topLeft, PointF topRight, float height) {
+        PointF dp = new PointF(topRight.x - topLeft.x, topRight.y - topLeft.y);
+        float len = dp.length();
+        PointF np = new PointF(height * (dp.y / len), height * (dp.x / len));
+        PointF p2 = new PointF(topLeft.x - np.x, topLeft.y + np.y);
+        PointF p3 = new PointF(topRight.x - np.x, topRight.y + np.y);
+        return new Quad(topLeft, topRight, p2, p3);
+    }
+
+    /**
+     * Return a Quad that represents the specified rotated rectangle.
+     *
+     * The Quad is rotated counter-clockwise around its centroid.
+     *
+     * @param rect the source rectangle
+     * @param angle the angle to rotate the source rectangle in radians
+     * @return the Quad representing the source rectangle rotated by the given angle.
+     */
+    public static Quad fromRotatedRect(RectF rect, float angle) {
+        return Quad.fromRect(rect).rotated(angle);
+    }
+
+    /**
+     * Return a Quad that represents the specified transformed rectangle.
+     *
+     * The transform is applied by multiplying each point (x, y, 1) by the matrix.
+     *
+     * @param rect the source rectangle
+     * @param matrix the transformation matrix
+     * @return the Quad representing the source rectangle transformed by the matrix
+     */
+    public static Quad fromTransformedRect(RectF rect, Matrix matrix) {
+        return Quad.fromRect(rect).transformed(matrix);
+    }
+
+    /**
+     * Returns the transformation matrix to transform the source Quad to the target Quad.
+     *
+     * @param source the source quad
+     * @param target the target quad
+     * @return the transformation matrix to map source to target.
+     */
+    public static Matrix getTransform(Quad source, Quad target) {
+        // We only use the first 3 points as they sufficiently specify the transform
+        Matrix transform = new Matrix();
+        transform.setPolyToPoly(source.asCoords(), 0, target.asCoords(), 0, 3);
+        return transform;
+    }
+
+    /**
+     * The top-left point of the Quad.
+     * @return top-left point of the Quad.
+     */
+    public PointF topLeft() {
+        return mTopLeft;
+    }
+
+    /**
+     * The top-right point of the Quad.
+     * @return top-right point of the Quad.
+     */
+    public PointF topRight() {
+        return mTopRight;
+    }
+
+    /**
+     * The bottom-left point of the Quad.
+     * @return bottom-left point of the Quad.
+     */
+    public PointF bottomLeft() {
+        return mBottomLeft;
+    }
+
+    /**
+     * The bottom-right point of the Quad.
+     * @return bottom-right point of the Quad.
+     */
+    public PointF bottomRight() {
+        return mBottomRight;
+    }
+
+    /**
+     * Rotate the quad by the given angle.
+     *
+     * The Quad is rotated counter-clockwise around its centroid.
+     *
+     * @param angle the angle to rotate in radians
+     * @return the rotated Quad
+     */
+    public Quad rotated(float angle) {
+        PointF center = center();
+        float cosa = (float) Math.cos(angle);
+        float sina = (float) Math.sin(angle);
+
+        PointF topLeft = rotatePoint(topLeft(), center, cosa, sina);
+        PointF topRight = rotatePoint(topRight(), center, cosa, sina);
+        PointF bottomLeft = rotatePoint(bottomLeft(), center, cosa, sina);
+        PointF bottomRight = rotatePoint(bottomRight(), center, cosa, sina);
+
+        return new Quad(topLeft, topRight, bottomLeft, bottomRight);
+    }
+
+    /**
+     * Transform the quad with the given transformation matrix.
+     *
+     * The transform is applied by multiplying each point (x, y, 1) by the matrix.
+     *
+     * @param matrix the transformation matrix
+     * @return the transformed Quad
+     */
+    public Quad transformed(Matrix matrix) {
+        float[] points = asCoords();
+        matrix.mapPoints(points);
+        return new Quad(points);
+    }
+
+    /**
+     * Returns the centroid of the Quad.
+     *
+     * The centroid of the Quad is where the two inner diagonals connecting the opposite corners
+     * meet.
+     *
+     * @return the centroid of the Quad.
+     */
+    public PointF center() {
+        // As the diagonals bisect each other, we can simply return the center of one of the
+        // diagonals.
+        return new PointF((mTopLeft.x + mBottomRight.x) / 2f,
+                          (mTopLeft.y + mBottomRight.y) / 2f);
+    }
+
+    /**
+     * Returns the quad as a float-array of coordinates.
+     * The order of coordinates is top-left, top-right, bottom-left, bottom-right. This is the
+     * default order of coordinates used in ImageShaders, so this method can be used to bind
+     * an attribute to the Quad.
+     */
+    public float[] asCoords() {
+        return new float[] { mTopLeft.x, mTopLeft.y,
+                             mTopRight.x, mTopRight.y,
+                             mBottomLeft.x, mBottomLeft.y,
+                             mBottomRight.x, mBottomRight.y };
+    }
+
+    /**
+     * Grow the Quad outwards by the specified factor.
+     *
+     * This method moves the corner points of the Quad outward along the diagonals that connect
+     * them to the centroid. A factor of 1.0 moves the quad outwards by the distance of the corners
+     * to the centroid.
+     *
+     * @param factor the growth factor
+     * @return the Quad grown by the specified amount
+     */
+    public Quad grow(float factor) {
+        PointF pc = center();
+        return new Quad(factor * (mTopLeft.x - pc.x) + pc.x,
+                        factor * (mTopLeft.y - pc.y) + pc.y,
+                        factor * (mTopRight.x - pc.x) + pc.x,
+                        factor * (mTopRight.y - pc.y) + pc.y,
+                        factor * (mBottomLeft.x - pc.x) + pc.x,
+                        factor * (mBottomLeft.y - pc.y) + pc.y,
+                        factor * (mBottomRight.x - pc.x) + pc.x,
+                        factor * (mBottomRight.y - pc.y) + pc.y);
+    }
+
+    /**
+     * Scale the Quad by the specified factor.
+     *
+     * @param factor the scaling factor
+     * @return the Quad instance scaled by the specified factor.
+     */
+    public Quad scale(float factor) {
+        return new Quad(mTopLeft.x * factor, mTopLeft.y * factor,
+                        mTopRight.x * factor, mTopRight.y * factor,
+                        mBottomLeft.x * factor, mBottomLeft.y * factor,
+                        mBottomRight.x * factor, mBottomRight.y * factor);
+    }
+
+    /**
+     * Scale the Quad by the specified factors in the x and y factors.
+     *
+     * @param sx the x scaling factor
+     * @param sy the y scaling factor
+     * @return the Quad instance scaled by the specified factors.
+     */
+    public Quad scale2(float sx, float sy) {
+        return new Quad(mTopLeft.x * sx, mTopLeft.y * sy,
+                        mTopRight.x * sx, mTopRight.y * sy,
+                        mBottomLeft.x * sx, mBottomLeft.y * sy,
+                        mBottomRight.x * sx, mBottomRight.y * sy);
+    }
+
+    /**
+     * Returns the Quad's left-to-right edge.
+     *
+     * Returns a vector that goes from the Quad's top-left to top-right (or bottom-left to
+     * bottom-right).
+     *
+     * @return the edge vector as a PointF.
+     */
+    public PointF xEdge() {
+        return new PointF(mTopRight.x - mTopLeft.x, mTopRight.y - mTopLeft.y);
+    }
+
+    /**
+     * Returns the Quad's top-to-bottom edge.
+     *
+     * Returns a vector that goes from the Quad's top-left to bottom-left (or top-right to
+     * bottom-right).
+     *
+     * @return the edge vector as a PointF.
+     */
+    public PointF yEdge() {
+        return new PointF(mBottomLeft.x - mTopLeft.x, mBottomLeft.y - mTopLeft.y);
+    }
+
+    @Override
+    public String toString() {
+        return "Quad(" + mTopLeft.x + ", " + mTopLeft.y + ", "
+                       + mTopRight.x + ", " + mTopRight.y + ", "
+                       + mBottomLeft.x + ", " + mBottomLeft.y + ", "
+                       + mBottomRight.x + ", " + mBottomRight.y + ")";
+    }
+
+    private Quad(PointF topLeft, PointF topRight, PointF bottomLeft, PointF bottomRight) {
+        mTopLeft = topLeft;
+        mTopRight = topRight;
+        mBottomLeft = bottomLeft;
+        mBottomRight = bottomRight;
+    }
+
+    private Quad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
+        mTopLeft = new PointF(x0, y0);
+        mTopRight = new PointF(x1, y1);
+        mBottomLeft = new PointF(x2, y2);
+        mBottomRight = new PointF(x3, y3);
+    }
+
+    private Quad(float[] points) {
+        mTopLeft = new PointF(points[0], points[1]);
+        mTopRight = new PointF(points[2], points[3]);
+        mBottomLeft = new PointF(points[4], points[5]);
+        mBottomRight = new PointF(points[6], points[7]);
+    }
+
+    private static PointF rotatePoint(PointF p, PointF c, float cosa, float sina) {
+        float x = (p.x - c.x) * cosa - (p.y - c.y) * sina + c.x;
+        float y = (p.x - c.x) * sina + (p.y - c.y) * cosa + c.y;
+        return new PointF(x,y);
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AverageFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AverageFilter.java
new file mode 100644
index 0000000..d873e0a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AverageFilter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013 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.
+ */
+// Takes sharpness scores in RT and averages them over time
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public class AverageFilter extends Filter {
+
+    private static final String TAG = "AverageFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int NUM_FRAMES = 5;
+    private int counter = 0;
+    private float[] temp = new float[NUM_FRAMES];
+
+    /**
+     * @param context
+     * @param name
+     */
+    public AverageFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType floatT = FrameType.single(float.class);
+        return new Signature()
+        .addInputPort("sharpness", Signature.PORT_REQUIRED, floatT)
+        .addOutputPort("avg", Signature.PORT_REQUIRED, floatT)
+        .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameValue inFrameValue = getConnectedInputPort("sharpness").pullFrame().asFrameValue();
+        if (counter < NUM_FRAMES && counter >= 0) {
+            temp[counter] = ((Float)inFrameValue.getValue()).floatValue();
+        }
+
+        counter = (counter + 1) % NUM_FRAMES;
+
+        float output = (temp[0] + temp[1] + temp[2] + temp[3] + temp[4]) / NUM_FRAMES;
+        if (mLogVerbose) Log.v(TAG, "Avg= " + output + "temp1= " + temp[0] + "temp2= " +
+                temp[1] + "temp3= " + temp[2] + "temp4=" + temp[3] + "temp5=" + temp[4]);
+
+        OutputPort outPort = getConnectedOutputPort("avg");
+        FrameValue outFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        outFrame.setValue(output);
+        outPort.pushFrame(outFrame);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilter.java
new file mode 100644
index 0000000..88cd44a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+
+public class AvgBrightnessFilter extends Filter {
+
+    private static final String TAG = "AvgBrightnessFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    public AvgBrightnessFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU);
+        FrameType floatT = FrameType.single(float.class);
+        return new Signature().addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+                .addOutputPort("brightnessRating", Signature.PORT_OPTIONAL, floatT)
+                .disallowOtherPorts();
+
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+
+        float brightness;
+        ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
+
+        brightness = brightnessOperator(inputImage.getWidth(),inputImage.getHeight(), inputBuffer);
+
+        inputImage.unlock();
+
+        if (mLogVerbose) Log.v(TAG, "contrastRatio: " + brightness);
+
+        OutputPort brightnessPort = getConnectedOutputPort("brightnessRating");
+        FrameValue brightnessOutFrame = brightnessPort.fetchAvailableFrame(null).asFrameValue();
+        brightnessOutFrame.setValue(brightness);
+        brightnessPort.pushFrame(brightnessOutFrame);
+    }
+
+    private static native float brightnessOperator(int width, int height, ByteBuffer imageBuffer);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CSVWriterFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CSVWriterFilter.java
new file mode 100644
index 0000000..ca16c27
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CSVWriterFilter.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+
+public class CSVWriterFilter extends Filter {
+
+    private static final String TAG = "CSVWriterFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+    private boolean mFirstTime = true;
+    private final static int NUM_FRAMES = 3;
+    private final String mFileName = "/CSVFile.csv";
+
+    public CSVWriterFilter(MffContext context, String name) {
+
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType floatT = FrameType.single(float.class);
+        FrameType stringT = FrameType.single(String.class);
+        FrameType floatArrayT = FrameType.array(float.class);
+
+        return new Signature()
+                .addInputPort("sharpness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("overExposure", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("underExposure", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("colorfulness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("contrastRating", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("brightness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("motionValues", Signature.PORT_REQUIRED, floatArrayT)
+                .addInputPort("imageFileName", Signature.PORT_REQUIRED, stringT)
+                .addInputPort("csvFilePath", Signature.PORT_REQUIRED, stringT)
+                .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+
+
+
+        Log.v(TAG,"in csv writer on process");
+        FrameValue sharpnessValue =
+                getConnectedInputPort("sharpness").pullFrame().asFrameValue();
+        float sharpness = ((Float)sharpnessValue.getValue()).floatValue();
+
+        FrameValue overExposureValue =
+                getConnectedInputPort("overExposure").pullFrame().asFrameValue();
+        float overExposure = ((Float)overExposureValue.getValue()).floatValue();
+
+        FrameValue underExposureValue =
+                getConnectedInputPort("underExposure").pullFrame().asFrameValue();
+        float underExposure = ((Float)underExposureValue.getValue()).floatValue();
+
+        FrameValue colorfulnessValue =
+                getConnectedInputPort("colorfulness").pullFrame().asFrameValue();
+        float colorfulness = ((Float)colorfulnessValue.getValue()).floatValue();
+
+        FrameValue contrastValue =
+                getConnectedInputPort("contrastRating").pullFrame().asFrameValue();
+        float contrast = ((Float)contrastValue.getValue()).floatValue();
+
+        FrameValue brightnessValue =
+                getConnectedInputPort("brightness").pullFrame().asFrameValue();
+        float brightness = ((Float)brightnessValue.getValue()).floatValue();
+
+        FrameValue motionValuesFrameValue =
+                getConnectedInputPort("motionValues").pullFrame().asFrameValue();
+        float[] motionValues = (float[]) motionValuesFrameValue.getValue();
+        float vectorAccel = (float) Math.sqrt(Math.pow(motionValues[0], 2) +
+                Math.pow(motionValues[1], 2) + Math.pow(motionValues[2], 2));
+
+        FrameValue imageFileNameFrameValue =
+                getConnectedInputPort("imageFileName").pullFrame().asFrameValue();
+        String imageFileName = ((String)imageFileNameFrameValue.getValue());
+
+        FrameValue csvFilePathFrameValue =
+                getConnectedInputPort("csvFilePath").pullFrame().asFrameValue();
+        String csvFilePath = ((String)csvFilePathFrameValue.getValue());
+
+
+        if(mFirstTime) {
+            try {
+                FileWriter fileWriter = new FileWriter(csvFilePath + "/CSVFile.csv");
+                BufferedWriter csvWriter = new BufferedWriter(fileWriter);
+
+                csvWriter.write("FileName,Sharpness,OverExposure,UnderExposure,Colorfulness," +
+                            "ContrastRating,Brightness,Motion");
+                csvWriter.newLine();
+                csvWriter.close();
+            } catch (IOException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+            mFirstTime = false;
+        }
+
+        try {
+            Log.v(TAG,"about to write to file");
+            FileWriter fileWriter = new FileWriter(csvFilePath + mFileName, true);
+            BufferedWriter csvWriter = new BufferedWriter(fileWriter);
+
+            csvWriter.write(imageFileName + "," + sharpness + "," + overExposure + "," +
+                    underExposure + "," + colorfulness + "," + contrast + "," + brightness +
+                    "," + vectorAccel);
+            Log.v(TAG, "" + imageFileName + "," + sharpness + "," + overExposure + "," +
+                    underExposure + "," + colorfulness + "," + contrast + "," + brightness +
+                    "," + vectorAccel);
+            csvWriter.newLine();
+            csvWriter.close();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
new file mode 100644
index 0000000..fa0f995
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.os.Handler;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicYuvToRGB;
+import android.renderscript.Type;
+import android.util.Log;
+import android.view.Surface;
+import com.android.ex.camera2.blocking.BlockingCameraManager;
+import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Camera2Source extends Filter implements Allocation.OnBufferAvailableListener {
+
+    private boolean mNewFrameAvailable = false;
+    private FrameType mOutputType;
+    private static final String TAG = "Camera2Source";
+    private CameraManager mCameraManager;
+    private CameraDevice mCamera;
+    private RenderScript mRS;
+    private Surface mSurface;
+    private CameraCharacteristics mProperties;
+    private CameraTestThread mLooperThread;
+    private int mHeight = 480;
+    private int mWidth = 640;
+    private Allocation mAllocationIn;
+    private ScriptIntrinsicYuvToRGB rgbConverter;
+    private Allocation mAllocationOut;
+    private Bitmap mBitmap;
+
+    class MyCameraListener extends CameraManager.AvailabilityListener {
+
+        @Override
+        public void onCameraAvailable(String cameraId) {
+            // TODO Auto-generated method stub
+            Log.v(TAG, "camera available to open");
+        }
+
+        @Override
+        public void onCameraUnavailable(String cameraId) {
+            // TODO Auto-generated method stub
+            Log.v(TAG, "camera unavailable to open");
+        }
+
+    }
+
+    class MyCaptureListener extends CameraDevice.CaptureListener {
+
+        @Override
+        public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
+                CaptureResult result) {
+            // TODO Auto-generated method stub
+            Log.v(TAG, "in onCaptureComplete");
+
+        }
+
+        @Override
+        public void onCaptureFailed(CameraDevice camera, CaptureRequest request,
+                CaptureFailure failure) {
+            // TODO Auto-generated method stub
+            Log.v(TAG, "onCaptureFailed is being called");
+        }
+
+    }
+
+    public Camera2Source(MffContext context, String name) {
+        super(context, name);
+        mOutputType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+
+        Context ctx = context.getApplicationContext();
+        mCameraManager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
+
+        mRS = RenderScript.create(context.getApplicationContext());
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+                .addOutputPort("timestamp", Signature.PORT_OPTIONAL, FrameType.single(long.class))
+                .addOutputPort("video", Signature.PORT_REQUIRED, mOutputType)
+                .addOutputPort("orientation", Signature.PORT_REQUIRED,
+                        FrameType.single(float.class))
+                .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onClose() {
+        Log.v(TAG, "onClose being called");
+        try {
+            mCamera.close();
+            mSurface.release();
+            mLooperThread.close();
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onOpen() {
+        mLooperThread = new CameraTestThread();
+        Handler mHandler;
+        try {
+            mHandler = mLooperThread.start();
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        try {
+            String backCameraId = "0";
+            BlockingCameraManager blkManager = new BlockingCameraManager(mCameraManager);
+            mCamera = blkManager.openCamera(backCameraId, /*listener*/null, mHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        } catch (BlockingOpenException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        Element ele = Element.createPixel(mRS, Element.DataType.UNSIGNED_8,
+                Element.DataKind.PIXEL_YUV);
+
+        rgbConverter = ScriptIntrinsicYuvToRGB.create(mRS,ele);
+        Type.Builder yuvBuilder = new Type.Builder(mRS,ele);
+
+        yuvBuilder.setYuvFormat(ImageFormat.YUV_420_888);
+        yuvBuilder.setX(mWidth);
+        yuvBuilder.setY(mHeight);
+        mAllocationIn = Allocation.createTyped(mRS, yuvBuilder.create(),
+                Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT);
+        mSurface = mAllocationIn.getSurface();
+        mAllocationIn.setOnBufferAvailableListener(this);
+        rgbConverter.setInput(mAllocationIn);
+
+        mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
+        mAllocationOut = Allocation.createFromBitmap(mRS, mBitmap);
+
+
+        Log.v(TAG, "mcamera: " + mCamera);
+
+        List<Surface> surfaces = new ArrayList<Surface>();
+        surfaces.add(mSurface);
+        CaptureRequest.Builder mCaptureRequest = null;
+        try {
+            mCamera.configureOutputs(surfaces);
+            mCaptureRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            mCaptureRequest.addTarget(mSurface);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        try {
+            mCamera.setRepeatingRequest(mCaptureRequest.build(), new MyCaptureListener(),
+                    mHandler);
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+        mProperties = null;
+        try {
+            mProperties = mCameraManager.getCameraCharacteristics(mCamera.getId());
+        } catch (CameraAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    @Override
+    protected void onProcess() {
+        Log.v(TAG, "on Process");
+        if (nextFrame()) {
+            OutputPort outPort = getConnectedOutputPort("video");
+
+            // Create a 2D frame that will hold the output
+            int[] dims = new int[] {
+                    mWidth, mHeight
+            };
+            FrameImage2D outputFrame = Frame.create(mOutputType, dims).asFrameImage2D();
+            rgbConverter.forEach(mAllocationOut);
+            mAllocationOut.copyTo(mBitmap);
+            outputFrame.setBitmap(mBitmap);
+            outPort.pushFrame(outputFrame);
+            outputFrame.release();
+
+            OutputPort orientationPort = getConnectedOutputPort("orientation");
+            FrameValue orientationFrame = orientationPort.fetchAvailableFrame(null).asFrameValue();
+
+            // FIXME: Hardcoded value because ORIENTATION returns null, Qualcomm
+            // bug
+            Integer orientation = mProperties.get(CameraCharacteristics.SENSOR_ORIENTATION);
+            float temp;
+            if (orientation != null) {
+                temp = orientation.floatValue();
+            } else {
+                temp = 90.0f;
+            }
+            orientationFrame.setValue(temp);
+            orientationPort.pushFrame(orientationFrame);
+        }
+    }
+
+    private synchronized boolean nextFrame() {
+        boolean frameAvailable = mNewFrameAvailable;
+        if (frameAvailable) {
+            mNewFrameAvailable = false;
+        } else {
+            enterSleepState();
+        }
+        return frameAvailable;
+    }
+
+    public void onBufferAvailable(Allocation a) {
+        Log.v(TAG, "on Buffer Available");
+        a.ioReceive();
+        synchronized (this) {
+            mNewFrameAvailable = true;
+        }
+        wakeUp();
+    }
+
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CameraTestThread.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CameraTestThread.java
new file mode 100644
index 0000000..8a0fced
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/CameraTestThread.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Camera test thread wrapper for handling camera callbacks
+ */
+public class CameraTestThread implements AutoCloseable {
+    private static final String TAG = "CameraTestThread";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    // Timeout for initializing looper and opening camera in Milliseconds.
+    private static final long WAIT_FOR_COMMAND_TO_COMPLETE = 5000;
+    private Looper mLooper = null;
+    private Handler mHandler = null;
+
+    /**
+     * Create and start a looper thread, return the Handler
+     */
+    public synchronized Handler start() throws Exception {
+        final ConditionVariable startDone = new ConditionVariable();
+        if (mLooper != null || mHandler !=null) {
+            Log.w(TAG, "Looper thread already started");
+            return mHandler;
+        }
+
+        new Thread() {
+            @Override
+            public void run() {
+                if (VERBOSE) Log.v(TAG, "start loopRun");
+                Looper.prepare();
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+                mHandler = new Handler();
+                startDone.open();
+                Looper.loop();
+                if (VERBOSE) Log.v(TAG, "createLooperThread: finished");
+            }
+        }.start();
+
+        if (VERBOSE) Log.v(TAG, "start waiting for looper");
+        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
+            throw new TimeoutException("createLooperThread: start timeout");
+        }
+        return mHandler;
+    }
+
+    /**
+     * Terminate the looper thread
+     */
+    public synchronized void close() throws Exception {
+        if (mLooper == null || mHandler == null) {
+            Log.w(TAG, "Looper thread doesn't start yet");
+            return;
+        }
+
+        if (VERBOSE) Log.v(TAG, "Terminate looper thread");
+        mLooper.quit();
+        mLooper.getThread().join();
+        mLooper = null;
+        mHandler = null;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilter.java
new file mode 100644
index 0000000..d918437
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+
+public class ContrastRatioFilter extends Filter {
+
+    private static final String TAG = "ContrastRatioFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    public ContrastRatioFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU);
+        FrameType floatT = FrameType.single(float.class);
+        return new Signature().addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+                .addOutputPort("contrastRatingToGoodness", Signature.PORT_REQUIRED, floatT)
+                .disallowOtherPorts();
+
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+
+        float contrastRatio;
+        ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
+
+        contrastRatio = contrastOperator(inputImage.getWidth(), inputImage.getHeight(),
+                    inputBuffer);
+
+        inputImage.unlock();
+
+        if (mLogVerbose) Log.v(TAG, "contrastRatio: " + contrastRatio);
+
+        OutputPort contrastToGoodnessPort = getConnectedOutputPort("contrastRatingToGoodness");
+        FrameValue contrastOutFrame2 =
+                contrastToGoodnessPort.fetchAvailableFrame(null).asFrameValue();
+        contrastOutFrame2.setValue(contrastRatio);
+        contrastToGoodnessPort.pushFrame(contrastOutFrame2);
+
+
+    }
+
+    private static native float contrastOperator(int width, int height, ByteBuffer imageBuffer);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ExposureFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ExposureFilter.java
new file mode 100644
index 0000000..6128718
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ExposureFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+
+public class ExposureFilter extends Filter {
+
+    private FrameType mImageType;
+    private static final String TAG = "ExposureFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+    private static int OVER_EXPOSURE_TOLERANCE = 5;
+
+    public ExposureFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType floatT = FrameType.single(float.class);
+        return new Signature().addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+                .addOutputPort("overExposedNum", Signature.PORT_OPTIONAL, floatT)
+                .addOutputPort("overExposureRating", Signature.PORT_REQUIRED, floatT)
+                .addOutputPort("underExposedNum", Signature.PORT_OPTIONAL, floatT)
+                .addOutputPort("underExposureRating", Signature.PORT_REQUIRED, floatT)
+                .disallowOtherPorts();
+
+    }
+
+    @Override
+    protected void onProcess() {
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+
+        float overExposedPixels, underExposedPixels;
+        ByteBuffer inputBuffer = inputImage.lockBytes(Frame.MODE_READ);
+
+        overExposedPixels = overExposureOperator(inputImage.getWidth(),
+                                                 inputImage.getHeight(),
+                                                 inputBuffer);
+        underExposedPixels = underExposureOperator(inputImage.getWidth(),
+                                                   inputImage.getHeight(),
+                                                   inputBuffer);
+        inputImage.unlock();
+
+
+        if (mLogVerbose) Log.v(TAG, "underExposedPixelCount: " + underExposedPixels);
+
+        OutputPort underPort = getConnectedOutputPort("underExposedNum");
+        if (underPort != null) {
+            FrameValue underOutFrame = underPort.fetchAvailableFrame(null).asFrameValue();
+            underOutFrame.setValue(underExposedPixels*inputImage.getWidth()*inputImage.getHeight());
+            underPort.pushFrame(underOutFrame);
+        }
+
+
+        OutputPort underPort2 = getConnectedOutputPort("underExposureRating");
+        FrameValue underOutFrame2 = underPort2.fetchAvailableFrame(null).asFrameValue();
+        underOutFrame2.setValue(underExposedPixels);
+        underPort2.pushFrame(underOutFrame2);
+
+        if (mLogVerbose) Log.v(TAG, "overExposedPixelCount: " + overExposedPixels);
+
+        OutputPort overPort = getConnectedOutputPort("overExposedNum");
+        if (overPort != null) {
+            FrameValue overOutFrame = overPort.fetchAvailableFrame(null).asFrameValue();
+            overOutFrame.setValue(overExposedPixels*inputImage.getWidth()*inputImage.getHeight());
+            overPort.pushFrame(overOutFrame);
+        }
+
+
+        OutputPort overPort2 = getConnectedOutputPort("overExposureRating");
+        FrameValue overOutFrame2 = overPort2.fetchAvailableFrame(null).asFrameValue();
+        overOutFrame2.setValue(overExposedPixels);
+        overPort2.pushFrame(overOutFrame2);
+
+    }
+
+    private static native float overExposureOperator(int width, int height,
+            ByteBuffer imageBuffer);
+    private static native float underExposureOperator(int width, int height,
+            ByteBuffer imageBuffer);
+
+    static {
+        System.loadLibrary("smartcamera_jni");
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilter.java
new file mode 100644
index 0000000..c4a39e8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2013 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.
+ */
+// Takes in an array, returns the size of the array
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.hardware.Camera.Face;
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.Frame;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValues;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.nio.ByteBuffer;
+
+public class FaceSquareFilter extends Filter {
+
+    private static final String TAG = "FaceSquareFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static int FACE_X_RANGE = 2000;
+    private static int WIDTH_OFFSET = 1000;
+    private static int HEIGHT_OFFSET = 1000;
+
+    public FaceSquareFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageType = FrameType.buffer2D(FrameType.ELEMENT_RGBA8888);
+        FrameType facesType = FrameType.array(Camera.Face.class);
+        return new Signature()
+                .addInputPort("image", Signature.PORT_REQUIRED, imageType)
+                .addInputPort("faces", Signature.PORT_REQUIRED, facesType)
+                .addOutputPort("image", Signature.PORT_REQUIRED, imageType)
+                .disallowOtherPorts();
+    }
+
+    /**
+     * @see androidx.media.filterfw.Filter#onProcess()
+     */
+    @Override
+    protected void onProcess() {
+        // Get inputs
+        FrameImage2D imageFrame = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+        FrameValues facesFrame = getConnectedInputPort("faces").pullFrame().asFrameValues();
+        Face[] faces = (Face[]) facesFrame.getValues();
+        int[] dims = imageFrame.getDimensions();
+        ByteBuffer buffer = imageFrame.lockBytes(Frame.MODE_WRITE);
+        byte[] pixels = buffer.array();
+
+        // For every face in faces, draw a white rect around the
+        // face following the rect member of the Face
+        drawBoxes(pixels, faces, dims);
+
+        imageFrame.unlock();
+
+        OutputPort outPort = getConnectedOutputPort("image");
+        outPort.pushFrame(imageFrame);
+    }
+
+    public void drawBoxes(byte[] pixels, Face[] faces, int[] dims) {
+        for(int i = 0; i < faces.length; i++) {
+            Rect tempRect = faces[i].rect;
+            int top = (tempRect.top+HEIGHT_OFFSET)*dims[1]/FACE_X_RANGE;
+            int bottom = (tempRect.bottom+HEIGHT_OFFSET)*dims[1]/FACE_X_RANGE;
+            int left = (tempRect.left+WIDTH_OFFSET)*dims[0]/FACE_X_RANGE;
+            int right = (tempRect.right+WIDTH_OFFSET)*dims[0]/FACE_X_RANGE;
+
+            if (top < 0) {
+                top = 0;
+            } else if (top > dims[1]) {
+                top = dims[1];
+            }
+            if (left < 0) {
+                left = 0;
+            } else if (left > dims[0]) {
+                left = dims[0];
+            }
+            if (bottom > dims[1]) {
+                bottom = dims[1];
+            } else if (bottom < 0) {
+                bottom = 0;
+            }
+            if (right > dims[0]) {
+                right = dims[0];
+            } else if (right < 0) {
+                right = 0;
+            }
+
+            for (int j = 0; j < (bottom - top); j++) {
+                // Left edge
+                if (left > 0 && top > 0) {
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + left) +
+                           ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + left) +
+                           ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + left) +
+                           ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                }
+
+                // Right edge
+                if (right > 0 && top > 0) {
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + right) +
+                           ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + right) +
+                           ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * (top + j) + right) +
+                           ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                }
+
+            }
+            for (int k = 0; k < (right - left); k++) {
+                // Top edge
+                if (top < dims[1]) {
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * top + left + k) +
+                           ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * top + left + k) +
+                           ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * top + left + k) +
+                           ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+
+                }
+                // Bottom edge
+                if (bottom < dims[1]) {
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * bottom + left + k) +
+                           ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * bottom + left + k) +
+                           ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                    pixels[ImageConstants.PIX_CHANNELS * (dims[0] * bottom + left + k) +
+                           ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                }
+
+
+            }
+
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilter.java
new file mode 100644
index 0000000..72452b3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 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.
+ */
+// Takes in an array, returns the size of the array
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.lang.reflect.Array;
+
+public class FloatArrayToSizeFilter extends Filter {
+
+    private static final String TAG = "FloatArrayToSizeFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+    /**
+     * @param context
+     * @param name
+     */
+    public FloatArrayToSizeFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType intT = FrameType.single(int.class);
+        FrameType floatType = FrameType.array(float.class);
+
+        return new Signature()
+                .addInputPort("array", Signature.PORT_REQUIRED, floatType)
+                .addOutputPort("size", Signature.PORT_REQUIRED, intT)
+                .disallowOtherPorts();
+    }
+
+    /**
+     * @see androidx.media.filterfw.Filter#onProcess()
+     */
+    @Override
+    protected void onProcess() {
+        FrameValue arrayFrame = getConnectedInputPort("array").pullFrame().asFrameValues();
+        Object array = arrayFrame.getValue();
+        int size = Array.getLength(array);
+
+        OutputPort outPort = getConnectedOutputPort("size");
+        FrameValue sizeFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        sizeFrame.setValue(size);
+        outPort.pushFrame(sizeFrame);
+
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilter.java
new file mode 100644
index 0000000..4606cfb
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 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.
+ */
+// Takes in an array, returns the size of the array
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+public class FloatArrayToStrFilter extends Filter {
+
+    private static final String TAG = "FloatArrayToStrFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * @param context
+     * @param name
+     */
+    public FloatArrayToStrFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType floatType = FrameType.array(float.class);
+
+        return new Signature()
+                .addInputPort("array", Signature.PORT_REQUIRED, floatType)
+                .addOutputPort("string", Signature.PORT_REQUIRED, FrameType.single(String.class))
+                .disallowOtherPorts();
+    }
+
+    /**
+     * @see androidx.media.filterfw.Filter#onProcess()
+     */
+    @Override
+    protected void onProcess() {
+        FrameValue arrayFrame = getConnectedInputPort("array").pullFrame().asFrameValues();
+        float[] array = (float[]) arrayFrame.getValue();
+        String outstr = Arrays.toString(array);
+
+        OutputPort outPort = getConnectedOutputPort("string");
+        FrameValue stringFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        stringFrame.setValue(outstr);
+        outPort.pushFrame(stringFrame);
+
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/IfElseFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/IfElseFilter.java
new file mode 100644
index 0000000..9553b75
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/IfElseFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.util.Log;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameBuffer2D;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+
+public class IfElseFilter extends Filter {
+
+    private static final String TAG = "IfElseFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    public IfElseFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType videoIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+        FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
+
+        return new Signature().addInputPort("falseResult", Signature.PORT_REQUIRED, imageIn)
+                .addInputPort("trueResult", Signature.PORT_REQUIRED, videoIn)
+                .addInputPort("condition", Signature.PORT_REQUIRED, FrameType.single(boolean.class))
+                .addOutputPort("output", Signature.PORT_REQUIRED, imageOut)
+                .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("output");
+        FrameImage2D trueFrame = getConnectedInputPort("trueResult").pullFrame().asFrameImage2D();
+        FrameImage2D falseFrame = getConnectedInputPort("falseResult").pullFrame().asFrameImage2D();
+        FrameValue boolFrameValue = getConnectedInputPort("condition").pullFrame().asFrameValue();
+        boolean condition = (Boolean) boolFrameValue.getValue();
+        FrameBuffer2D outputFrame;
+        // If the condition is true, then we want to use the camera, else use the gallery
+        if (condition) {
+            outputFrame = trueFrame;
+        } else {
+            outputFrame = falseFrame;
+        }
+        outPort.pushFrame(outputFrame);
+
+    }
+
+}
diff --git a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageConstants.java
similarity index 60%
rename from tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
rename to tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageConstants.java
index 7bb4c35..cfae8fc 100644
--- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageConstants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright 2013 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,18 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.testplugin;
+package androidx.media.filterfw.samples.simplecamera;
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+public class ImageConstants {
 
-public class TestPlugin extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
+    public static final int MAX_BYTE = 255;
+    public static final int RED_OFFSET = 0;
+    public static final int GREEN_OFFSET = 1;
+    public static final int BLUE_OFFSET = 2;
+    public static final int PIX_CHANNELS = 4;
 }
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilter.java
new file mode 100644
index 0000000..14ec762
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilter.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2013 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.
+ */
+// Takes sharpness score, rates the image good if above 10, bad otherwise
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.widget.ImageView;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public class ImageGoodnessFilter extends Filter {
+
+    private static final String TAG = "ImageGoodnessFilter";
+    private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final static String GREAT = "Great Picture!";
+    private final static String GOOD = "Good Picture!";
+    private final static String OK = "Ok Picture";
+    private final static String BAD = "Bad Picture";
+    private final static String AWFUL = "Awful Picture";
+    private final static float SMALL_SCORE_INC = 0.25f;
+    private final static float BIG_SCORE_INC = 0.5f;
+    private final static float LOW_VARIANCE = 0.1f;
+    private final static float MEDIUM_VARIANCE = 10;
+    private final static float HIGH_VARIANCE = 100;
+    private float sharpnessMean = 0;
+    private float sharpnessVar = 0;
+    private float underExposureMean = 0;
+    private float underExposureVar = 0;
+    private float overExposureMean = 0;
+    private float overExposureVar = 0;
+    private float contrastMean = 0;
+    private float contrastVar = 0;
+    private float colorfulnessMean = 0;
+    private float colorfulnessVar = 0;
+    private float brightnessMean = 0;
+    private float brightnessVar = 0;
+
+    private float motionMean = 0;
+    private float scoreMean = 0;
+    private static final float DECAY = 0.03f;
+    /**
+     * @param context
+     * @param name
+     */
+    public ImageGoodnessFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        FrameType floatT = FrameType.single(float.class);
+        FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
+
+        return new Signature()
+                .addInputPort("sharpness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("overExposure", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("underExposure", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("colorfulness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("contrastRating", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("motionValues", Signature.PORT_REQUIRED, FrameType.array(float.class))
+                .addInputPort("brightness", Signature.PORT_REQUIRED, floatT)
+                .addInputPort("capturing", Signature.PORT_REQUIRED, FrameType.single(boolean.class))
+                .addInputPort("image", Signature.PORT_REQUIRED, imageIn)
+                .addOutputPort("goodOrBadPic", Signature.PORT_REQUIRED,
+                        FrameType.single(String.class))
+                .addOutputPort("score", Signature.PORT_OPTIONAL, floatT)
+                .disallowOtherPorts();
+    }
+
+    /**
+     * @see androidx.media.filterfw.Filter#onProcess()
+     */
+    @Override
+    protected void onProcess() {
+        FrameValue sharpnessFrameValue =
+                getConnectedInputPort("sharpness").pullFrame().asFrameValue();
+        float sharpness = ((Float)sharpnessFrameValue.getValue()).floatValue();
+
+        FrameValue overExposureFrameValue =
+                getConnectedInputPort("overExposure").pullFrame().asFrameValue();
+        float overExposure = ((Float)overExposureFrameValue.getValue()).floatValue();
+
+        FrameValue underExposureFrameValue =
+                getConnectedInputPort("underExposure").pullFrame().asFrameValue();
+        float underExposure = ((Float)underExposureFrameValue.getValue()).floatValue();
+
+        FrameValue colorfulnessFrameValue =
+                getConnectedInputPort("colorfulness").pullFrame().asFrameValue();
+        float colorfulness = ((Float)colorfulnessFrameValue.getValue()).floatValue();
+
+        FrameValue contrastRatingFrameValue =
+                getConnectedInputPort("contrastRating").pullFrame().asFrameValue();
+        float contrastRating = ((Float)contrastRatingFrameValue.getValue()).floatValue();
+
+        FrameValue brightnessFrameValue =
+                getConnectedInputPort("brightness").pullFrame().asFrameValue();
+        float brightness = ((Float)brightnessFrameValue.getValue()).floatValue();
+
+        FrameValue motionValuesFrameValue =
+                getConnectedInputPort("motionValues").pullFrame().asFrameValue();
+        float[] motionValues = (float[]) motionValuesFrameValue.getValue();
+
+
+        float vectorAccel = (float) Math.sqrt(Math.pow(motionValues[0], 2) +
+                Math.pow(motionValues[1], 2) + Math.pow(motionValues[2], 2));
+        String outStr;
+
+        FrameValue capturingFrameValue =
+                getConnectedInputPort("capturing").pullFrame().asFrameValue();
+        boolean capturing = (Boolean) capturingFrameValue.getValue();
+
+        FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
+
+
+        // TODO: get rid of magic numbers
+        float score = 0.0f;
+        score = computePictureScore(vectorAccel, sharpness, underExposure, overExposure,
+                    contrastRating, colorfulness, brightness);
+        if (scoreMean == 0) scoreMean = score;
+        else scoreMean = scoreMean * (1 - DECAY) + score * DECAY;
+
+        if (motionMean == 0) motionMean = vectorAccel;
+        else motionMean = motionMean * (1 - DECAY) + vectorAccel * DECAY;
+
+        float classifierScore = classifierComputeScore(vectorAccel, sharpness, underExposure,
+                colorfulness, contrastRating, score);
+
+//        Log.v(TAG, "ClassifierScore:: " + classifierScore);
+        final float GREAT_SCORE = 3.5f;
+        final float GOOD_SCORE = 2.5f;
+        final float OK_SCORE = 1.5f;
+        final float BAD_SCORE = 0.5f;
+
+        if (score >= GREAT_SCORE) {
+            outStr = GREAT;
+        } else if (score >= GOOD_SCORE) {
+            outStr = GOOD;
+        } else if (score >= OK_SCORE) {
+            outStr = OK;
+        } else if (score >= BAD_SCORE) {
+            outStr = BAD;
+        } else {
+            outStr = AWFUL;
+        }
+
+        if(capturing) {
+            if (outStr.equals(GREAT)) {
+                // take a picture
+                Bitmap bitmap = inputImage.toBitmap();
+
+                new AsyncOperation().execute(bitmap);
+                final float RESET_FEATURES = 0.01f;
+                sharpnessMean = RESET_FEATURES;
+                underExposureMean = RESET_FEATURES;
+                overExposureMean = RESET_FEATURES;
+                contrastMean = RESET_FEATURES;
+                colorfulnessMean = RESET_FEATURES;
+                brightnessMean = RESET_FEATURES;
+            }
+        }
+
+        OutputPort outPort = getConnectedOutputPort("goodOrBadPic");
+        FrameValue stringFrame = outPort.fetchAvailableFrame(null).asFrameValue();
+        stringFrame.setValue(outStr);
+        outPort.pushFrame(stringFrame);
+
+        OutputPort scoreOutPort = getConnectedOutputPort("score");
+        FrameValue scoreFrame = scoreOutPort.fetchAvailableFrame(null).asFrameValue();
+        scoreFrame.setValue(score);
+        scoreOutPort.pushFrame(scoreFrame);
+
+    }
+
+    private class AsyncOperation extends AsyncTask<Bitmap, Void, String> {
+        private Bitmap b;
+        protected void onPostExecute(String result) {
+            ImageView view = SmartCamera.getImageView();
+            view.setImageBitmap(b);
+        }
+
+        @Override
+        protected String doInBackground(Bitmap... params) {
+            // TODO Auto-generated method stub
+            b = params[0];
+            return null;
+        }
+
+    }
+    // Returns a number between -1 and 1
+    private float classifierComputeScore(float vectorAccel, float sharpness, float underExposure,
+           float colorfulness, float contrast, float score) {
+        float result = (-0.0223f * sharpness + -0.0563f * underExposure + 0.0137f * colorfulness
+                + 0.3102f * contrast + 0.0314f * vectorAccel + -0.0094f * score + 0.0227f *
+                sharpnessMean + 0.0459f * underExposureMean + -0.3934f * contrastMean +
+                -0.0697f * motionMean + 0.0091f * scoreMean + -0.0152f);
+        return result;
+    }
+
+    // Returns a number between -1 and 4 representing the score for this picture
+    private float computePictureScore(float vector_accel, float sharpness,
+            float underExposure, float overExposure, float contrastRating, float colorfulness,
+            float brightness) {
+        final float ACCELERATION_THRESHOLD_VERY_STEADY = 0.1f;
+        final float ACCELERATION_THRESHOLD_STEADY = 0.3f;
+        final float ACCELERATION_THRESHOLD_MOTION = 2f;
+
+        float score = 0.0f;
+        if (vector_accel > ACCELERATION_THRESHOLD_MOTION) {
+            score -= (BIG_SCORE_INC + BIG_SCORE_INC); // set score to -1, bad pic
+        } else if (vector_accel > ACCELERATION_THRESHOLD_STEADY) {
+            score -= BIG_SCORE_INC;
+            score = subComputeScore(sharpness, underExposure, overExposure, contrastRating,
+                    colorfulness, brightness, score);
+        } else if (vector_accel < ACCELERATION_THRESHOLD_VERY_STEADY) {
+            score += BIG_SCORE_INC;
+            score = subComputeScore(sharpness, underExposure, overExposure, contrastRating,
+                    colorfulness, brightness, score);
+        } else {
+            score = subComputeScore(sharpness, underExposure, overExposure, contrastRating,
+                    colorfulness, brightness, score);
+        }
+        return score;
+    }
+
+    // Changes the score by at most +/- 3.5
+    private float subComputeScore(float sharpness, float underExposure, float overExposure,
+                float contrastRating, float colorfulness, float brightness, float score) {
+        // The score methods return values -0.5 to 0.5
+        final float SHARPNESS_WEIGHT = 2;
+        score += SHARPNESS_WEIGHT * sharpnessScore(sharpness);
+        score += underExposureScore(underExposure);
+        score += overExposureScore(overExposure);
+        score += contrastScore(contrastRating);
+        score += colorfulnessScore(colorfulness);
+        score += brightnessScore(brightness);
+        return score;
+    }
+
+    private float sharpnessScore(float sharpness) {
+        if (sharpnessMean == 0) {
+            sharpnessMean = sharpness;
+            sharpnessVar = 0;
+            return 0;
+        } else {
+            sharpnessMean = sharpnessMean * (1 - DECAY) + sharpness * DECAY;
+            sharpnessVar = sharpnessVar * (1 - DECAY) + (sharpness - sharpnessMean) *
+                    (sharpness - sharpnessMean) * DECAY;
+            if (sharpnessVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (sharpness < sharpnessMean && sharpnessVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (sharpness < sharpnessMean) {
+                return -SMALL_SCORE_INC;
+            } else if (sharpness > sharpnessMean && sharpnessVar > HIGH_VARIANCE) {
+                return 0;
+            } else if (sharpness > sharpnessMean && sharpnessVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else  {
+                return BIG_SCORE_INC; // low variance, sharpness above the mean
+            }
+        }
+    }
+
+    private float underExposureScore(float underExposure) {
+        if (underExposureMean == 0) {
+            underExposureMean = underExposure;
+            underExposureVar = 0;
+            return 0;
+        } else {
+            underExposureMean = underExposureMean * (1 - DECAY) + underExposure * DECAY;
+            underExposureVar = underExposureVar * (1 - DECAY) + (underExposure - underExposureMean)
+                    * (underExposure - underExposureMean) * DECAY;
+            if (underExposureVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (underExposure > underExposureMean && underExposureVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (underExposure > underExposureMean) {
+                return -SMALL_SCORE_INC;
+            } else if (underExposure < underExposureMean && underExposureVar > HIGH_VARIANCE) {
+                return 0;
+            } else if (underExposure < underExposureMean && underExposureVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else {
+                return BIG_SCORE_INC; // low variance, underExposure below the mean
+            }
+        }
+    }
+
+    private float overExposureScore(float overExposure) {
+        if (overExposureMean == 0) {
+            overExposureMean = overExposure;
+            overExposureVar = 0;
+            return 0;
+        } else {
+            overExposureMean = overExposureMean * (1 - DECAY) + overExposure * DECAY;
+            overExposureVar = overExposureVar * (1 - DECAY) + (overExposure - overExposureMean) *
+                    (overExposure - overExposureMean) * DECAY;
+            if (overExposureVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (overExposure > overExposureMean && overExposureVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (overExposure > overExposureMean) {
+                return -SMALL_SCORE_INC;
+            } else if (overExposure < overExposureMean && overExposureVar > HIGH_VARIANCE) {
+                return 0;
+            } else if (overExposure < overExposureMean && overExposureVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else {
+                return BIG_SCORE_INC; // low variance, overExposure below the mean
+            }
+        }
+    }
+
+    private float contrastScore(float contrast) {
+        if (contrastMean == 0) {
+            contrastMean = contrast;
+            contrastVar = 0;
+            return 0;
+        } else {
+            contrastMean = contrastMean * (1 - DECAY) + contrast * DECAY;
+            contrastVar = contrastVar * (1 - DECAY) + (contrast - contrastMean) *
+                    (contrast - contrastMean) * DECAY;
+            if (contrastVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (contrast < contrastMean && contrastVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (contrast < contrastMean) {
+                return -SMALL_SCORE_INC;
+            } else if (contrast > contrastMean && contrastVar > 100) {
+                return 0;
+            } else if (contrast > contrastMean && contrastVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else {
+                return BIG_SCORE_INC; // low variance, contrast above the mean
+            }
+        }
+    }
+
+    private float colorfulnessScore(float colorfulness) {
+        if (colorfulnessMean == 0) {
+            colorfulnessMean = colorfulness;
+            colorfulnessVar = 0;
+            return 0;
+        } else {
+            colorfulnessMean = colorfulnessMean * (1 - DECAY) + colorfulness * DECAY;
+            colorfulnessVar = colorfulnessVar * (1 - DECAY) + (colorfulness - colorfulnessMean) *
+                    (colorfulness - colorfulnessMean) * DECAY;
+            if (colorfulnessVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (colorfulness < colorfulnessMean && colorfulnessVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (colorfulness < colorfulnessMean) {
+                return -SMALL_SCORE_INC;
+            } else if (colorfulness > colorfulnessMean && colorfulnessVar > 100) {
+                return 0;
+            } else if (colorfulness > colorfulnessMean && colorfulnessVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else {
+                return BIG_SCORE_INC; // low variance, colorfulness above the mean
+            }
+        }
+    }
+
+    private float brightnessScore(float brightness) {
+        if (brightnessMean == 0) {
+            brightnessMean = brightness;
+            brightnessVar = 0;
+            return 0;
+        } else {
+            brightnessMean = brightnessMean * (1 - DECAY) + brightness * DECAY;
+            brightnessVar = brightnessVar * (1 - DECAY) + (brightness - brightnessMean) *
+                    (brightness - brightnessMean) * DECAY;
+            if (brightnessVar < LOW_VARIANCE) {
+                return BIG_SCORE_INC;
+            } else if (brightness < brightnessMean && brightnessVar > MEDIUM_VARIANCE) {
+                return -BIG_SCORE_INC;
+            } else if (brightness < brightnessMean) {
+                return -SMALL_SCORE_INC;
+            } else if (brightness > brightnessMean && brightnessVar > 100) {
+                return 0;
+            } else if (brightness > brightnessMean && brightnessVar > MEDIUM_VARIANCE) {
+                return SMALL_SCORE_INC;
+            } else {
+                return BIG_SCORE_INC; // low variance, brightness above the mean
+            }
+        }
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/MotionSensorWTime.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/MotionSensorWTime.java
new file mode 100644
index 0000000..64f3ef3
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/MotionSensorWTime.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Make values from a motion sensor (e.g., accelerometer) available as filter outputs.
+
+package androidx.media.filterfw.samples.simplecamera;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.FrameValues;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+public final class MotionSensorWTime extends Filter implements SensorEventListener {
+
+    private SensorManager mSensorManager = null;
+    private Sensor mSensor = null;
+
+    private float[] mValues = new float[3];
+    private float[][] mTemp = new float[3][3];
+    private float[] mAvgValues = new float[3];
+    private int mCounter = 0;
+
+    public MotionSensorWTime(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addOutputPort("values", Signature.PORT_REQUIRED, FrameType.array(float.class))
+            .addOutputPort("timestamp", Signature.PORT_OPTIONAL, FrameType.single(long.class))
+            .disallowOtherPorts();
+    }
+
+    @Override
+    protected void onPrepare() {
+        mSensorManager = (SensorManager)getContext().getApplicationContext()
+                            .getSystemService(Context.SENSOR_SERVICE);
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
+        // TODO: currently, the type of sensor is hardcoded. Should be able to set the sensor
+        //  type as filter input!
+        mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI);
+    }
+
+    @Override
+    protected void onTearDown() {
+        mSensorManager.unregisterListener(this);
+    }
+
+    @Override
+    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // (Do we need to do something when sensor accuracy changes?)
+    }
+
+    @Override
+    public final void onSensorChanged(SensorEvent event) {
+        synchronized(mValues) {
+            mValues[0] = event.values[0];
+            mValues[1] = event.values[1];
+            mValues[2] = event.values[2];
+        }
+    }
+
+    @Override
+    protected void onProcess() {
+        OutputPort outPort = getConnectedOutputPort("values");
+        FrameValues outFrame = outPort.fetchAvailableFrame(null).asFrameValues();
+        synchronized(mValues) {
+            if (mCounter < 3 && mCounter >= 0) {
+                mTemp[0][mCounter] = mValues[0];
+                mTemp[1][mCounter] = mValues[1];
+                mTemp[2][mCounter] = mValues[2];
+            }
+
+            mCounter = (mCounter + 1) % 3;
+
+            mAvgValues[0] = (mTemp[0][0] + mTemp[0][1] + mTemp[0][2]) / 3;
+            mAvgValues[1] = (mTemp[1][0] + mTemp[1][1] + mTemp[1][2]) / 3;
+            mAvgValues[2] = (mTemp[2][0] + mTemp[2][1] + mTemp[2][2]) / 3;
+            outFrame.setValues(mAvgValues);
+        }
+        outFrame.setTimestamp(System.currentTimeMillis() * 1000000L);
+        outPort.pushFrame(outFrame);
+
+        OutputPort timeOutPort = getConnectedOutputPort("timestamp");
+        if (timeOutPort != null) {
+            long timestamp = System.nanoTime();
+            Log.v("MotionSensor", "Timestamp is: " + timestamp);
+            FrameValue timeStampFrame = timeOutPort.fetchAvailableFrame(null).asFrameValue();
+            timeStampFrame.setValue(timestamp);
+            timeOutPort.pushFrame(timeStampFrame);
+        }
+    }
+}
+
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/SmartCamera.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/SmartCamera.java
new file mode 100644
index 0000000..ba0333a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/SmartCamera.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+import androidx.media.filterfw.FilterGraph;
+import androidx.media.filterfw.GraphReader;
+import androidx.media.filterfw.GraphRunner;
+import androidx.media.filterfw.MffContext;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+
+public class SmartCamera extends Activity {
+
+    private SurfaceView mCameraView;
+    private TextView mGoodBadTextView;
+    private TextView mFPSTextView;
+    private TextView mEyesTextView;
+    private TextView mSmilesTextView;
+    private TextView mScoreTextView;
+    private static ImageView mImageView1;
+    private static ImageView mImageView2;
+    private static ImageView mImageView3;
+    private static ImageView mImageView4;
+    private static ImageView mImageView5;
+    private Button mStartStopButton;
+    private TextView mImagesSavedTextView;
+    private Spinner mSpinner;
+    private LinearLayout mLinearLayout;
+
+    private MffContext mContext;
+    private FilterGraph mGraph;
+    private GraphRunner mRunner;
+    private Handler mHandler = new Handler();
+
+    private static final String TAG = "SmartCamera";
+    private static final boolean sUseFacialExpression = false;
+    private boolean isPendingRunGraph = false;
+
+    private static ArrayList<ImageView> mImages;
+    private static int count = -1;
+    private static boolean countHasReachedMax = false;
+    private static int numImages = 0;
+
+    // Function to return the correct image view to display the current bitmap
+    public static ImageView getImageView() {
+        if (count == numImages-1) countHasReachedMax = true;
+        count = (count+1) % numImages;
+        return mImages.get(count);
+    }
+
+    // Function used to run images through the graph, mainly for CSV data generation
+    public void runGraphOnImage(String filePath, String fileName) {
+        if(fileName.endsWith(".jpg") == false) {
+            return;
+        }
+        mGraph.getVariable("gallerySource").setValue(filePath + "/" + fileName);
+        Log.v(TAG, "runGraphOnImage : : " + filePath + " name: " + fileName);
+        mGraph.getVariable("imageName").setValue(fileName);
+        mGraph.getVariable("filePath").setValue(filePath); // wrong
+        try {
+            Thread.sleep(400);
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    // Function to clear the "Images Saved" text off the screen
+    private void clearImagesSavedTextView() {
+        mImagesSavedTextView.setText("");
+    }
+
+    // Function to capture the images in the current imageviews and save them to the gallery
+    private void captureImages() {
+        ((WaveTriggerFilter) mGraph.getFilter("snapEffect")).trigger();
+        mGraph.getVariable("startCapture").setValue(false);
+        Bitmap bitmap = null;
+        Drawable res = getResources().getDrawable(R.drawable.black_screen);
+        Calendar cal = Calendar.getInstance();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
+
+        Log.v(TAG, "numImages: " + numImages + " count: " + count +
+                " hasReachedMax: " + countHasReachedMax);
+        int maxI = countHasReachedMax ? numImages : count+1;
+        if(maxI != 0) {
+            if (maxI == 1) mImagesSavedTextView.setText("Image Saved");
+            else {
+                mImagesSavedTextView.setText("" + maxI + " Images Saved");
+            }
+        }
+        for (int i = 0; i < maxI; i++) {
+            bitmap = ((BitmapDrawable)mImages.get(i).getDrawable()).getBitmap();
+            mImages.get(i).setImageDrawable(res);
+            MediaStore.Images.Media.insertImage(getContentResolver(), bitmap,
+                    sdf.format(cal.getTime()) + "_image" + i + ".jpg", "image " + i);
+        }
+        mStartStopButton.setText("Start");
+        count = -1;
+        countHasReachedMax = false;
+        mSpinner.setEnabled(true);
+        mHandler.postDelayed(new Runnable() {
+            public void run() {
+                clearImagesSavedTextView();
+            }
+        }, 5000);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.simplecamera);
+        setTitle("Smart Camera");
+
+        mContext = new MffContext(this);
+
+        mCameraView = (SurfaceView) findViewById(R.id.cameraView);
+        mGoodBadTextView = (TextView) findViewById(R.id.goodOrBadTextView);
+        mFPSTextView = (TextView) findViewById(R.id.fpsTextView);
+        mScoreTextView = (TextView) findViewById(R.id.scoreTextView);
+        mStartStopButton = (Button) findViewById(R.id.startButton);
+        mImagesSavedTextView = (TextView) findViewById(R.id.imagesSavedTextView);
+        mImagesSavedTextView.setText("");
+        mSpinner = (Spinner) findViewById(R.id.spinner);
+        mLinearLayout = (LinearLayout) findViewById(R.id.scrollViewLinearLayout);
+        mImages = new ArrayList<ImageView>();
+
+        // Spinner is used to determine how many image views are displayed at the bottom
+        // of the screen. Based on the item position that is selected, we inflate that
+        // many imageviews into the bottom linear layout.
+        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parentView, View selectedItemView,
+                    int position, long id) {
+                mLinearLayout.removeViews(0,numImages);
+                numImages = position+1;
+                mImages.clear();
+                LayoutInflater inflater = getLayoutInflater();
+                for (int i = 0; i < numImages; i++) {
+                    ImageView tmp = (ImageView) inflater.inflate(R.layout.imageview, null);
+                    mImages.add(tmp);
+                    mLinearLayout.addView(tmp);
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parentView) {
+            }
+        });
+
+        numImages = mSpinner.getSelectedItemPosition()+1;
+        mImages.clear();
+        LayoutInflater inflater = getLayoutInflater();
+        for (int i = 0; i < numImages; i++) {
+            ImageView tmp = (ImageView) inflater.inflate(R.layout.imageview, null);
+            mImages.add(tmp);
+            mLinearLayout.addView(tmp);
+
+        }
+
+        // Button used to start and stop the capture of images when they are deemed great
+        mStartStopButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mStartStopButton.getText().equals("Start")) {
+                    mGraph.getVariable("startCapture").setValue(true);
+                    mStartStopButton.setText("Stop");
+                    mSpinner.setEnabled(false);
+                } else {
+                    boolean tmp = (Boolean) mGraph.getVariable("startCapture").getValue();
+                    if (tmp == false) {
+                        return;
+                    }
+                    if (count == numImages-1) countHasReachedMax = true;
+                    captureImages();
+                }
+            }
+        });
+
+        // Button to open the gallery to show the images in there
+        Button galleryOpen = (Button) findViewById(R.id.galleryOpenButton);
+        galleryOpen.setOnClickListener(new OnClickListener() {
+           @Override
+           public void onClick(View v) {
+               Intent openGalleryIntent = new Intent(Intent.ACTION_MAIN);
+               openGalleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
+               startActivity(openGalleryIntent);
+           }
+        });
+
+        loadGraph();
+        mGraph.getVariable("startCapture").setValue(false);
+        runGraph();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        Log.i(TAG, "onPause");
+        if (mContext != null) {
+            mContext.onPause();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.i(TAG, "onResume");
+        if (mContext != null) {
+            mContext.onResume();
+        }
+        if (isPendingRunGraph) {
+            isPendingRunGraph = false;
+            runGraph();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        Log.i(TAG, "onStop");
+    }
+
+    // Build the Filtergraph for Camera
+    private void loadGraph() {
+        try {
+            mGraph = GraphReader.readXmlGraphResource(mContext, R.raw.camera_graph);
+            mRunner = mGraph.getRunner();
+
+            // Connect views
+            mGraph.bindFilterToView("camViewTarget", mCameraView);
+            mGraph.bindFilterToView("goodOrBadTextView", mGoodBadTextView);
+            mGraph.bindFilterToView("fpsTextView", mFPSTextView);
+            mGraph.bindFilterToView("scoreTextView", mScoreTextView);
+
+            // Used for Facial Expressions
+            if (sUseFacialExpression) {
+                mGraph.bindFilterToView("eyesTextView", mEyesTextView);
+                mGraph.bindFilterToView("smilesTextView", mSmilesTextView);
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // Asynchronously run the filtergraph
+    private void runGraph() {
+        mRunner.setIsVerbose(true);
+        mRunner.start(mGraph);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/WaveTriggerFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/WaveTriggerFilter.java
new file mode 100644
index 0000000..9f72940
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/WaveTriggerFilter.java
@@ -0,0 +1,83 @@
+/*
+ * 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.InputPort;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.OutputPort;
+import androidx.media.filterfw.Signature;
+
+// The Filter is used to generate the camera snap effect.
+// The effect is to give the image a sudden white appearance.
+public final class WaveTriggerFilter extends Filter {
+
+    private boolean mTrigger = false;
+    private boolean mInWaveMode = false;
+    private float mTime = 0f;
+
+    public WaveTriggerFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+            .addOutputPort("value", Signature.PORT_REQUIRED, FrameType.single())
+            .disallowOtherPorts();
+    }
+
+    public synchronized void trigger() {
+        mTrigger = true;
+    }
+
+    @Override
+    public void onInputPortOpen(InputPort port) {
+        if (port.getName().equals("trigger")) {
+            port.bindToFieldNamed("mTrigger");
+            port.setAutoPullEnabled(true);
+        }
+    }
+
+    @Override
+    protected synchronized void onProcess() {
+        // Check if we were triggered
+        if (mTrigger) {
+            mInWaveMode = true;
+            mTrigger = false;
+            mTime = 0.5f;
+        }
+
+        // Calculate output value
+        float value = 0.5f;
+        if (mInWaveMode) {
+            value = -Math.abs(mTime - 1f) + 1f;
+            mTime += 0.2f;
+            if (mTime >= 2f) {
+                mInWaveMode = false;
+            }
+        }
+
+        // Push Value
+        OutputPort outPort = getConnectedOutputPort("value");
+        FrameValue frame = outPort.fetchAvailableFrame(null).asFrameValue();
+        frame.setValue(value);
+        outPort.pushFrame(frame);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
new file mode 100644
index 0000000..50926a6
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := SmartCamera-tests
+
+LOCAL_SRC_FILES += $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+#LOCAL_STATIC_JAVA_LIBRARIES := filterframework-test-lib
+LOCAL_STATIC_JAVA_LIBRARIES += guava
+
+#LOCAL_JAVA_LIBRARIES := filterframework-test-lib
+LOCAL_STATIC_JAVA_LIBRARIES := guava
+
+LOCAL_STATIC_JAVA_LIBRARIES +=
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_INSTRUMENTATION_FOR := SmartCamera
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml
new file mode 100644
index 0000000..3363af4
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.media.filterfw.samples.simplecamera.tests"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" />
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="androidx.media.filterfw.samples.simplecamera" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+</manifest>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/project.properties b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/project.properties
new file mode 100644
index 0000000..4653837a
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/project.properties
@@ -0,0 +1,16 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library.reference.1=../filterfw-test-lib
+android.library.reference.2=../filterfw
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/res/.README b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/res/.README
new file mode 100644
index 0000000..c29cd87
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/res/.README
@@ -0,0 +1,3 @@
+The res directory is needed for Eclipse to correctly build the project, but it
+is not possible to check in a directory into git. This file guarantees the res
+directory exists.
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameSourceFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameSourceFilter.java
new file mode 100644
index 0000000..7daa03f
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameSourceFilter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * A {@link Filter} that pushes out externally injected frames.
+ * <p> When a frame is injected using {@link #injectFrame(Frame)}, this source will push it on its
+ * output port and then sleep until another frame is injected.
+ * <p> Multiple frames may be injected before any frame is pushed out. In this case they will be
+ * queued and pushed in FIFO order.
+ */
+class FrameSourceFilter extends Filter {
+
+    private final Queue<Frame> mFrames = new LinkedList<Frame>();
+
+    FrameSourceFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+                .addOutputPort("output", Signature.PORT_REQUIRED, FrameType.any())
+                .disallowOtherPorts();
+    }
+
+    private synchronized Frame obtainFrame() {
+        if (mFrames.isEmpty()) {
+            enterSleepState();
+            return null;
+        } else {
+            return mFrames.poll();
+        }
+    }
+
+    /**
+     * Call this method to inject a frame that will be pushed in a future execution of the filter.
+     * <p> If multiple frames are injected then they will be pushed one per execution in FIFO order.
+     */
+    public synchronized void injectFrame(Frame frame) {
+        mFrames.add(frame);
+        wakeUp();
+    }
+
+    @Override
+    protected void onProcess() {
+        Frame frame = obtainFrame();
+        if (frame != null) {
+            getConnectedOutputPort("output").pushFrame(frame);
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameTargetFilter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameTargetFilter.java
new file mode 100644
index 0000000..1f0e267
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/FrameTargetFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw;
+
+/**
+ * A {@link Filter} that consumes frames and allows to register a listener to observe when
+ * a new frame has been consumed.
+ */
+class FrameTargetFilter extends Filter {
+
+    interface Listener {
+        /**
+         * Called each time this filter receives a new frame. The implementer of this method is
+         * responsible for releasing the frame.
+         */
+        void onFramePushed(String filterName, Frame frame);
+    }
+
+    private Listener mListener;
+
+    FrameTargetFilter(MffContext context, String name) {
+        super(context, name);
+    }
+
+    @Override
+    public Signature getSignature() {
+        return new Signature()
+                .addInputPort("input", Signature.PORT_REQUIRED, FrameType.any())
+                .disallowOtherPorts();
+    }
+
+    public synchronized void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected synchronized void onProcess() {
+        Frame frame = getConnectedInputPort("input").pullFrame();
+        if (mListener != null) {
+            frame.retain();
+            mListener.onFramePushed(getName(), frame);
+        }
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffFilterTestCase.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffFilterTestCase.java
new file mode 100644
index 0000000..84efd28
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffFilterTestCase.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw;
+
+import androidx.media.filterfw.GraphRunner.Listener;
+import androidx.media.filterfw.Signature.PortInfo;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A {@link TestCase} for testing single MFF filter runs. Implementers should extend this class and
+ * implement the {@link #createFilter(MffContext)} method to create the filter under test. Inside
+ * each test method, the implementer should supply one or more frames for all the filter inputs
+ * (calling {@link #injectInputFrame(String, Frame)}) and then invoke {@link #process()}. Once the
+ * processing finishes, one should call {@link #getOutputFrame(String)} to get and inspect the
+ * output frames.
+ *
+ * TODO: extend this to deal with filters that push multiple output frames.
+ * TODO: relax the requirement that all output ports should be pushed (the implementer should be
+ *       able to tell which ports to wait for before process() returns).
+ * TODO: handle undeclared inputs and outputs.
+ */
+public abstract class MffFilterTestCase extends MffTestCase {
+
+    private static final long DEFAULT_TIMEOUT_MS = 1000;
+
+    private FilterGraph mGraph;
+    private GraphRunner mRunner;
+    private Map<String, Frame> mOutputFrames;
+    private Set<String> mEmptyOutputPorts;
+
+    private SettableFuture<Void> mProcessResult;
+
+    protected abstract Filter createFilter(MffContext mffContext);
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        MffContext mffContext = getMffContext();
+        FilterGraph.Builder graphBuilder = new FilterGraph.Builder(mffContext);
+        Filter filterUnderTest = createFilter(mffContext);
+        graphBuilder.addFilter(filterUnderTest);
+
+        connectInputPorts(mffContext, graphBuilder, filterUnderTest);
+        connectOutputPorts(mffContext, graphBuilder, filterUnderTest);
+
+        mGraph = graphBuilder.build();
+        mRunner = mGraph.getRunner();
+        mRunner.setListener(new Listener() {
+            @Override
+            public void onGraphRunnerStopped(GraphRunner runner) {
+                mProcessResult.set(null);
+            }
+
+            @Override
+            public void onGraphRunnerError(Exception exception, boolean closedSuccessfully) {
+                mProcessResult.setException(exception);
+            }
+        });
+
+        mOutputFrames = new HashMap<String, Frame>();
+        mProcessResult = SettableFuture.create();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (Frame frame : mOutputFrames.values()) {
+            frame.release();
+        }
+        mOutputFrames = null;
+
+        mRunner.stop();
+        mRunner = null;
+        mGraph = null;
+
+        mProcessResult = null;
+        super.tearDown();
+    }
+
+    protected void injectInputFrame(String portName, Frame frame) {
+        FrameSourceFilter filter = (FrameSourceFilter) mGraph.getFilter("in_" + portName);
+        filter.injectFrame(frame);
+    }
+
+    /**
+     * Returns the frame pushed out by the filter under test. Should only be called after
+     * {@link #process(long)} has returned.
+     */
+    protected Frame getOutputFrame(String outputPortName) {
+        return mOutputFrames.get("out_" + outputPortName);
+    }
+
+    protected void process(long timeoutMs)
+            throws ExecutionException, TimeoutException, InterruptedException {
+        mRunner.start(mGraph);
+        mProcessResult.get(timeoutMs, TimeUnit.MILLISECONDS);
+    }
+
+    protected void process() throws ExecutionException, TimeoutException, InterruptedException {
+        process(DEFAULT_TIMEOUT_MS);
+    }
+
+    /**
+     * This method should be called to create the input frames inside the test cases (instead of
+     * {@link Frame#create(FrameType, int[])}). This is required to work around a requirement for
+     * the latter method to be called on the MFF thread.
+     */
+    protected Frame createFrame(FrameType type, int[] dimensions) {
+        return new Frame(type, dimensions, mRunner.getFrameManager());
+    }
+
+    private void connectInputPorts(
+            MffContext mffContext, FilterGraph.Builder graphBuilder, Filter filter) {
+        Signature signature = filter.getSignature();
+        for (Entry<String, PortInfo> inputPortEntry : signature.getInputPorts().entrySet()) {
+            Filter inputFilter = new FrameSourceFilter(mffContext, "in_" + inputPortEntry.getKey());
+            graphBuilder.addFilter(inputFilter);
+            graphBuilder.connect(inputFilter, "output", filter, inputPortEntry.getKey());
+        }
+    }
+
+    private void connectOutputPorts(
+            MffContext mffContext, FilterGraph.Builder graphBuilder, Filter filter) {
+        Signature signature = filter.getSignature();
+        mEmptyOutputPorts = new HashSet<String>();
+        OutputFrameListener outputFrameListener = new OutputFrameListener();
+        for (Entry<String, PortInfo> outputPortEntry : signature.getOutputPorts().entrySet()) {
+            FrameTargetFilter outputFilter = new FrameTargetFilter(
+                    mffContext, "out_" + outputPortEntry.getKey());
+            graphBuilder.addFilter(outputFilter);
+            graphBuilder.connect(filter, outputPortEntry.getKey(), outputFilter, "input");
+            outputFilter.setListener(outputFrameListener);
+            mEmptyOutputPorts.add("out_" + outputPortEntry.getKey());
+        }
+    }
+
+    private class OutputFrameListener implements FrameTargetFilter.Listener {
+
+        @Override
+        public void onFramePushed(String filterName, Frame frame) {
+            mOutputFrames.put(filterName, frame);
+            boolean alreadyPushed = !mEmptyOutputPorts.remove(filterName);
+            if (alreadyPushed) {
+                throw new IllegalStateException(
+                        "A frame has been pushed twice to the same output port.");
+            }
+            if (mEmptyOutputPorts.isEmpty()) {
+                // All outputs have been pushed, stop the graph.
+                mRunner.stop();
+            }
+        }
+
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffTestCase.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffTestCase.java
new file mode 100644
index 0000000..2f33a5c
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/MffTestCase.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+/**
+ * A {@link TestCase} for testing objects requiring {@link MffContext}. This test case can only be
+ * used to test the functionality that does not rely on GL support and camera.
+ */
+public class MffTestCase extends AndroidTestCase {
+
+    private HandlerThread mMffContextHandlerThread;
+    private MffContext mMffContext;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // MffContext needs to be created on a separate thread to allow MFF to post Runnable's.
+        mMffContextHandlerThread = new HandlerThread("MffContextThread");
+        mMffContextHandlerThread.start();
+        Handler handler = new Handler(mMffContextHandlerThread.getLooper());
+        FutureTask<MffContext> task = new FutureTask<MffContext>(new Callable<MffContext>() {
+            @Override
+            public MffContext call() throws Exception {
+                MffContext.Config config = new MffContext.Config();
+                config.requireCamera = false;
+                config.requireOpenGL = false;
+                config.forceNoGL = true;
+                return new MffContext(getContext(), config);
+            }
+        });
+        handler.post(task);
+        // Wait for the context to be created on the handler thread.
+        mMffContext = task.get();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mMffContextHandlerThread.getLooper().quit();
+        mMffContextHandlerThread = null;
+        mMffContext.release();
+        mMffContext = null;
+        super.tearDown();
+    }
+
+    protected MffContext getMffContext() {
+        return mMffContext;
+    }
+
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AverageFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AverageFilterTest.java
new file mode 100644
index 0000000..37b5eb8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AverageFilterTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+
+public class AverageFilterTest extends MffFilterTestCase {
+
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        return new AverageFilter(mffContext, "averageFilter");
+    }
+
+    public void testAverageFilter() throws Exception {
+        FrameValue frame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        frame.setValue(5f);
+
+        injectInputFrame("sharpness", frame);
+
+        process();
+        assertEquals(1f, ((Float) getOutputFrame("avg").asFrameValue().getValue()).floatValue(),
+                0.001f);
+    }
+
+    public void testAverageFilter2() throws Exception{
+        FrameValue frame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        frame.setValue(4f);
+
+        injectInputFrame("sharpness", frame);
+
+        process();
+        assertEquals(0.8f, ((Float) getOutputFrame("avg").asFrameValue().getValue()).floatValue(),
+                0.001f);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilterTest.java
new file mode 100644
index 0000000..3c8d127
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/AvgBrightnessFilterTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import android.net.Uri;
+import android.provider.MediaStore;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+
+public class AvgBrightnessFilterTest extends MffFilterTestCase {
+    private AssetManager assetMgr = null;
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        assetMgr = mffContext.getApplicationContext().getAssets();
+        return new AvgBrightnessFilter(mffContext, "brightnessFilter");
+    }
+
+    public void testBrightnessFilter() throws Exception{
+        final int INPUT_WIDTH = 480;
+        final int INPUT_HEIGHT = 640;
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {INPUT_WIDTH,INPUT_HEIGHT}).asFrameImage2D();
+
+        Bitmap bitmap = BitmapFactory.decodeStream(assetMgr.open("0002_000390.jpg"));
+        image.setBitmap(bitmap);
+
+        injectInputFrame("image", image);
+
+        process();
+        final float EXPECTED_RESULT = 0.35f;
+        assertEquals(EXPECTED_RESULT, ((Float) getOutputFrame("brightnessRating").
+                asFrameValue().getValue()).floatValue(), 0.01f);
+    }
+}
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilterTest.java
new file mode 100644
index 0000000..6072755
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ContrastRatioFilterTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+import androidx.media.filterfw.samples.simplecamera.ContrastRatioFilter;
+
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+
+public class ContrastRatioFilterTest extends MffFilterTestCase {
+    private AssetManager assetMgr = null;
+
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        assetMgr = mffContext.getApplicationContext().getAssets();
+        return new ContrastRatioFilter(mffContext, "contrastFilter");
+    }
+
+    public void testContrastFilter() throws Exception {
+
+        final int INPUT_WIDTH = 480;
+        final int INPUT_HEIGHT = 640;
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {INPUT_WIDTH,INPUT_HEIGHT}).asFrameImage2D();
+
+        Bitmap bitmap = BitmapFactory.decodeStream(assetMgr.open("0002_000390.jpg"));
+        image.setBitmap(bitmap);
+
+        injectInputFrame("image", image);
+
+        process();
+        final float EXPECTED_RESULT = 0.29901487f;
+        assertEquals(EXPECTED_RESULT, ((Float) getOutputFrame("contrastRating").
+                asFrameValue().getValue()).floatValue(), 0.001f);
+
+
+    }
+}
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ExposureFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ExposureFilterTest.java
new file mode 100644
index 0000000..25ac212
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ExposureFilterTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+
+public class ExposureFilterTest extends MffFilterTestCase {
+
+    private AssetManager assetMgr = null;
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        assetMgr = mffContext.getApplicationContext().getAssets();
+        return new ExposureFilter(mffContext, "exposureFilter");
+    }
+
+    public void testExposureFilter() throws Exception{
+        final int INPUT_WIDTH = 480;
+        final int INPUT_HEIGHT = 640;
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {INPUT_WIDTH,INPUT_HEIGHT}).asFrameImage2D();
+
+        Bitmap bitmap = BitmapFactory.decodeStream(assetMgr.open("0002_000390.jpg"));
+        image.setBitmap(bitmap);
+
+        injectInputFrame("image", image);
+        process();
+        final float EXPECTED_OVEREXPOSURE = 0.00757f;
+        assertEquals(EXPECTED_OVEREXPOSURE, ((Float) getOutputFrame("overExposureRating").
+                asFrameValue().getValue()).floatValue(), 0.001f);
+        final float EXPECTED_UNDEREXPOSURE = 0.2077f;
+        assertEquals(EXPECTED_UNDEREXPOSURE, ((Float) getOutputFrame("underExposureRating").
+                asFrameValue().getValue()).floatValue(), 0.001f);
+    }
+}
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilterTest.java
new file mode 100644
index 0000000..02387fe
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FaceSquareFilterTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValues;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import android.hardware.Camera;
+import android.hardware.Camera.Face;
+import android.graphics.Rect;
+
+
+public class FaceSquareFilterTest extends MffFilterTestCase {
+
+    private AssetManager assetMgr = null;
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        assetMgr = mffContext.getApplicationContext().getAssets();
+        return new FaceSquareFilter(mffContext, "faceSquareFilter");
+    }
+
+    public void testFaceSquareFilter() throws Exception{
+        final int INPUT_WIDTH = 1536;
+        final int INPUT_HEIGHT = 2048;
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {INPUT_WIDTH,INPUT_HEIGHT}).asFrameImage2D();
+
+        FrameValues facesFrame = createFrame(FrameType.array(Camera.Face.class), new int[] {1,1}).
+                asFrameValues();
+
+        Bitmap bitmap = BitmapFactory.decodeStream(assetMgr.open("XZZ019.jpg"));
+        image.setBitmap(bitmap);
+        injectInputFrame("image", image);
+
+        Face face = new Face();
+        Rect faceRect = new Rect();
+        // These are the values for image 141 with 1 face
+        faceRect.set(-533, -453, 369, 224);
+        face.rect = faceRect;
+        Face[] faces = new Face[1];
+        faces[0] = face;
+        facesFrame.setValue(faces);
+        injectInputFrame("faces", facesFrame);
+        process();
+
+        // ensure the output image has the rectangle in the right place
+        FrameImage2D outputImage = getOutputFrame("image").asFrameImage2D();
+        int[] pixels = new int[bitmap.getByteCount()];
+        bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
+                bitmap.getHeight());
+
+        final int FACE_X_RANGE = 2000;
+        final int WIDTH_OFFSET = 1000;
+        final int HEIGHT_OFFSET = 1000;
+
+        int top = (faceRect.top+HEIGHT_OFFSET)*bitmap.getHeight()/FACE_X_RANGE;
+        int bottom = (faceRect.bottom+HEIGHT_OFFSET)*bitmap.getHeight()/FACE_X_RANGE;
+        int left = (faceRect.left+WIDTH_OFFSET)*bitmap.getWidth()/FACE_X_RANGE;
+        int right = (faceRect.right+WIDTH_OFFSET)*bitmap.getWidth()/FACE_X_RANGE;
+
+        if (top < 0) {
+            top = 0;
+        } else if (top > bitmap.getHeight()) {
+            top = bitmap.getHeight();
+        }
+        if (left < 0) {
+            left = 0;
+        } else if (left > bitmap.getWidth()) {
+            left = bitmap.getWidth();
+        }
+        if (bottom > bitmap.getHeight()) {
+            bottom = bitmap.getHeight();
+        } else if (bottom < 0) {
+            bottom = 0;
+        }
+        if (right > bitmap.getWidth()) {
+            right = bitmap.getWidth();
+        } else if (right < 0) {
+            right = 0;
+        }
+
+        for (int j = 0; j < (bottom - top); j++) {
+            // Left edge
+            if (left > 0 && top > 0) {
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + left) +
+                       ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + left) +
+                       ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + left) +
+                       ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+            }
+
+            // Right edge
+            if (right > 0 && top > 0) {
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + right) +
+                       ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + right) +
+                       ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * (top + j) + right) +
+                       ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+            }
+
+        }
+        for (int k = 0; k < (right - left); k++) {
+            // Top edge
+            if (top < bitmap.getHeight()) {
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * top + left + k) +
+                       ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * top + left + k) +
+                       ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * top + left + k) +
+                       ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+
+            }
+            // Bottom edge
+            if (bottom < bitmap.getHeight()) {
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * bottom + left + k) +
+                       ImageConstants.RED_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * bottom + left + k) +
+                       ImageConstants.GREEN_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+                pixels[ImageConstants.PIX_CHANNELS * (bitmap.getWidth() * bottom + left + k) +
+                       ImageConstants.BLUE_OFFSET] = (byte) ImageConstants.MAX_BYTE;
+            }
+        }
+
+        Bitmap outputBitmap = outputImage.toBitmap();
+        int[] outputPixels = new int[outputBitmap.getByteCount()];
+        outputBitmap.getPixels(outputPixels, 0, outputBitmap.getWidth(), 0, 0,
+                outputBitmap.getWidth(), outputBitmap.getHeight());
+        int equalCount = 0;
+        for ( int i = 0; i < outputBitmap.getByteCount(); i++) {
+            if (pixels[i] == outputPixels[i])
+                equalCount++;
+        }
+
+        if (equalCount + (0.05f*outputBitmap.getByteCount()) < outputBitmap.getByteCount()) {
+            // Assertion will fail if condition is true
+            assertEquals(equalCount, outputBitmap.getByteCount());
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilterTest.java
new file mode 100644
index 0000000..0f4c6d0
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToSizeFilterTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.samples.simplecamera.FloatArrayToSizeFilter;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+
+public class FloatArrayToSizeFilterTest extends MffFilterTestCase {
+
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        return new FloatArrayToSizeFilter(mffContext, "floatArrayToSizeFilter");
+    }
+
+
+    public void testToSize() throws Exception {
+        FrameValue floatArray = createFrame(FrameType.array(float.class), new int[] { 1 }).
+                asFrameValue();
+        float[] floatArr = { 10f, 15f, 25f };
+        floatArray.setValue(floatArr);
+
+        injectInputFrame("array", floatArray);
+
+        process();
+        assertEquals(3, ((Integer) getOutputFrame("size").asFrameValue().getValue()).intValue());
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilterTest.java
new file mode 100644
index 0000000..bf6a197
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/FloatArrayToStrFilterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+import androidx.media.filterfw.samples.simplecamera.FloatArrayToStrFilter;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+
+public class FloatArrayToStrFilterTest extends MffFilterTestCase {
+
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        return new FloatArrayToStrFilter(mffContext, "floatArrayToStrFilter");
+    }
+
+    public void testToStr() throws Exception {
+        FrameValue floatArray = createFrame(FrameType.array(float.class), new int[] { 1 }).
+                asFrameValue();
+        float[] floatArr = { 10f, 15f, 25f };
+        floatArray.setValue(floatArr);
+
+        injectInputFrame("array", floatArray);
+
+        process();
+
+        assertEquals("[10.0, 15.0, 25.0]", (String) getOutputFrame("string").asFrameValue().
+                getValue());
+    }
+}
\ No newline at end of file
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/IfElseFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/IfElseFilterTest.java
new file mode 100644
index 0000000..30835ea
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/IfElseFilterTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameImage2D;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+public class IfElseFilterTest extends MffFilterTestCase {
+    private final static int BIG_INPUT_WIDTH = 1536;
+    private final static int BIG_INPUT_HEIGHT = 2048;
+    private final static int SMALL_INPUT_WIDTH = 480;
+    private final static int SMALL_INPUT_HEIGHT = 640;
+
+    private AssetManager assetMgr = null;
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        assetMgr = mffContext.getApplicationContext().getAssets();
+        return new IfElseFilter(mffContext, "ifElseFilter");
+    }
+
+    public void testIfElseFilterTrue() throws Exception {
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {BIG_INPUT_WIDTH,BIG_INPUT_HEIGHT}).asFrameImage2D();
+        FrameImage2D video =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {SMALL_INPUT_WIDTH,SMALL_INPUT_HEIGHT}).asFrameImage2D();
+
+        // Image of legs
+        Bitmap videoBitmap = BitmapFactory.decodeStream(assetMgr.open("0002_000390.jpg"));
+        // Image of a face
+        Bitmap imageBitmap = BitmapFactory.decodeStream(assetMgr.open("XZZ019.jpg"));
+
+        image.setBitmap(imageBitmap);
+        injectInputFrame("falseResult", image);
+        video.setBitmap(videoBitmap);
+        injectInputFrame("trueResult", video);
+
+        FrameValue conditionFrame = createFrame(FrameType.single(boolean.class), new int[] {1}).
+                asFrameValue();
+        conditionFrame.setValue(true);
+        injectInputFrame("condition", conditionFrame);
+
+        process();
+
+        // Ensure that for true, we use the video input
+        FrameImage2D outputImage = getOutputFrame("output").asFrameImage2D();
+        assertEquals(outputImage, video);
+    }
+
+    public void testIfElseFilterFalse() throws Exception {
+
+        FrameImage2D image =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {BIG_INPUT_WIDTH,BIG_INPUT_HEIGHT}).asFrameImage2D();
+        FrameImage2D video =
+                createFrame(FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_CPU),
+                        new int[] {SMALL_INPUT_WIDTH,SMALL_INPUT_HEIGHT}).asFrameImage2D();
+
+        // Image of legs
+        Bitmap videoBitmap = BitmapFactory.decodeStream(assetMgr.open("0002_000390.jpg"));
+        // Image of a face
+        Bitmap imageBitmap = BitmapFactory.decodeStream(assetMgr.open("XZZ019.jpg"));
+
+        image.setBitmap(imageBitmap);
+        injectInputFrame("falseResult", image);
+        video.setBitmap(videoBitmap);
+        injectInputFrame("trueResult", video);
+
+
+        FrameValue conditionFrame = createFrame(FrameType.single(boolean.class), new int[] {1}).
+                asFrameValue();
+        conditionFrame.setValue(false);
+        injectInputFrame("condition", conditionFrame);
+
+        process();
+
+        // Ensure that for true, we use the video input
+        FrameImage2D outputImage = getOutputFrame("output").asFrameImage2D();
+        assertEquals(outputImage, image);
+    }
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilterTest.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilterTest.java
new file mode 100644
index 0000000..43bc090
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/src/androidx/media/filterfw/samples/simplecamera/ImageGoodnessFilterTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013 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 androidx.media.filterfw.samples.simplecamera;
+
+import androidx.media.filterfw.Filter;
+import androidx.media.filterfw.FrameType;
+import androidx.media.filterfw.FrameValue;
+import androidx.media.filterfw.MffContext;
+import androidx.media.filterfw.MffFilterTestCase;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+public class ImageGoodnessFilterTest extends MffFilterTestCase {
+
+    String AWFUL_STRING = "Awful Picture";
+    String BAD_STRING  = "Bad Picture";
+    String OK_STRING = "Ok Picture";
+    String GOOD_STRING = "Good Picture!";
+    String GREAT_STRING = "Great Picture!";
+    @Override
+    protected Filter createFilter(MffContext mffContext) {
+        return new ImageGoodnessFilter(mffContext, "goodnessFilter");
+    }
+
+    public void testAwfulPicture() throws Exception {
+        FrameValue sharpnessFrame = createFrame(FrameType.single(), new int[] { 1 }).
+                asFrameValue();
+        sharpnessFrame.setValue(10f);
+        FrameValue oEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        oEFrame.setValue(0.39f);
+        FrameValue uEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        uEFrame.setValue(0.25f);
+        FrameValue colorFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        colorFrame.setValue(2.1f);
+        FrameValue contrastFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        contrastFrame.setValue(0.18f);
+        FrameValue motionFrame = createFrame(FrameType.array(), new int[] { 1 }).asFrameValue();
+        float[] motionFloatArray = { 9.0f, 3.0f, 2.0f };
+        motionFrame.setValue(motionFloatArray);
+
+        injectInputFrame("sharpness", sharpnessFrame);
+        injectInputFrame("overExposure", oEFrame);
+        injectInputFrame("underExposure", uEFrame);
+        injectInputFrame("colorfulness", colorFrame);
+        injectInputFrame("contrastRating", contrastFrame);
+        injectInputFrame("motionValues", motionFrame);
+
+        process();
+        assertEquals("Awful Picture", (String) getOutputFrame("goodOrBadPic").asFrameValue().
+                getValue());
+    }
+
+    public void testBadPicture() throws Exception {
+        FrameValue sharpnessFrame = createFrame(FrameType.single(), new int[] { 1 }).
+                asFrameValue();
+        sharpnessFrame.setValue(10f);
+        FrameValue oEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        oEFrame.setValue(0.39f);
+        FrameValue uEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        uEFrame.setValue(0.25f);
+        FrameValue colorFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        colorFrame.setValue(2.1f);
+        FrameValue contrastFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        contrastFrame.setValue(0.18f);
+        FrameValue motionFrame = createFrame(FrameType.array(), new int[] { 1 }).asFrameValue();
+        float[] motionFloatArray = { 0.0f, 0.0f, 0.0f };
+        motionFrame.setValue(motionFloatArray);
+
+        injectInputFrame("sharpness", sharpnessFrame);
+        injectInputFrame("overExposure", oEFrame);
+        injectInputFrame("underExposure", uEFrame);
+        injectInputFrame("colorfulness", colorFrame);
+        injectInputFrame("contrastRating", contrastFrame);
+        injectInputFrame("motionValues", motionFrame);
+
+        process();
+        assertEquals("Bad Picture", (String) getOutputFrame("goodOrBadPic").asFrameValue().
+                getValue());
+    }
+
+    public void testOkPicture() throws Exception {
+        FrameValue sharpnessFrame = createFrame(FrameType.single(), new int[] { 1 }).
+                asFrameValue();
+        sharpnessFrame.setValue(30f);
+        FrameValue oEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        oEFrame.setValue(0.39f);
+        FrameValue uEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        uEFrame.setValue(0.25f);
+        FrameValue colorFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        colorFrame.setValue(2.1f);
+        FrameValue contrastFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        contrastFrame.setValue(0.18f);
+        FrameValue motionFrame = createFrame(FrameType.array(), new int[] { 1 }).asFrameValue();
+        float[] motionFloatArray = { 0.0f, 0.0f, 0.0f };
+        motionFrame.setValue(motionFloatArray);
+
+        injectInputFrame("sharpness", sharpnessFrame);
+        injectInputFrame("overExposure", oEFrame);
+        injectInputFrame("underExposure", uEFrame);
+        injectInputFrame("colorfulness", colorFrame);
+        injectInputFrame("contrastRating", contrastFrame);
+        injectInputFrame("motionValues", motionFrame);
+
+        process();
+        assertEquals("Ok Picture", (String) getOutputFrame("goodOrBadPic").asFrameValue().
+                getValue());
+    }
+
+    public void testGoodPicture() throws Exception {
+        FrameValue sharpnessFrame = createFrame(FrameType.single(), new int[] { 1 }).
+                asFrameValue();
+        sharpnessFrame.setValue(50f);
+        FrameValue oEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        oEFrame.setValue(0.01f);
+        FrameValue uEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        uEFrame.setValue(0.01f);
+        FrameValue colorFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        colorFrame.setValue(2.1f);
+        FrameValue contrastFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        contrastFrame.setValue(0.18f);
+        FrameValue motionFrame = createFrame(FrameType.array(), new int[] { 1 }).asFrameValue();
+        float[] motionFloatArray = { 0.0f, 0.0f, 0.0f };
+        motionFrame.setValue(motionFloatArray);
+
+        injectInputFrame("sharpness", sharpnessFrame);
+        injectInputFrame("overExposure", oEFrame);
+        injectInputFrame("underExposure", uEFrame);
+        injectInputFrame("colorfulness", colorFrame);
+        injectInputFrame("contrastRating", contrastFrame);
+        injectInputFrame("motionValues", motionFrame);
+
+        process();
+        assertEquals("Good Picture!", (String) getOutputFrame("goodOrBadPic").asFrameValue().
+                getValue());
+    }
+
+    public void testGreatPicture() throws Exception {
+        FrameValue sharpnessFrame = createFrame(FrameType.single(), new int[] { 1 }).
+                asFrameValue();
+        sharpnessFrame.setValue(50f);
+        FrameValue oEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        oEFrame.setValue(0.01f);
+        FrameValue uEFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        uEFrame.setValue(0.02f);
+        FrameValue colorFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        colorFrame.setValue(2.1f);
+        FrameValue contrastFrame = createFrame(FrameType.single(), new int[] { 1 }).asFrameValue();
+        contrastFrame.setValue(0.25f);
+        FrameValue motionFrame = createFrame(FrameType.array(), new int[] { 1 }).asFrameValue();
+        float[] motionFloatArray = { 0.0f, 0.0f, 0.0f };
+        motionFrame.setValue(motionFloatArray);
+
+        injectInputFrame("sharpness", sharpnessFrame);
+        injectInputFrame("overExposure", oEFrame);
+        injectInputFrame("underExposure", uEFrame);
+        injectInputFrame("colorfulness", colorFrame);
+        injectInputFrame("contrastRating", contrastFrame);
+        injectInputFrame("motionValues", motionFrame);
+
+        process();
+        assertEquals("Great Picture!", (String) getOutputFrame("goodOrBadPic").asFrameValue().
+                getValue());
+    }
+}
diff --git a/tests/CoreTests/android/core/HttpHeaderTest.java b/tests/CoreTests/android/core/HttpHeaderTest.java
index eedbc3f..cc7c34c 100644
--- a/tests/CoreTests/android/core/HttpHeaderTest.java
+++ b/tests/CoreTests/android/core/HttpHeaderTest.java
@@ -20,10 +20,6 @@
 
 import android.net.http.Headers;
 import android.util.Log;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-
-import java.lang.reflect.Method;
 
 public class HttpHeaderTest extends AndroidTestCase {
 
@@ -67,38 +63,4 @@
         assertEquals("max-age=15,private", h.getCacheControl());
     }
 
-    // Test that cache behaves correctly when receiving a compund
-    // cache-control statement containing no-cache and max-age argument.
-    //
-    // If a cache control header contains both a max-age arument and
-    // a no-cache argument the max-age argument should be ignored.
-    // The resource can be cached, but a validity check must be done on
-    // every request. Test case checks that the expiry time is 0 for
-    // this item, so item will be validated on subsequent requests.
-    public void testCacheControlMultipleArguments() throws Exception {
-        // get private method CacheManager.parseHeaders()
-        Method m = CacheManager.class.getDeclaredMethod("parseHeaders",
-                new Class[] {int.class, Headers.class, String.class});
-        m.setAccessible(true);
-
-        // create indata
-        Headers h = new Headers();
-        CharArrayBuffer buffer = new CharArrayBuffer(64);
-        buffer.append(CACHE_CONTROL_COMPOUND);
-        h.parseHeader(buffer);
-
-        CacheResult c = (CacheResult)m.invoke(null, 200, h, "text/html");
-
-        // Check that expires is set to 0, to ensure that no-cache has overridden
-        // the max-age argument
-        assertEquals(0, c.getExpires());
-
-        // check reverse order
-        buffer.clear();
-        buffer.append(CACHE_CONTROL_COMPOUND2);
-        h.parseHeader(buffer);
-
-        c = (CacheResult)m.invoke(null, 200, h, "text/html");
-        assertEquals(0, c.getExpires());
-    }
 }
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png
new file mode 100644
index 0000000..829378e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
index bd44b52..931daed 100644
--- a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png
new file mode 100644
index 0000000..2773a70
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
index c629387..6e1ac91 100644
--- a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/status_bar.xml b/tools/layoutlib/bridge/resources/bars/status_bar.xml
index d3c492e..51b474d 100644
--- a/tools/layoutlib/bridge/resources/bars/status_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/status_bar.xml
@@ -6,10 +6,12 @@
 			android:layout_weight="1"/>
 	<ImageView
 			android:layout_height="wrap_content"
-			android:layout_width="wrap_content"/>
+			android:layout_width="wrap_content"
+			android:layout_marginTop="1dp"/>
 	<ImageView
 			android:layout_height="wrap_content"
 			android:layout_width="wrap_content"
-			android:layout_marginLeft="3dip"
-			android:layout_marginRight="5dip"/>
+			android:layout_marginLeft="3dp"
+			android:layout_marginRight="5dp"
+			android:layout_marginTop="1dp"/>
 </merge>
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png
new file mode 100644
index 0000000..c7fd719
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..625c61d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index be27b54..fa68796 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -48,6 +48,11 @@
  */
 public final class NinePatch_Delegate {
 
+    // ---- delegate manager ----
+    private static final DelegateManager<NinePatch_Delegate> sManager =
+            new DelegateManager<NinePatch_Delegate>(NinePatch_Delegate.class);
+
+    // ---- delegate helper data ----
     /**
      * Cache map for {@link NinePatchChunk}.
      * When the chunks are created they are serialized into a byte[], and both are put
@@ -60,6 +65,10 @@
     private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
         new HashMap<byte[], SoftReference<NinePatchChunk>>();
 
+    // ---- delegate data ----
+    private byte[] chunk;
+
+
     // ---- Public Helper methods ----
 
     /**
@@ -149,32 +158,39 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
+    /*package*/ static int validateNinePatchChunk(int bitmap, byte[] chunk) {
         // the default JNI implementation only checks that the byte[] has the same
         // size as the C struct it represent. Since we cannot do the same check (serialization
         // will return different size depending on content), we do nothing.
+        NinePatch_Delegate newDelegate = new NinePatch_Delegate();
+        newDelegate.chunk = chunk;
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    /*package*/ static void nativeFinalize(int chunk) {
+        sManager.removeJavaReferenceFor(chunk);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
-            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+            int chunk, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
                 (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
-                bitmap_instance, c, paint_instance_or_null,
+                bitmap_instance, chunk, paint_instance_or_null,
                 destDensity, srcDensity);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
-            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+            int chunk, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
                 loc.left, loc.top, loc.width(), loc.height(),
-                bitmap_instance, c, paint_instance_or_null,
+                bitmap_instance, chunk, paint_instance_or_null,
                 destDensity, srcDensity);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
+    /*package*/ static int nativeGetTransparentRegion(int bitmap, int chunk, Rect location) {
         return 0;
     }
 
@@ -182,7 +198,7 @@
 
     private static void draw(int canvas_instance,
             final int left, final int top, final int right, final int bottom,
-            int bitmap_instance, byte[] c, int paint_instance_or_null,
+            int bitmap_instance, int chunk, int paint_instance_or_null,
             final int destDensity, final int srcDensity) {
         // get the delegate from the native int.
         final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
@@ -190,6 +206,11 @@
             return;
         }
 
+        byte[] c = null;
+        NinePatch_Delegate delegate = sManager.getDelegate(chunk);
+        if (delegate != null) {
+            c = delegate.chunk;
+        }
         if (c == null) {
             // not a 9-patch?
             BufferedImage image = bitmap_delegate.getImage();
diff --git a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
new file mode 100644
index 0000000..15cd687
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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.text.format;
+
+import java.util.Calendar;
+import java.util.UnknownFormatConversionException;
+import java.util.regex.Pattern;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide new implementation for native methods of {@link Time}
+ *
+ * Through the layoutlib_create tool, some native methods of Time have been replaced by calls to
+ * methods of the same name in this delegate class.
+ */
+public class Time_Delegate {
+
+    // Regex to match odd number of '%'.
+    private static final Pattern p = Pattern.compile("(?<!%)(%%)*%(?!%)");
+
+    @LayoutlibDelegate
+    /*package*/ static String format1(Time thisTime, String format) {
+
+        try {
+            // Change the format by adding changing '%' to "%1$t". This is required to tell the
+            // formatter which argument to use from the argument list. '%%' is left as is. In the
+            // replacement string, $0 refers to matched pattern. \\1 means '1', written this way to
+            // separate it from 0. \\$ means '$', written this way to suppress the special meaning
+            // of $.
+            return String.format(
+                    p.matcher(format).replaceAll("$0\\1\\$t"),
+                    timeToCalendar(thisTime, Calendar.getInstance()));
+        } catch (UnknownFormatConversionException e) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_STRFTIME, "Unrecognized format", e, format);
+            return format;
+        }
+    }
+
+    private static Calendar timeToCalendar(Time time, Calendar calendar) {
+        calendar.set(time.year, time.month, time.monthDay, time.hour, time.minute, time.second);
+        return calendar;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 9a633bf..7e5ae8d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -17,6 +17,7 @@
 
 import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.DisplayAdjustments;
 import android.view.DisplayInfo;
 import android.view.View;
 import android.view.WindowManager;
@@ -32,7 +33,8 @@
         DisplayInfo info = new DisplayInfo();
         info.logicalHeight = mMetrics.heightPixels;
         info.logicalWidth = mMetrics.widthPixels;
-        mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info, null);
+        mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index baa956d..3692d96 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -45,10 +45,7 @@
         // We do know the order though.
         // 0 is the spacer
         loadIcon(1, "stat_sys_wifi_signal_4_fully.png", density);
-        Drawable drawable = loadIcon(2, ResourceType.DRAWABLE, "stat_sys_battery_charge");
-        if (drawable instanceof LevelListDrawable) {
-            ((LevelListDrawable) drawable).setLevel(100);
-        }
+        loadIcon(2, "stat_sys_battery_charge_anim100.png", density);
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 87047b3..23d08e3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -248,11 +248,16 @@
      * The counterpart is {@link #setUp()}.
      */
     private void tearDown() {
-        // Make sure to remove static references, otherwise we could not unload the lib
-        mContext.disposeResources();
+        // The context may be null, if there was an error during init().
+        if (mContext != null) {
+            // Make sure to remove static references, otherwise we could not unload the lib
+            mContext.disposeResources();
+        }
 
-        // quit HandlerThread created during this session.
-        HandlerThread_Delegate.cleanUp(sCurrentContext);
+        if (sCurrentContext != null) {
+            // quit HandlerThread created during this session.
+            HandlerThread_Delegate.cleanUp(sCurrentContext);
+        }
 
         // clear the stored ViewConfiguration since the map is per density and not per context.
         ViewConfiguration_Accessor.clearConfigurations();
@@ -263,8 +268,12 @@
         sCurrentContext = null;
 
         Bridge.setLog(null);
-        mContext.getRenderResources().setFrameworkResourceIdProvider(null);
-        mContext.getRenderResources().setLogger(null);
+        if (mContext != null) {
+            mContext.getRenderResources().setFrameworkResourceIdProvider(null);
+            mContext.getRenderResources().setLogger(null);
+        }
+
+        mContext = null;
     }
 
     public static BridgeContext getCurrentContext() {
diff --git a/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java
new file mode 100644
index 0000000..a773d93
--- /dev/null
+++ b/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 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 libcore.icu;
+
+import java.text.FieldPosition;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.ibm.icu.text.DateIntervalFormat;
+import com.ibm.icu.util.DateInterval;
+import com.ibm.icu.util.TimeZone;
+import com.ibm.icu.util.ULocale;
+
+public class DateIntervalFormat_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<DateIntervalFormat_Delegate> sManager =
+            new DelegateManager<DateIntervalFormat_Delegate>(DateIntervalFormat_Delegate.class);
+
+    // ---- delegate data ----
+    private DateIntervalFormat mFormat;
+
+
+    // ---- native methods ----
+
+    /*package*/static String formatDateInterval(long address, long fromDate, long toDate) {
+        DateIntervalFormat_Delegate delegate = sManager.getDelegate((int)address);
+        if (delegate == null) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Unable for find native DateIntervalFormat", null);
+            return null;
+        }
+        DateInterval interval = new DateInterval(fromDate, toDate);
+        StringBuffer sb = new StringBuffer();
+        FieldPosition pos = new FieldPosition(0);
+        delegate.mFormat.format(interval, sb, pos);
+        return sb.toString();
+    }
+
+    /*package*/ static long createDateIntervalFormat(String skeleton, String localeName,
+            String tzName) {
+        TimeZone prevDefaultTz = TimeZone.getDefault();
+        TimeZone.setDefault(TimeZone.getTimeZone(tzName));
+        DateIntervalFormat_Delegate newDelegate = new DateIntervalFormat_Delegate();
+        newDelegate.mFormat =
+                DateIntervalFormat.getInstance(skeleton, new ULocale(localeName));
+        TimeZone.setDefault(prevDefaultTz);
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    /*package*/ static void destroyDateIntervalFormat(long address) {
+        sManager.removeJavaReferenceFor((int)address);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index cd4f82b..06ae804 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -17,6 +17,8 @@
 package libcore.icu;
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.util.ULocale;
 
 import java.util.Locale;
 
@@ -45,7 +47,8 @@
 
     @LayoutlibDelegate
     /*package*/ static String getBestDateTimePattern(String skeleton, String localeName) {
-        return "";            // TODO: check what the right value should be.
+        return DateTimePatternGenerator.getInstance(new ULocale(localeName))
+                .getBestPattern(skeleton);
     }
 
     @LayoutlibDelegate
@@ -181,12 +184,18 @@
         result.longStandAloneMonthNames = result.longMonthNames;
         result.shortStandAloneMonthNames = result.shortMonthNames;
 
+        // The platform code expects this to begin at index 1, rather than 0. It maps it directly to
+        // the constants from java.util.Calendar.<weekday>
         result.longWeekdayNames = new String[] {
-                "Monday" ,"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
+                "", "Sunday", "Monday" ,"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
         result.shortWeekdayNames = new String[] {
-                "Mon" ,"Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+                "", "Sun", "Mon" ,"Tue", "Wed", "Thu", "Fri", "Sat" };
+        result.tinyWeekdayNames = new String[] {
+                "", "S", "M", "T", "W", "T", "F", "S" };
+
         result.longStandAloneWeekdayNames = result.longWeekdayNames;
         result.shortStandAloneWeekdayNames = result.shortWeekdayNames;
+        result.tinyStandAloneWeekdayNames = result.tinyWeekdayNames;
 
         result.fullTimeFormat = "";
         result.longTimeFormat = "";
diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath
index dbc4cfd..cd8bb0d 100644
--- a/tools/layoutlib/create/.classpath
+++ b/tools/layoutlib/create/.classpath
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry excluding="mock_android/" kind="src" path="tests"/>
+	<classpathentry excluding="mock_data/" kind="src" path="tests"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_PLAT/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 894611b..ef2b185 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -71,6 +71,9 @@
 and "." and "$" are interpreted as-is).
 In practice we almost but not quite request the inclusion of full packages.
 
+The analyzer is also given a list of classes to exclude. A fake implementation of these
+classes is injected by the Generator.
+
 With this information, the analyzer parses the input zip to find all the classes.
 All classes deriving from the requested bases classes are kept.
 All classes which name matched the glob pattern are kept.
@@ -93,6 +96,7 @@
 - specific methods for which to delegate calls.
 - specific methods to remove based on their return type.
 - specific classes to rename.
+- specific classes to refactor.
 
 Each of these are specific strategies we use to be able to modify the Android code
 to fit within the Eclipse renderer. These strategies are explained beow.
@@ -100,10 +104,7 @@
 The core method of the generator is transform(): it takes an input ASM ClassReader
 and modifies it to produce a byte array suitable for the final JAR file.
 
-The first step of the transformation is changing the name of the class in case
-we requested the class to be renamed. This uses the RenameClassAdapter to also rename
-all inner classes and references in methods and types. Note that other classes are
-not transformed and keep referencing the original name.
+The first step of the transformation is to implement the method delegates.
 
 The TransformClassAdapter is then used to process the potentially renamed class.
 All protected or private classes are market as public.
@@ -115,11 +116,25 @@
 The code of the methods is then kept as-is, except for native methods which are
 replaced by a stub. Methods that are to be overridden are also replaced by a stub.
 
-The transformed class is then fed through the DelegateClassAdapter to implement
-method delegates. 
-
 Finally fields are also visited and changed from protected/private to public.
 
+The next step of the transformation is changing the name of the class in case
+we requested the class to be renamed. This uses the RenameClassAdapter to also rename
+all inner classes and references in methods and types. Note that other classes are
+not transformed and keep referencing the original name.
+
+The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but
+updates the references in all classes. This is used to update the references of classes
+in the java package that were added in the Dalvik VM but are not a part of the standard
+JVM. The existing classes are modified to update all references to these non-standard
+classes. An alternate implementation of these (com.android.tools.layoutlib.java.*) is
+injected.
+
+The ClassAdapters are chained together to achieve the desired output. (Look at section
+2.2.7 Transformation chains in the asm user guide, link in the References.) The order of
+execution of these is:
+ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
+RefactorClassAdapter -> ClassWriter
 
 - Method stubs
 --------------
@@ -141,19 +156,27 @@
 - Strategies
 ------------
 
-We currently have 4 strategies to deal with overriding the rendering code
+We currently have 6 strategies to deal with overriding the rendering code
 and make it run in Eclipse. Most of these strategies are implemented hand-in-hand
 by the bridge (which runs in Eclipse) and the generator.
 
 
 1- Class Injection
 
-This is the easiest: we currently inject 4 classes, namely:
+This is the easiest: we currently inject the following classes:
 - OverrideMethod and its associated MethodListener and MethodAdapter are used
   to intercept calls to some specific methods that are stubbed out and change
   their return value.
 - CreateInfo class, which configured the generator. Not used yet, but could
   in theory help us track what the generator changed.
+- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new
+  classes are injected. The implementation for these classes has been taken from
+  Android's libcore (platform/libcore/luni/src/main/java/java/...).
+- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM.
+  They are added to the Dalvik VM for performance reasons. An implementation that is very
+  close to the original (which is at platform/libcore/luni/src/main/java/...) is injected.
+  Since these classees were in part of the java package, where we can't inject classes,
+  all references to these have been updated (See strategy 4- Refactoring Classes).
 
 
 2- Overriding methods
@@ -189,7 +212,15 @@
 This won't rename/replace the inner static methods of a given class.
 
 
-4- Method erasure based on return type
+4- Refactoring classes
+
+This is very similar to the Renaming classes except that it also updates the reference in
+all classes. This is done for classes which are added to the Dalvik VM for performance
+reasons but are not present in the Standard Java VM. An implementation for these classes
+is also injected.
+
+
+5- Method erasure based on return type
 
 This is mostly an implementation detail of the bridge: in the Paint class
 mentioned above, some inner static classes are used to pass around
@@ -201,7 +232,7 @@
 bridge will provide its own implementation.
 
 
-5- Method Delegates
+6- Method Delegates
 
 This strategy is used to override method implementations.
 Given a method SomeClass.MethodName(), 1 or 2 methods are generated:
@@ -233,7 +264,7 @@
   http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
 
 ASM user guide:
-  http://download.forge.objectweb.org/asm/asm-guide.pdf
+  http://download.forge.objectweb.org/asm/asm4-guide.pdf
 
 
 --
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
new file mode 100644
index 0000000..b2caa25
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+/**
+ * Provides the common code for RenameClassAdapter and RefactorClassAdapter. It
+ * goes through the complete class and finds references to other classes. It
+ * then calls {@link #renameInternalType(String)} to convert the className to
+ * the new value, if need be.
+ */
+public abstract class AbstractClassAdapter extends ClassVisitor {
+
+    /**
+     * Returns the new FQCN for the class, if the reference to this class needs
+     * to be updated. Else, it returns the same string.
+     * @param name Old FQCN
+     * @return New FQCN if it needs to be renamed, else the old FQCN
+     */
+    abstract String renameInternalType(String name);
+
+    public AbstractClassAdapter(ClassVisitor cv) {
+        super(Opcodes.ASM4, cv);
+    }
+
+    /**
+     * Renames a type descriptor, e.g. "Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    String renameTypeDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        return renameType(Type.getType(desc));
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    String renameType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            return "L" + renameInternalType(in) + ";";
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return sb.toString();
+        }
+        return type.getDescriptor();
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;".
+     * This is like renameType() except that it returns a Type object.
+     * If the type doesn't need to be renamed, returns the input type object.
+     */
+    Type renameTypeAsType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            String newIn = renameInternalType(in);
+            if (newIn != in) {
+                return Type.getType("L" + newIn + ";");
+            }
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return Type.getType(sb.toString());
+        }
+        return type;
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    String renameMethodDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        Type[] args = Type.getArgumentTypes(desc);
+
+        StringBuilder sb = new StringBuilder("(");
+        for (Type arg : args) {
+            String name = renameType(arg);
+            sb.append(name);
+        }
+        sb.append(')');
+
+        Type ret = Type.getReturnType(desc);
+        String name = renameType(ret);
+        sb.append(name);
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Renames the ClassSignature handled by ClassVisitor.visit
+     * or the MethodTypeSignature handled by ClassVisitor.visitMethod.
+     */
+    String renameTypeSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.accept(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+
+    /**
+     * Renames the FieldTypeSignature handled by ClassVisitor.visitField
+     * or MethodVisitor.visitLocalVariable.
+     */
+    String renameFieldSignature(String sig) {
+        return renameTypeSignature(sig);
+    }
+
+
+    //----------------------------------
+    // Methods from the ClassAdapter
+
+    @Override
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        name = renameInternalType(name);
+        superName = renameInternalType(superName);
+        signature = renameTypeSignature(signature);
+        if (interfaces != null) {
+            for (int i = 0; i < interfaces.length; ++i) {
+                interfaces[i] = renameInternalType(interfaces[i]);
+            }
+        }
+
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        name = renameInternalType(name);
+        outerName = renameInternalType(outerName);
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {
+        super.visitOuterClass(renameInternalType(owner), name, renameTypeDesc(desc));
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        desc = renameMethodDesc(desc);
+        signature = renameTypeSignature(signature);
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        return new RenameMethodAdapter(mw);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        desc = renameTypeDesc(desc);
+        return super.visitAnnotation(desc, visible);
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        desc = renameTypeDesc(desc);
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+
+    //----------------------------------
+
+    /**
+     * A method visitor that renames all references from an old class name to a new class name.
+     */
+    public class RenameMethodAdapter extends MethodVisitor {
+
+        /**
+         * Creates a method visitor that renames all references from a given old name to a given new
+         * name. The method visitor will also rename all inner classes.
+         * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+         */
+        public RenameMethodAdapter(MethodVisitor mv) {
+            super(Opcodes.ASM4, mv);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitAnnotation(desc, visible);
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitParameterAnnotation(parameter, desc, visible);
+        }
+
+        @Override
+        public void visitTypeInsn(int opcode, String type) {
+            // The type sometimes turns out to be a type descriptor. We try to detect it and fix.
+            if (type.indexOf(';') > 0) {
+                type = renameTypeDesc(type);
+            } else {
+                type = renameInternalType(type);
+            }
+            super.visitTypeInsn(opcode, type);
+        }
+
+        @Override
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameTypeDesc(desc);
+
+            super.visitFieldInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            // The owner sometimes turns out to be a type descriptor. We try to detect it and fix.
+            if (owner.indexOf(';') > 0) {
+                owner = renameTypeDesc(owner);
+            } else {
+                owner = renameInternalType(owner);
+            }
+            desc = renameMethodDesc(desc);
+
+            super.visitMethodInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitLdcInsn(Object cst) {
+            // If cst is a Type, this means the code is trying to pull the .class constant
+            // for this class, so it needs to be renamed too.
+            if (cst instanceof Type) {
+                cst = renameTypeAsType((Type) cst);
+            }
+            super.visitLdcInsn(cst);
+        }
+
+        @Override
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            desc = renameTypeDesc(desc);
+
+            super.visitMultiANewArrayInsn(desc, dims);
+        }
+
+        @Override
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            type = renameInternalType(type);
+
+            super.visitTryCatchBlock(start, end, handler, type);
+        }
+
+        @Override
+        public void visitLocalVariable(String name, String desc, String signature,
+                Label start, Label end, int index) {
+            desc = renameTypeDesc(desc);
+            signature = renameFieldSignature(signature);
+
+            super.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+
+    }
+
+    //----------------------------------
+
+    public class RenameSignatureAdapter extends SignatureVisitor {
+
+        private final SignatureVisitor mSv;
+
+        public RenameSignatureAdapter(SignatureVisitor sv) {
+            super(Opcodes.ASM4);
+            mSv = sv;
+        }
+
+        @Override
+        public void visitClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitClassType(name);
+        }
+
+        @Override
+        public void visitInnerClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitInnerClassType(name);
+        }
+
+        @Override
+        public SignatureVisitor visitArrayType() {
+            SignatureVisitor sv = mSv.visitArrayType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitBaseType(char descriptor) {
+            mSv.visitBaseType(descriptor);
+        }
+
+        @Override
+        public SignatureVisitor visitClassBound() {
+            SignatureVisitor sv = mSv.visitClassBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitEnd() {
+            mSv.visitEnd();
+        }
+
+        @Override
+        public SignatureVisitor visitExceptionType() {
+            SignatureVisitor sv = mSv.visitExceptionType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitFormalTypeParameter(String name) {
+            mSv.visitFormalTypeParameter(name);
+        }
+
+        @Override
+        public SignatureVisitor visitInterface() {
+            SignatureVisitor sv = mSv.visitInterface();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitInterfaceBound() {
+            SignatureVisitor sv = mSv.visitInterfaceBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitParameterType() {
+            SignatureVisitor sv = mSv.visitParameterType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitReturnType() {
+            SignatureVisitor sv = mSv.visitReturnType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitSuperclass() {
+            SignatureVisitor sv = mSv.visitSuperclass();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitTypeArgument() {
+            mSv.visitTypeArgument();
+        }
+
+        @Override
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            SignatureVisitor sv = mSv.visitTypeArgument(wildcard);
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitTypeVariable(String name) {
+            mSv.visitTypeVariable(name);
+        }
+
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 412695f..1572a40 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -34,6 +34,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
@@ -57,6 +58,8 @@
     private final String[] mDeriveFrom;
     /** Glob patterns of classes to keep, e.g. "com.foo.*" */
     private final String[] mIncludeGlobs;
+    /** The set of classes to exclude.*/
+    private final Set<String> mExcludedClasses;
 
     /**
      * Creates a new analyzer.
@@ -69,12 +72,13 @@
      *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
      */
     public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
-            String[] deriveFrom, String[] includeGlobs) {
+            String[] deriveFrom, String[] includeGlobs, Set<String> excludeClasses) {
         mLog = log;
         mGen = gen;
         mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
         mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
         mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
+        mExcludedClasses = excludeClasses;
     }
 
     /**
@@ -82,9 +86,6 @@
      * Fills the generator with classes & dependencies found.
      */
     public void analyze() throws IOException, LogAbortException {
-
-        AsmAnalyzer visitor = this;
-
         Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
         mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
                 mOsSourceJar.size() > 1 ? "s" : "");
@@ -232,7 +233,7 @@
      */
     void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
             Map<String, ClassReader> inOutFound) throws LogAbortException {
-        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);
+        findClass(super_name, zipClasses, inOutFound);
 
         for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
             String className = entry.getKey();
@@ -363,11 +364,12 @@
 
             className = internalToBinaryClassName(className);
 
-            // exclude classes that have already been found
+            // exclude classes that have already been found or are marked to be excluded
             if (mInKeep.containsKey(className) ||
                     mOutKeep.containsKey(className) ||
                     mInDeps.containsKey(className) ||
-                    mOutDeps.containsKey(className)) {
+                    mOutDeps.containsKey(className) ||
+                    mExcludedClasses.contains(getBaseName(className))) {
                 return;
             }
 
@@ -451,6 +453,13 @@
             }
         }
 
+        private String getBaseName(String className) {
+            int pos = className.indexOf('$');
+            if (pos > 0) {
+                return className.substring(0, pos);
+            }
+            return className;
+        }
 
         // ---------------------------------------------------
         // --- ClassVisitor, FieldVisitor
@@ -682,7 +691,7 @@
             }
 
             @Override
-            public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+            public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
                 // pass -- table switch instruction
 
             }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index a9ede26..b102561 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -65,6 +65,9 @@
     /** A map { FQCN => set { method names } } of methods to rewrite as delegates.
      *  The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */
     private final HashMap<String, Set<String>> mDelegateMethods;
+    /** FQCN Names of classes to refactor. All reference to old-FQCN will be updated to new-FQCN.
+     * map old-FQCN => new-FQCN */
+    private final HashMap<String, String> mRefactorClasses;
 
     /**
      * Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -119,6 +122,17 @@
             mClassesNotRenamed.add(oldFqcn);
         }
 
+        // Create a map of classes to be refactored.
+        mRefactorClasses = new HashMap<String, String>();
+        String[] refactorClasses = createInfo.getJavaPkgClasses();
+        n = refactorClasses.length;
+        for (int i = 0; i < n; i += 2) {
+            assert i + 1 < n;
+            String oldFqcn = binaryToInternalClassName(refactorClasses[i]);
+            String newFqcn = binaryToInternalClassName(refactorClasses[i + 1]);
+            mRefactorClasses.put(oldFqcn, newFqcn);;
+        }
+
         // create the map of renamed class -> return type of method to delete.
         mDeleteReturns = new HashMap<String, Set<String>>();
         String[] deleteReturns = createInfo.getDeleteReturns();
@@ -308,14 +322,14 @@
         // original class reader.
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 
-        ClassVisitor rv = cw;
+        ClassVisitor cv = new RefactorClassAdapter(cw, mRefactorClasses);
         if (newName != className) {
-            rv = new RenameClassAdapter(cw, className, newName);
+            cv = new RenameClassAdapter(cv, className, newName);
         }
 
-        ClassVisitor cv = new TransformClassAdapter(mLog, mStubMethods,
+        cv = new TransformClassAdapter(mLog, mStubMethods,
                 mDeleteReturns.get(className),
-                newName, rv,
+                newName, cv,
                 stubNativesOnly, stubNativesOnly || hasNativeMethods);
 
         Set<String> delegateMethods = mDelegateMethods.get(className);
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index d955040..f6779e3 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -17,6 +17,11 @@
 package com.android.tools.layoutlib.create;
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.java.AutoCloseable;
+import com.android.tools.layoutlib.java.Charsets;
+import com.android.tools.layoutlib.java.IntegralToString;
+import com.android.tools.layoutlib.java.Objects;
+import com.android.tools.layoutlib.java.UnsafeByteSequence;
 
 /**
  * Describes the work to be done by {@link AsmGenerator}.
@@ -84,6 +89,15 @@
         return DELETE_RETURNS;
     }
 
+    /**
+     * Returns the list of classes to refactor, must be an even list: the binary FQCN of class to
+     * replace followed by the new FQCN. All references to the old class should be updated to the
+     * new class. The list can be empty but must not be null.
+     */
+    @Override
+    public String[] getJavaPkgClasses() {
+      return JAVA_PKG_CLASSES;
+    }
     //-----
 
     /**
@@ -95,7 +109,13 @@
             MethodAdapter.class,
             ICreateInfo.class,
             CreateInfo.class,
-            LayoutlibDelegate.class
+            LayoutlibDelegate.class,
+            /* Java package classes */
+            AutoCloseable.class,
+            Objects.class,
+            IntegralToString.class,
+            UnsafeByteSequence.class,
+            Charsets.class,
         };
 
     /**
@@ -111,6 +131,7 @@
         "android.os.HandlerThread#run",
         "android.os.Build#getString",
         "android.text.format.DateFormat#is24HourFormat",
+        "android.text.format.Time#format1",
         "android.view.Choreographer#getRefreshRate",
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.LayoutInflater#rInflate",
@@ -169,6 +190,7 @@
         "android.text.AndroidBidi",
         "android.util.FloatMath",
         "android.view.Display",
+        "libcore.icu.DateIntervalFormat",
         "libcore.icu.ICU",
     };
 
@@ -195,6 +217,20 @@
         };
 
     /**
+     * The list of class references to update, must be an even list: the binary
+     * FQCN of class to replace followed by the new FQCN. The classes to
+     * replace are to be excluded from the output.
+     */
+    private final static String[] JAVA_PKG_CLASSES =
+        new String[] {
+            "java.lang.AutoCloseable",                         "com.android.tools.layoutlib.java.AutoCloseable",
+            "java.util.Objects",                               "com.android.tools.layoutlib.java.Objects",
+            "java.nio.charset.Charsets",                       "com.android.tools.layoutlib.java.Charsets",
+            "java.lang.IntegralToString",                      "com.android.tools.layoutlib.java.IntegralToString",
+            "java.lang.UnsafeByteSequence",                    "com.android.tools.layoutlib.java.UnsafeByteSequence",
+        };
+
+    /**
      * List of classes for which the methods returning them should be deleted.
      * The array contains a list of null terminated section starting with the name of the class
      * to rename in which the methods are deleted, followed by a list of return types identifying
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 40c1706..9387814 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -62,4 +62,11 @@
      */
     public abstract String[] getDeleteReturns();
 
+    /**
+     * Returns the list of classes to refactor, must be an even list: the
+     * binary FQCN of class to replace followed by the new FQCN. All references
+     * to the old class should be updated to the new class.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getJavaPkgClasses();
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index 9cd74db..ba23048 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -86,7 +87,9 @@
         }
 
         try {
-            AsmGenerator agen = new AsmGenerator(log, osDestJar, new CreateInfo());
+            CreateInfo info = new CreateInfo();
+            Set<String> excludeClasses = getExcludedClasses(info);
+            AsmGenerator agen = new AsmGenerator(log, osDestJar, info);
 
             AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
                     new String[] {                          // derived from
@@ -110,7 +113,8 @@
                         "android.pim.*", // for datepicker
                         "android.os.*",  // for android.os.Handler
                         "android.database.ContentObserver", // for Digital clock
-                        });
+                    },
+                    excludeClasses);
             aa.analyze();
             agen.generate();
 
@@ -146,6 +150,16 @@
         return 1;
     }
 
+    private static Set<String> getExcludedClasses(CreateInfo info) {
+        String[] refactoredClasses = info.getJavaPkgClasses();
+        Set<String> excludedClasses = new HashSet<String>(refactoredClasses.length);
+        for (int i = 0; i < refactoredClasses.length; i+=2) {
+            excludedClasses.add(refactoredClasses[i]);
+        }
+        return excludedClasses;
+
+    }
+
     private static int listDeps(ArrayList<String> osJarPath, Log log) {
         DependencyFinder df = new DependencyFinder(log);
         try {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
new file mode 100644
index 0000000..91161f5
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import java.util.HashMap;
+
+import org.objectweb.asm.ClassVisitor;
+
+public class RefactorClassAdapter extends AbstractClassAdapter {
+
+    private final HashMap<String, String> mRefactorClasses;
+
+    RefactorClassAdapter(ClassVisitor cv, HashMap<String, String> refactorClasses) {
+        super(cv);
+        mRefactorClasses = refactorClasses;
+    }
+
+    @Override
+    protected String renameInternalType(String oldClassName) {
+        if (oldClassName != null) {
+            String newName = mRefactorClasses.get(oldClassName);
+            if (newName != null) {
+                return newName;
+            }
+            int pos = oldClassName.indexOf('$');
+            if (pos > 0) {
+                newName = mRefactorClasses.get(oldClassName.substring(0, pos));
+                if (newName != null) {
+                    return newName + oldClassName.substring(pos);
+                }
+            }
+        }
+        return oldClassName;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
index 383cbb8..661074c 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
@@ -16,17 +16,7 @@
 
 package com.android.tools.layoutlib.create;
 
-import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.signature.SignatureReader;
-import org.objectweb.asm.signature.SignatureVisitor;
-import org.objectweb.asm.signature.SignatureWriter;
 
 /**
  * This class visitor renames a class from a given old name to a given new name.
@@ -36,7 +26,7 @@
  * For inner classes, this handles only the case where the outer class name changes.
  * The inner class name should remain the same.
  */
-public class RenameClassAdapter extends ClassVisitor {
+public class RenameClassAdapter extends AbstractClassAdapter {
 
 
     private final String mOldName;
@@ -49,8 +39,8 @@
      * The class visitor will also rename all inner classes and references in the methods.
      * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
      */
-    public RenameClassAdapter(ClassWriter cv, String oldName, String newName) {
-        super(Opcodes.ASM4, cv);
+    public RenameClassAdapter(ClassVisitor cv, String oldName, String newName) {
+        super(cv);
         mOldBase = mOldName = oldName;
         mNewBase = mNewName = newName;
 
@@ -66,71 +56,6 @@
         assert (mOldBase == null && mNewBase == null) || (mOldBase != null && mNewBase != null);
     }
 
-
-    /**
-     * Renames a type descriptor, e.g. "Lcom.package.MyClass;"
-     * If the type doesn't need to be renamed, returns the input string as-is.
-     */
-    String renameTypeDesc(String desc) {
-        if (desc == null) {
-            return null;
-        }
-
-        return renameType(Type.getType(desc));
-    }
-
-    /**
-     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
-     * object element, e.g. "[Lcom.package.MyClass;"
-     * If the type doesn't need to be renamed, returns the internal name of the input type.
-     */
-    String renameType(Type type) {
-        if (type == null) {
-            return null;
-        }
-
-        if (type.getSort() == Type.OBJECT) {
-            String in = type.getInternalName();
-            return "L" + renameInternalType(in) + ";";
-        } else if (type.getSort() == Type.ARRAY) {
-            StringBuilder sb = new StringBuilder();
-            for (int n = type.getDimensions(); n > 0; n--) {
-                sb.append('[');
-            }
-            sb.append(renameType(type.getElementType()));
-            return sb.toString();
-        }
-        return type.getDescriptor();
-    }
-
-    /**
-     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
-     * object element, e.g. "[Lcom.package.MyClass;".
-     * This is like renameType() except that it returns a Type object.
-     * If the type doesn't need to be renamed, returns the input type object.
-     */
-    Type renameTypeAsType(Type type) {
-        if (type == null) {
-            return null;
-        }
-
-        if (type.getSort() == Type.OBJECT) {
-            String in = type.getInternalName();
-            String newIn = renameInternalType(in);
-            if (newIn != in) {
-                return Type.getType("L" + newIn + ";");
-            }
-        } else if (type.getSort() == Type.ARRAY) {
-            StringBuilder sb = new StringBuilder();
-            for (int n = type.getDimensions(); n > 0; n--) {
-                sb.append('[');
-            }
-            sb.append(renameType(type.getElementType()));
-            return Type.getType(sb.toString());
-        }
-        return type;
-    }
-
     /**
      * Renames an internal type name, e.g. "com.package.MyClass".
      * If the type doesn't need to be renamed, returns the input string as-is.
@@ -138,7 +63,8 @@
      * The internal type of some of the MethodVisitor turns out to be a type
        descriptor sometimes so descriptors are renamed too.
      */
-    String renameInternalType(String type) {
+    @Override
+    protected String renameInternalType(String type) {
         if (type == null) {
             return null;
         }
@@ -155,309 +81,7 @@
         if (pos == mOldBase.length() && type.startsWith(mOldBase)) {
             return mNewBase + type.substring(pos);
         }
-
-        // The internal type of some of the MethodVisitor turns out to be a type
-        // descriptor sometimes. This is the case with visitTypeInsn(type) and
-        // visitMethodInsn(owner). We try to detect it and adjust it here.
-        if (type.indexOf(';') > 0) {
-            type = renameTypeDesc(type);
-        }
-
         return type;
     }
 
-    /**
-     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
-     * return value.
-     */
-    String renameMethodDesc(String desc) {
-        if (desc == null) {
-            return null;
-        }
-
-        Type[] args = Type.getArgumentTypes(desc);
-
-        StringBuilder sb = new StringBuilder("(");
-        for (Type arg : args) {
-            String name = renameType(arg);
-            sb.append(name);
-        }
-        sb.append(')');
-
-        Type ret = Type.getReturnType(desc);
-        String name = renameType(ret);
-        sb.append(name);
-
-        return sb.toString();
-    }
-
-
-    /**
-     * Renames the ClassSignature handled by ClassVisitor.visit
-     * or the MethodTypeSignature handled by ClassVisitor.visitMethod.
-     */
-    String renameTypeSignature(String sig) {
-        if (sig == null) {
-            return null;
-        }
-        SignatureReader reader = new SignatureReader(sig);
-        SignatureWriter writer = new SignatureWriter();
-        reader.accept(new RenameSignatureAdapter(writer));
-        sig = writer.toString();
-        return sig;
-    }
-
-
-    /**
-     * Renames the FieldTypeSignature handled by ClassVisitor.visitField
-     * or MethodVisitor.visitLocalVariable.
-     */
-    String renameFieldSignature(String sig) {
-        if (sig == null) {
-            return null;
-        }
-        SignatureReader reader = new SignatureReader(sig);
-        SignatureWriter writer = new SignatureWriter();
-        reader.acceptType(new RenameSignatureAdapter(writer));
-        sig = writer.toString();
-        return sig;
-    }
-
-
-    //----------------------------------
-    // Methods from the ClassAdapter
-
-    @Override
-    public void visit(int version, int access, String name, String signature,
-            String superName, String[] interfaces) {
-        name = renameInternalType(name);
-        superName = renameInternalType(superName);
-        signature = renameTypeSignature(signature);
-
-        super.visit(version, access, name, signature, superName, interfaces);
-    }
-
-    @Override
-    public void visitInnerClass(String name, String outerName, String innerName, int access) {
-        assert outerName.equals(mOldName);
-        outerName = renameInternalType(outerName);
-        name = outerName + "$" + innerName;
-        super.visitInnerClass(name, outerName, innerName, access);
-    }
-
-    @Override
-    public MethodVisitor visitMethod(int access, String name, String desc,
-            String signature, String[] exceptions) {
-        desc = renameMethodDesc(desc);
-        signature = renameTypeSignature(signature);
-        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
-        return new RenameMethodAdapter(mw);
-    }
-
-    @Override
-    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-        desc = renameTypeDesc(desc);
-        return super.visitAnnotation(desc, visible);
-    }
-
-    @Override
-    public FieldVisitor visitField(int access, String name, String desc,
-            String signature, Object value) {
-        desc = renameTypeDesc(desc);
-        signature = renameFieldSignature(signature);
-        return super.visitField(access, name, desc, signature, value);
-    }
-
-
-    //----------------------------------
-
-    /**
-     * A method visitor that renames all references from an old class name to a new class name.
-     */
-    public class RenameMethodAdapter extends MethodVisitor {
-
-        /**
-         * Creates a method visitor that renames all references from a given old name to a given new
-         * name. The method visitor will also rename all inner classes.
-         * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
-         */
-        public RenameMethodAdapter(MethodVisitor mv) {
-            super(Opcodes.ASM4, mv);
-        }
-
-        @Override
-        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-            desc = renameTypeDesc(desc);
-
-            return super.visitAnnotation(desc, visible);
-        }
-
-        @Override
-        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
-            desc = renameTypeDesc(desc);
-
-            return super.visitParameterAnnotation(parameter, desc, visible);
-        }
-
-        @Override
-        public void visitTypeInsn(int opcode, String type) {
-            type = renameInternalType(type);
-
-            super.visitTypeInsn(opcode, type);
-        }
-
-        @Override
-        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
-            owner = renameInternalType(owner);
-            desc = renameTypeDesc(desc);
-
-            super.visitFieldInsn(opcode, owner, name, desc);
-        }
-
-        @Override
-        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
-            owner = renameInternalType(owner);
-            desc = renameMethodDesc(desc);
-
-            super.visitMethodInsn(opcode, owner, name, desc);
-        }
-
-        @Override
-        public void visitLdcInsn(Object cst) {
-            // If cst is a Type, this means the code is trying to pull the .class constant
-            // for this class, so it needs to be renamed too.
-            if (cst instanceof Type) {
-                cst = renameTypeAsType((Type) cst);
-            }
-            super.visitLdcInsn(cst);
-        }
-
-        @Override
-        public void visitMultiANewArrayInsn(String desc, int dims) {
-            desc = renameTypeDesc(desc);
-
-            super.visitMultiANewArrayInsn(desc, dims);
-        }
-
-        @Override
-        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
-            type = renameInternalType(type);
-
-            super.visitTryCatchBlock(start, end, handler, type);
-        }
-
-        @Override
-        public void visitLocalVariable(String name, String desc, String signature,
-                Label start, Label end, int index) {
-            desc = renameTypeDesc(desc);
-            signature = renameFieldSignature(signature);
-
-            super.visitLocalVariable(name, desc, signature, start, end, index);
-        }
-
-    }
-
-    //----------------------------------
-
-    public class RenameSignatureAdapter extends SignatureVisitor {
-
-        private final SignatureVisitor mSv;
-
-        public RenameSignatureAdapter(SignatureVisitor sv) {
-            super(Opcodes.ASM4);
-            mSv = sv;
-        }
-
-        @Override
-        public void visitClassType(String name) {
-            name = renameInternalType(name);
-            mSv.visitClassType(name);
-        }
-
-        @Override
-        public void visitInnerClassType(String name) {
-            name = renameInternalType(name);
-            mSv.visitInnerClassType(name);
-        }
-
-        @Override
-        public SignatureVisitor visitArrayType() {
-            SignatureVisitor sv = mSv.visitArrayType();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public void visitBaseType(char descriptor) {
-            mSv.visitBaseType(descriptor);
-        }
-
-        @Override
-        public SignatureVisitor visitClassBound() {
-            SignatureVisitor sv = mSv.visitClassBound();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public void visitEnd() {
-            mSv.visitEnd();
-        }
-
-        @Override
-        public SignatureVisitor visitExceptionType() {
-            SignatureVisitor sv = mSv.visitExceptionType();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public void visitFormalTypeParameter(String name) {
-            mSv.visitFormalTypeParameter(name);
-        }
-
-        @Override
-        public SignatureVisitor visitInterface() {
-            SignatureVisitor sv = mSv.visitInterface();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public SignatureVisitor visitInterfaceBound() {
-            SignatureVisitor sv = mSv.visitInterfaceBound();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public SignatureVisitor visitParameterType() {
-            SignatureVisitor sv = mSv.visitParameterType();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public SignatureVisitor visitReturnType() {
-            SignatureVisitor sv = mSv.visitReturnType();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public SignatureVisitor visitSuperclass() {
-            SignatureVisitor sv = mSv.visitSuperclass();
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public void visitTypeArgument() {
-            mSv.visitTypeArgument();
-        }
-
-        @Override
-        public SignatureVisitor visitTypeArgument(char wildcard) {
-            SignatureVisitor sv = mSv.visitTypeArgument(wildcard);
-            return new RenameSignatureAdapter(sv);
-        }
-
-        @Override
-        public void visitTypeVariable(String name) {
-            mSv.visitTypeVariable(name);
-        }
-
-    }
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java
new file mode 100644
index 0000000..ed2c128
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java;
+
+/**
+ * Defines the same interface as the java.lang.AutoCloseable which was added in
+ * Java 7. This hack makes it possible to run the Android code which uses Java 7
+ * features (API 18 and beyond) to run on Java 6.
+ * <p/>
+ * Extracted from API level 18, file:
+ * platform/libcore/luni/src/main/java/java/lang/AutoCloseable.java
+ */
+public interface AutoCloseable {
+    /**
+     * Closes the object and release any system resources it holds.
+     */
+    void close() throws Exception; }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Charsets.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Charsets.java
new file mode 100644
index 0000000..f73b06b
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Charsets.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java;
+
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * Defines the same class as the java.nio.charset.Charsets which was added in
+ * Dalvik VM. This hack, provides a replacement for that class which can't be
+ * loaded in the standard JVM since it's in the java package and standard JVM
+ * doesn't have it. An implementation of the native methods in the original
+ * class has been added.
+ * <p/>
+ * Extracted from API level 18, file:
+ * platform/libcore/luni/src/main/java/java/nio/charset/Charsets
+ */
+public final class Charsets {
+    /**
+     * A cheap and type-safe constant for the ISO-8859-1 Charset.
+     */
+    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+
+    /**
+     * A cheap and type-safe constant for the US-ASCII Charset.
+     */
+    public static final Charset US_ASCII = Charset.forName("US-ASCII");
+
+    /**
+     * A cheap and type-safe constant for the UTF-8 Charset.
+     */
+    public static final Charset UTF_8 = Charset.forName("UTF-8");
+
+    /**
+     * Returns a new byte array containing the bytes corresponding to the given characters,
+     * encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'.
+     */
+    public static byte[] toAsciiBytes(char[] chars, int offset, int length) {
+        CharBuffer cb = CharBuffer.allocate(length);
+        cb.put(chars, offset, length);
+        return US_ASCII.encode(cb).array();
+    }
+
+    /**
+     * Returns a new byte array containing the bytes corresponding to the given characters,
+     * encoded in ISO-8859-1. Unrepresentable characters are replaced by (byte) '?'.
+     */
+    public static byte[] toIsoLatin1Bytes(char[] chars, int offset, int length) {
+        CharBuffer cb = CharBuffer.allocate(length);
+        cb.put(chars, offset, length);
+        return ISO_8859_1.encode(cb).array();
+    }
+
+    /**
+     * Returns a new byte array containing the bytes corresponding to the given characters,
+     * encoded in UTF-8. All characters are representable in UTF-8.
+     */
+    public static byte[] toUtf8Bytes(char[] chars, int offset, int length) {
+        CharBuffer cb = CharBuffer.allocate(length);
+        cb.put(chars, offset, length);
+        return UTF_8.encode(cb).array();
+    }
+
+    /**
+     * Returns a new byte array containing the bytes corresponding to the given characters,
+     * encoded in UTF-16BE. All characters are representable in UTF-16BE.
+     */
+    public static byte[] toBigEndianUtf16Bytes(char[] chars, int offset, int length) {
+        byte[] result = new byte[length * 2];
+        int end = offset + length;
+        int resultIndex = 0;
+        for (int i = offset; i < end; ++i) {
+            char ch = chars[i];
+            result[resultIndex++] = (byte) (ch >> 8);
+            result[resultIndex++] = (byte) ch;
+        }
+        return result;
+    }
+
+    /**
+     * Decodes the given US-ASCII bytes into the given char[]. Equivalent to but faster than:
+     *
+     * for (int i = 0; i < count; ++i) {
+     *     char ch = (char) (data[start++] & 0xff);
+     *     value[i] = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR;
+     * }
+     */
+    public static void asciiBytesToChars(byte[] bytes, int offset, int length, char[] chars) {
+        if (bytes == null || chars == null) {
+            return;
+        }
+        final char REPLACEMENT_CHAR = (char)0xffd;
+        int start = offset;
+        for (int i = 0; i < length; ++i) {
+            char ch = (char) (bytes[start++] & 0xff);
+            chars[i] = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR;
+        }
+    }
+
+    /**
+     * Decodes the given ISO-8859-1 bytes into the given char[]. Equivalent to but faster than:
+     *
+     * for (int i = 0; i < count; ++i) {
+     *     value[i] = (char) (data[start++] & 0xff);
+     * }
+     */
+    public static void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars) {
+        if (bytes == null || chars == null) {
+            return;
+        }
+        int start = offset;
+        for (int i = 0; i < length; ++i) {
+            chars[i] = (char) (bytes[start++] & 0xff);
+        }
+    }
+
+    private Charsets() {
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/IntegralToString.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/IntegralToString.java
new file mode 100644
index 0000000..e6dd00a
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/IntegralToString.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java;
+
+/**
+ * Defines the same class as the java.lang.IntegralToString which was added in
+ * Dalvik VM. This hack, provides a replacement for that class which can't be
+ * loaded in the standard JVM since it's in the java package and standard JVM
+ * doesn't have it. Since it's no longer in java.lang, access to package
+ * private methods and classes has been replaced by the closes matching public
+ * implementation.
+ * <p/>
+ * Extracted from API level 18, file:
+ * platform/libcore/luni/src/main/java/java/lang/IntegralToString.java
+ */
+public final class IntegralToString {
+    /**
+     * When appending to an AbstractStringBuilder, this thread-local char[] lets us avoid
+     * allocation of a temporary array. (We can't write straight into the AbstractStringBuilder
+     * because it's almost as expensive to work out the exact length of the result as it is to
+     * do the formatting. We could try being conservative and "delete"-ing the unused space
+     * afterwards, but then we'd need to duplicate convertInt and convertLong rather than share
+     * the code.)
+     */
+    private static final ThreadLocal<char[]> BUFFER = new ThreadLocal<char[]>() {
+        @Override protected char[] initialValue() {
+            return new char[20]; // Maximum length of a base-10 long.
+        }
+    };
+
+    /**
+     * These tables are used to special-case toString computation for
+     * small values.  This serves three purposes: it reduces memory usage;
+     * it increases performance for small values; and it decreases the
+     * number of comparisons required to do the length computation.
+     * Elements of this table are lazily initialized on first use.
+     * No locking is necessary, i.e., we use the non-volatile, racy
+     * single-check idiom.
+     */
+    private static final String[] SMALL_NONNEGATIVE_VALUES = new String[100];
+    private static final String[] SMALL_NEGATIVE_VALUES = new String[100];
+
+    /** TENS[i] contains the tens digit of the number i, 0 <= i <= 99. */
+    private static final char[] TENS = {
+        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
+        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
+        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
+        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
+        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
+        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
+        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
+        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
+        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9'
+    };
+
+    /** Ones [i] contains the tens digit of the number i, 0 <= i <= 99. */
+    private static final char[] ONES = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    };
+
+    /**
+     * Table for MOD / DIV 10 computation described in Section 10-21
+     * of Hank Warren's "Hacker's Delight" online addendum.
+     * http://www.hackersdelight.org/divcMore.pdf
+     */
+    private static final char[] MOD_10_TABLE = {
+        0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 0
+    };
+
+    /**
+     * The digits for every supported radix.
+     */
+    private static final char[] DIGITS = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+        'u', 'v', 'w', 'x', 'y', 'z'
+    };
+
+    private static final char[] UPPER_CASE_DIGITS = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+        'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+        'U', 'V', 'W', 'X', 'Y', 'Z'
+    };
+
+    private IntegralToString() {
+    }
+
+    /**
+     * Equivalent to Integer.toString(i, radix).
+     */
+    public static String intToString(int i, int radix) {
+        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+            radix = 10;
+        }
+        if (radix == 10) {
+            return intToString(i);
+        }
+
+        /*
+         * If i is positive, negate it. This is the opposite of what one might
+         * expect. It is necessary because the range of the negative values is
+         * strictly larger than that of the positive values: there is no
+         * positive value corresponding to Integer.MIN_VALUE.
+         */
+        boolean negative = false;
+        if (i < 0) {
+            negative = true;
+        } else {
+            i = -i;
+        }
+
+        int bufLen = radix < 8 ? 33 : 12;  // Max chars in result (conservative)
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            int q = i / radix;
+            buf[--cursor] = DIGITS[radix * q - i];
+            i = q;
+        } while (i != 0);
+
+        if (negative) {
+            buf[--cursor] = '-';
+        }
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    /**
+     * Equivalent to Integer.toString(i).
+     */
+    public static String intToString(int i) {
+        return convertInt(null, i);
+    }
+
+    /**
+     * Equivalent to sb.append(Integer.toString(i)).
+     */
+    public static void appendInt(StringBuilder sb, int i) {
+        convertInt(sb, i);
+    }
+
+    /**
+     * Returns the string representation of i and leaves sb alone if sb is null.
+     * Returns null and appends the string representation of i to sb if sb is non-null.
+     */
+    private static String convertInt(StringBuilder sb, int i) {
+        boolean negative = false;
+        String quickResult = null;
+        if (i < 0) {
+            negative = true;
+            i = -i;
+            if (i < 100) {
+                if (i < 0) {
+                    // If -n is still negative, n is Integer.MIN_VALUE
+                    quickResult = "-2147483648";
+                } else {
+                    quickResult = SMALL_NEGATIVE_VALUES[i];
+                    if (quickResult == null) {
+                        SMALL_NEGATIVE_VALUES[i] = quickResult =
+                                i < 10 ? stringOf('-', ONES[i]) : stringOf('-', TENS[i], ONES[i]);
+                    }
+                }
+            }
+        } else {
+            if (i < 100) {
+                quickResult = SMALL_NONNEGATIVE_VALUES[i];
+                if (quickResult == null) {
+                    SMALL_NONNEGATIVE_VALUES[i] = quickResult =
+                            i < 10 ? stringOf(ONES[i]) : stringOf(TENS[i], ONES[i]);
+                }
+            }
+        }
+        if (quickResult != null) {
+            if (sb != null) {
+                sb.append(quickResult);
+                return null;
+            }
+            return quickResult;
+        }
+
+        int bufLen = 11; // Max number of chars in result
+        char[] buf = (sb != null) ? BUFFER.get() : new char[bufLen];
+        int cursor = bufLen;
+
+        // Calculate digits two-at-a-time till remaining digits fit in 16 bits
+        while (i >= (1 << 16)) {
+            // Compute q = n/100 and r = n % 100 as per "Hacker's Delight" 10-8
+            int q = (int) ((0x51EB851FL * i) >>> 37);
+            int r = i - 100*q;
+            buf[--cursor] = ONES[r];
+            buf[--cursor] = TENS[r];
+            i = q;
+        }
+
+        // Calculate remaining digits one-at-a-time for performance
+        while (i != 0) {
+            // Compute q = n/10 and r = n % 10 as per "Hacker's Delight" 10-8
+            int q = (0xCCCD * i) >>> 19;
+            int r = i - 10*q;
+            buf[--cursor] = DIGITS[r];
+            i = q;
+        }
+
+        if (negative) {
+            buf[--cursor] = '-';
+        }
+
+        if (sb != null) {
+            sb.append(buf, cursor, bufLen - cursor);
+            return null;
+        } else {
+            return new String(buf, cursor, bufLen - cursor);
+        }
+    }
+
+    /**
+     * Equivalent to Long.toString(v, radix).
+     */
+    public static String longToString(long v, int radix) {
+        int i = (int) v;
+        if (i == v) {
+            return intToString(i, radix);
+        }
+
+        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+            radix = 10;
+        }
+        if (radix == 10) {
+            return longToString(v);
+        }
+
+        /*
+         * If v is positive, negate it. This is the opposite of what one might
+         * expect. It is necessary because the range of the negative values is
+         * strictly larger than that of the positive values: there is no
+         * positive value corresponding to Integer.MIN_VALUE.
+         */
+        boolean negative = false;
+        if (v < 0) {
+            negative = true;
+        } else {
+            v = -v;
+        }
+
+        int bufLen = radix < 8 ? 65 : 23;  // Max chars in result (conservative)
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            long q = v / radix;
+            buf[--cursor] = DIGITS[(int) (radix * q - v)];
+            v = q;
+        } while (v != 0);
+
+        if (negative) {
+            buf[--cursor] = '-';
+        }
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    /**
+     * Equivalent to Long.toString(l).
+     */
+    public static String longToString(long l) {
+        return convertLong(null, l);
+    }
+
+    /**
+     * Equivalent to sb.append(Long.toString(l)).
+     */
+    public static void appendLong(StringBuilder sb, long l) {
+        convertLong(sb, l);
+    }
+
+    /**
+     * Returns the string representation of n and leaves sb alone if sb is null.
+     * Returns null and appends the string representation of n to sb if sb is non-null.
+     */
+    private static String convertLong(StringBuilder sb, long n) {
+        int i = (int) n;
+        if (i == n) {
+            return convertInt(sb, i);
+        }
+
+        boolean negative = (n < 0);
+        if (negative) {
+            n = -n;
+            if (n < 0) {
+                // If -n is still negative, n is Long.MIN_VALUE
+                String quickResult = "-9223372036854775808";
+                if (sb != null) {
+                    sb.append(quickResult);
+                    return null;
+                }
+                return quickResult;
+            }
+        }
+
+        int bufLen = 20; // Maximum number of chars in result
+        char[] buf = (sb != null) ? BUFFER.get() : new char[bufLen];
+
+        int low = (int) (n % 1000000000); // Extract low-order 9 digits
+        int cursor = intIntoCharArray(buf, bufLen, low);
+
+        // Zero-pad Low order part to 9 digits
+        while (cursor != (bufLen - 9)) {
+            buf[--cursor] = '0';
+        }
+
+        /*
+         * The remaining digits are (n - low) / 1,000,000,000.  This
+         * "exact division" is done as per the online addendum to Hank Warren's
+         * "Hacker's Delight" 10-20, http://www.hackersdelight.org/divcMore.pdf
+         */
+        n = ((n - low) >>> 9) * 0x8E47CE423A2E9C6DL;
+
+        /*
+         * If the remaining digits fit in an int, emit them using a
+         * single call to intIntoCharArray. Otherwise, strip off the
+         * low-order digit, put it in buf, and then call intIntoCharArray
+         * on the remaining digits (which now fit in an int).
+         */
+        if ((n & (-1L << 32)) == 0) {
+            cursor = intIntoCharArray(buf, cursor, (int) n);
+        } else {
+            /*
+             * Set midDigit to n % 10
+             */
+            int lo32 = (int) n;
+            int hi32 = (int) (n >>> 32);
+
+            // midDigit = ((unsigned) low32) % 10, per "Hacker's Delight" 10-21
+            int midDigit = MOD_10_TABLE[(0x19999999 * lo32 + (lo32 >>> 1) + (lo32 >>> 3)) >>> 28];
+
+            // Adjust midDigit for hi32. (assert hi32 == 1 || hi32 == 2)
+            midDigit -= hi32 << 2;  // 1L << 32 == -4 MOD 10
+            if (midDigit < 0) {
+                midDigit += 10;
+            }
+            buf[--cursor] = DIGITS[midDigit];
+
+            // Exact division as per Warren 10-20
+            int rest = ((int) ((n - midDigit) >>> 1)) * 0xCCCCCCCD;
+            cursor = intIntoCharArray(buf, cursor, rest);
+        }
+
+        if (negative) {
+            buf[--cursor] = '-';
+        }
+        if (sb != null) {
+            sb.append(buf, cursor, bufLen - cursor);
+            return null;
+        } else {
+            return new String(buf, cursor, bufLen - cursor);
+        }
+    }
+
+    /**
+     * Inserts the unsigned decimal integer represented by n into the specified
+     * character array starting at position cursor.  Returns the index after
+     * the last character inserted (i.e., the value to pass in as cursor the
+     * next time this method is called). Note that n is interpreted as a large
+     * positive integer (not a negative integer) if its sign bit is set.
+     */
+    private static int intIntoCharArray(char[] buf, int cursor, int n) {
+        // Calculate digits two-at-a-time till remaining digits fit in 16 bits
+        while ((n & 0xffff0000) != 0) {
+            /*
+             * Compute q = n/100 and r = n % 100 as per "Hacker's Delight" 10-8.
+             * This computation is slightly different from the corresponding
+             * computation in intToString: the shifts before and after
+             * multiply can't be combined, as that would yield the wrong result
+             * if n's sign bit were set.
+             */
+            int q = (int) ((0x51EB851FL * (n >>> 2)) >>> 35);
+            int r = n - 100*q;
+            buf[--cursor] = ONES[r];
+            buf[--cursor] = TENS[r];
+            n = q;
+        }
+
+        // Calculate remaining digits one-at-a-time for performance
+        while (n != 0) {
+            // Compute q = n / 10 and r = n % 10 as per "Hacker's Delight" 10-8
+            int q = (0xCCCD * n) >>> 19;
+            int r = n - 10*q;
+            buf[--cursor] = DIGITS[r];
+            n = q;
+        }
+        return cursor;
+    }
+
+    public static String intToBinaryString(int i) {
+        int bufLen = 32;  // Max number of binary digits in an int
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[i & 1];
+        }  while ((i >>>= 1) != 0);
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    public static String longToBinaryString(long v) {
+        int i = (int) v;
+        if (v >= 0 && i == v) {
+            return intToBinaryString(i);
+        }
+
+        int bufLen = 64;  // Max number of binary digits in a long
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[((int) v) & 1];
+        }  while ((v >>>= 1) != 0);
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    public static StringBuilder appendByteAsHex(StringBuilder sb, byte b, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS;
+        sb.append(digits[(b >> 4) & 0xf]);
+        sb.append(digits[b & 0xf]);
+        return sb;
+    }
+
+    public static String byteToHexString(byte b, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS;
+        char[] buf = new char[2]; // We always want two digits.
+        buf[0] = digits[(b >> 4) & 0xf];
+        buf[1] = digits[b & 0xf];
+        return new String(buf, 0, 2);
+    }
+
+    public static String bytesToHexString(byte[] bytes, boolean upperCase) {
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS;
+        char[] buf = new char[bytes.length * 2];
+        int c = 0;
+        for (byte b : bytes) {
+            buf[c++] = digits[(b >> 4) & 0xf];
+            buf[c++] = digits[b & 0xf];
+        }
+        return new String(buf);
+    }
+
+    public static String intToHexString(int i, boolean upperCase, int minWidth) {
+        int bufLen = 8;  // Max number of hex digits in an int
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS;
+        do {
+            buf[--cursor] = digits[i & 0xf];
+        } while ((i >>>= 4) != 0 || (bufLen - cursor < minWidth));
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    public static String longToHexString(long v) {
+        int i = (int) v;
+        if (v >= 0 && i == v) {
+            return intToHexString(i, false, 0);
+        }
+
+        int bufLen = 16;  // Max number of hex digits in a long
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[((int) v) & 0xF];
+        } while ((v >>>= 4) != 0);
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    public static String intToOctalString(int i) {
+        int bufLen = 11;  // Max number of octal digits in an int
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[i & 7];
+        } while ((i >>>= 3) != 0);
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    public static String longToOctalString(long v) {
+        int i = (int) v;
+        if (v >= 0 && i == v) {
+            return intToOctalString(i);
+        }
+        int bufLen = 22;  // Max number of octal digits in a long
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[((int) v) & 7];
+        } while ((v >>>= 3) != 0);
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    private static String stringOf(char... args) {
+        return new String(args, 0, args.length);
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Objects.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Objects.java
new file mode 100644
index 0000000..eb1ef72
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/Objects.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Defines the same class as the java.util.Objects which is added in Java 7.
+ * This hack makes it possible to run the Android code which uses Java 7 features
+ * (API 18 and beyond) to run on Java 6.
+ * <p/>
+ * Extracted from API level 19, file:
+ * platform/libcore/luni/src/main/java/java/util/Objects.java
+ */
+public final class Objects {
+  private Objects() {}
+
+  /**
+   * Returns 0 if {@code a == b}, or {@code c.compare(a, b)} otherwise.
+   * That is, this makes {@code c} null-safe.
+   */
+  public static <T> int compare(T a, T b, Comparator<? super T> c) {
+    if (a == b) {
+      return 0;
+    }
+    return c.compare(a, b);
+  }
+
+  /**
+   * Returns true if both arguments are null,
+   * the result of {@link Arrays#equals} if both arguments are primitive arrays,
+   * the result of {@link Arrays#deepEquals} if both arguments are arrays of reference types,
+   * and the result of {@link #equals} otherwise.
+   */
+  public static boolean deepEquals(Object a, Object b) {
+    if (a == null || b == null) {
+      return a == b;
+    } else if (a instanceof Object[] && b instanceof Object[]) {
+      return Arrays.deepEquals((Object[]) a, (Object[]) b);
+    } else if (a instanceof boolean[] && b instanceof boolean[]) {
+      return Arrays.equals((boolean[]) a, (boolean[]) b);
+    } else if (a instanceof byte[] && b instanceof byte[]) {
+      return Arrays.equals((byte[]) a, (byte[]) b);
+    } else if (a instanceof char[] && b instanceof char[]) {
+      return Arrays.equals((char[]) a, (char[]) b);
+    } else if (a instanceof double[] && b instanceof double[]) {
+      return Arrays.equals((double[]) a, (double[]) b);
+    } else if (a instanceof float[] && b instanceof float[]) {
+      return Arrays.equals((float[]) a, (float[]) b);
+    } else if (a instanceof int[] && b instanceof int[]) {
+      return Arrays.equals((int[]) a, (int[]) b);
+    } else if (a instanceof long[] && b instanceof long[]) {
+      return Arrays.equals((long[]) a, (long[]) b);
+    } else if (a instanceof short[] && b instanceof short[]) {
+      return Arrays.equals((short[]) a, (short[]) b);
+    }
+    return a.equals(b);
+  }
+
+  /**
+   * Null-safe equivalent of {@code a.equals(b)}.
+   */
+  public static boolean equals(Object a, Object b) {
+    return (a == null) ? (b == null) : a.equals(b);
+  }
+
+  /**
+   * Convenience wrapper for {@link Arrays#hashCode}, adding varargs.
+   * This can be used to compute a hash code for an object's fields as follows:
+   * {@code Objects.hash(a, b, c)}.
+   */
+  public static int hash(Object... values) {
+    return Arrays.hashCode(values);
+  }
+
+  /**
+   * Returns 0 for null or {@code o.hashCode()}.
+   */
+  public static int hashCode(Object o) {
+    return (o == null) ? 0 : o.hashCode();
+  }
+
+  /**
+   * Returns {@code o} if non-null, or throws {@code NullPointerException}.
+   */
+  public static <T> T requireNonNull(T o) {
+    if (o == null) {
+      throw new NullPointerException();
+    }
+    return o;
+  }
+
+  /**
+   * Returns {@code o} if non-null, or throws {@code NullPointerException}
+   * with the given detail message.
+   */
+  public static <T> T requireNonNull(T o, String message) {
+    if (o == null) {
+      throw new NullPointerException(message);
+    }
+    return o;
+  }
+
+  /**
+   * Returns "null" for null or {@code o.toString()}.
+   */
+  public static String toString(Object o) {
+    return (o == null) ? "null" : o.toString();
+  }
+
+  /**
+   * Returns {@code nullString} for null or {@code o.toString()}.
+   */
+  public static String toString(Object o, String nullString) {
+    return (o == null) ? nullString : o.toString();
+  }
+}
\ No newline at end of file
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/UnsafeByteSequence.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/UnsafeByteSequence.java
new file mode 100644
index 0000000..0e09080
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/UnsafeByteSequence.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java;
+
+import java.nio.charset.Charset;
+
+/**
+ * Defines the same class as the java.lang.UnsafeByteSequence which was added in
+ * Dalvik VM. This hack, provides a replacement for that class which can't be
+ * loaded in the standard JVM since it's in the java package and standard JVM
+ * doesn't have it.
+ * <p/>
+ * Extracted from API level 18, file:
+ * platform/libcore/luni/src/main/java/java/lang/UnsafeByteSequence.java
+ */
+public class UnsafeByteSequence {
+    private byte[] bytes;
+    private int count;
+
+    public UnsafeByteSequence(int initialCapacity) {
+        this.bytes = new byte[initialCapacity];
+    }
+
+    public int size() {
+        return count;
+    }
+
+    /**
+     * Moves the write pointer back to the beginning of the sequence,
+     * but without resizing or reallocating the buffer.
+     */
+    public void rewind() {
+        count = 0;
+    }
+
+    public void write(byte[] buffer, int offset, int length) {
+        if (count + length >= bytes.length) {
+            byte[] newBytes = new byte[(count + length) * 2];
+            System.arraycopy(bytes, 0, newBytes, 0, count);
+            bytes = newBytes;
+        }
+        System.arraycopy(buffer, offset, bytes, count, length);
+        count += length;
+    }
+
+    public void write(int b) {
+        if (count == bytes.length) {
+            byte[] newBytes = new byte[count * 2];
+            System.arraycopy(bytes, 0, newBytes, 0, count);
+            bytes = newBytes;
+        }
+        bytes[count++] = (byte) b;
+    }
+
+    public byte[] toByteArray() {
+        if (count == bytes.length) {
+            return bytes;
+        }
+        byte[] result = new byte[count];
+        System.arraycopy(bytes, 0, result, 0, count);
+        return result;
+    }
+
+    public String toString(Charset cs) {
+        return new String(bytes, 0, count, cs);
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index d6dba6a..005fc9d 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -31,7 +31,9 @@
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 
 /**
@@ -51,8 +53,10 @@
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
+        Set<String> excludeClasses = new HashSet<String>(1);
+        excludeClasses.add("java.lang.JavaClass");
         mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */,
-                null /* deriveFrom */, null /* includeGlobs */ );
+                null /* deriveFrom */, null /* includeGlobs */, excludeClasses);
     }
 
     @After
@@ -64,6 +68,7 @@
         Map<String, ClassReader> map = mAa.parseZip(mOsJarPath);
 
         assertArrayEquals(new String[] {
+                "java.lang.JavaClass",
                 "mock_android.dummy.InnerTest",
                 "mock_android.dummy.InnerTest$DerivingClass",
                 "mock_android.dummy.InnerTest$MyGenerics1",
@@ -221,7 +226,11 @@
         for (ClassReader cr2 : in_deps.values()) {
             cr2.accept(visitor, 0 /* flags */);
         }
+        keep.putAll(new_keep);
 
         assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
+        assertArrayEquals(new String[] {
+                "mock_android.widget.TableLayout",
+        }, keep.keySet().toArray());
     }
 }
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 7b76a5b..8a27173 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -19,16 +19,29 @@
 
 
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 /**
  * Unit tests for some methods of {@link AsmGenerator}.
@@ -40,6 +53,9 @@
     private String mOsDestJar;
     private File mTempFile;
 
+    // ASM internal name for the the class in java package that should be refactored.
+    private static final String JAVA_CLASS_NAME = "java/lang/JavaClass";
+
     @Before
     public void setUp() throws Exception {
         mLog = new MockLog();
@@ -48,7 +64,7 @@
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
-        mTempFile = File.createTempFile("mock", "jar");
+        mTempFile = File.createTempFile("mock", ".jar");
         mOsDestJar = mTempFile.getAbsolutePath();
         mTempFile.deleteOnExit();
     }
@@ -97,6 +113,11 @@
             }
 
             @Override
+            public String[] getJavaPkgClasses() {
+              return new String[0];
+            }
+
+            @Override
             public String[] getDeleteReturns() {
                  // methods deleted from their return type.
                 return new String[0];
@@ -109,11 +130,201 @@
                 null,                 // derived from
                 new String[] {        // include classes
                     "**"
-                });
+                },
+                new HashSet<String>(0) /* excluded classes */);
         aa.analyze();
         agen.generate();
 
         Set<String> notRenamed = agen.getClassesNotRenamed();
         assertArrayEquals(new String[] { "not/an/actual/ClassName" }, notRenamed.toArray());
+
+    }
+
+    @Test
+    public void testClassRefactoring() throws IOException, LogAbortException {
+        ICreateInfo ci = new ICreateInfo() {
+            @Override
+            public Class<?>[] getInjectedClasses() {
+                // classes to inject in the final JAR
+                return new Class<?>[] {
+                        com.android.tools.layoutlib.create.dataclass.JavaClass.class
+                };
+            }
+
+            @Override
+            public String[] getDelegateMethods() {
+                return new String[0];
+            }
+
+            @Override
+            public String[] getDelegateClassNatives() {
+                return new String[0];
+            }
+
+            @Override
+            public String[] getOverriddenMethods() {
+                // methods to force override
+                return new String[0];
+            }
+
+            @Override
+            public String[] getRenamedClasses() {
+                // classes to rename (so that we can replace them)
+                return new String[0];
+            }
+
+            @Override
+            public String[] getJavaPkgClasses() {
+             // classes to refactor (so that we can replace them)
+                return new String[] {
+                        "java.lang.JavaClass", "com.android.tools.layoutlib.create.dataclass.JavaClass",
+                };
+            }
+
+            @Override
+            public String[] getDeleteReturns() {
+                 // methods deleted from their return type.
+                return new String[0];
+            }
+        };
+
+        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+                null,                 // derived from
+                new String[] {        // include classes
+                    "**"
+                },
+                new HashSet<String>(1));
+        aa.analyze();
+        agen.generate();
+        Map<String, ClassReader> output = parseZip(mOsDestJar);
+        boolean injectedClassFound = false;
+        for (ClassReader cr: output.values()) {
+            TestClassVisitor cv = new TestClassVisitor();
+            cr.accept(cv, 0);
+            injectedClassFound |= cv.mInjectedClassFound;
+        }
+        assertTrue(injectedClassFound);
+    }
+
+    private Map<String,ClassReader> parseZip(String jarPath) throws IOException {
+        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+            ZipFile zip = new ZipFile(jarPath);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            ZipEntry entry;
+            while (entries.hasMoreElements()) {
+                entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                    String className = classReaderToClassName(cr);
+                    classes.put(className, cr);
+                }
+            }
+
+        return classes;
+    }
+
+    private String classReaderToClassName(ClassReader classReader) {
+        if (classReader == null) {
+            return null;
+        } else {
+            return classReader.getClassName().replace('/', '.');
+        }
+    }
+
+    private class TestClassVisitor extends ClassVisitor {
+
+        boolean mInjectedClassFound = false;
+
+        TestClassVisitor() {
+            super(Opcodes.ASM4);
+        }
+
+        @Override
+        public void visit(int version, int access, String name, String signature,
+                String superName, String[] interfaces) {
+            assertTrue(!getBase(name).equals(JAVA_CLASS_NAME));
+            if (name.equals("com/android/tools/layoutlib/create/dataclass/JavaClass")) {
+                mInjectedClassFound = true;
+            }
+            super.visit(version, access, name, signature, superName, interfaces);
+        }
+
+        @Override
+        public FieldVisitor visitField(int access, String name, String desc,
+                String signature, Object value) {
+            assertTrue(testType(Type.getType(desc)));
+            return super.visitField(access, name, desc, signature, value);
+        }
+
+        @SuppressWarnings("hiding")
+        @Override
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                String signature, String[] exceptions) {
+            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+            return new MethodVisitor(Opcodes.ASM4, mv) {
+
+                @Override
+                public void visitFieldInsn(int opcode, String owner, String name,
+                        String desc) {
+                    assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME));
+                    assertTrue(testType(Type.getType(desc)));
+                    super.visitFieldInsn(opcode, owner, name, desc);
+                }
+
+                @Override
+                public void visitLdcInsn(Object cst) {
+                    if (cst instanceof Type) {
+                        assertTrue(testType((Type)cst));
+                    }
+                    super.visitLdcInsn(cst);
+                }
+
+                @Override
+                public void visitTypeInsn(int opcode, String type) {
+                    assertTrue(!getBase(type).equals(JAVA_CLASS_NAME));
+                    super.visitTypeInsn(opcode, type);
+                }
+
+                @Override
+                public void visitMethodInsn(int opcode, String owner, String name,
+                        String desc) {
+                    assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME));
+                    assertTrue(testType(Type.getType(desc)));
+                    super.visitMethodInsn(opcode, owner, name, desc);
+                }
+
+            };
+        }
+
+        private boolean testType(Type type) {
+            int sort = type.getSort();
+            if (sort == Type.OBJECT) {
+                assertTrue(!getBase(type.getInternalName()).equals(JAVA_CLASS_NAME));
+            } else if (sort == Type.ARRAY) {
+                assertTrue(!getBase(type.getElementType().getInternalName())
+                        .equals(JAVA_CLASS_NAME));
+            } else if (sort == Type.METHOD) {
+                boolean r = true;
+                for (Type t : type.getArgumentTypes()) {
+                    r &= testType(t);
+                }
+                return r & testType(type.getReturnType());
+            }
+            return true;
+        }
+
+        private String getBase(String className) {
+            if (className == null) {
+                return null;
+            }
+            int pos = className.indexOf('$');
+            if (pos > 0) {
+                return className.substring(0, pos);
+            }
+            return className;
+        }
     }
 }
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
index 90c6a9c..6211e73 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
@@ -24,7 +24,7 @@
 import org.junit.Test;
 
 /**
- * 
+ *
  */
 public class RenameClassAdapterTest {
 
@@ -36,10 +36,10 @@
         mOuter = new RenameClassAdapter(null, // cv
                                          "com.pack.Old",
                                          "org.blah.New");
-        
+
         mInner = new RenameClassAdapter(null, // cv
                                          "com.pack.Old$Inner",
-                                         "org.blah.New$Inner"); 
+                                         "org.blah.New$Inner");
     }
 
     @After
@@ -72,7 +72,7 @@
         // arrays
         assertEquals("[Lorg.blah.New;",  mOuter.renameTypeDesc("[Lcom.pack.Old;"));
         assertEquals("[[Lorg.blah.New;", mOuter.renameTypeDesc("[[Lcom.pack.Old;"));
-        
+
         assertEquals("[Lorg.blah.New;",  mInner.renameTypeDesc("[Lcom.pack.Old;"));
         assertEquals("[[Lorg.blah.New;", mInner.renameTypeDesc("[[Lcom.pack.Old;"));
     }
@@ -93,10 +93,6 @@
      */
     @Test
     public void testRenameInternalType() {
-        // a descriptor is not left untouched
-        assertEquals("Lorg.blah.New;", mOuter.renameInternalType("Lcom.pack.Old;"));
-        assertEquals("Lorg.blah.New$Inner;", mOuter.renameInternalType("Lcom.pack.Old$Inner;"));
-
         // an actual FQCN
         assertEquals("org.blah.New", mOuter.renameInternalType("com.pack.Old"));
         assertEquals("org.blah.New$Inner", mOuter.renameInternalType("com.pack.Old$Inner"));
@@ -115,6 +111,6 @@
                mOuter.renameMethodDesc("(IDLcom.pack.Old;[Lcom.pack.Old$Inner;)Lcom.pack.Old$Other;"));
     }
 
-    
+
 
 }
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java
similarity index 75%
copy from tools/layoutlib/create/tests/mock_android/view/View.java
copy to tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java
index a80a98d..9b5a918 100644
--- a/tools/layoutlib/create/tests/mock_android/view/View.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package mock_android.view;
+package com.android.tools.layoutlib.create.dataclass;
 
-public class View {
+public final class JavaClass {
 
+    public static final String test = "test";
 }
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
index a7ea74f..60d8efb 100644
--- a/tools/layoutlib/create/tests/data/mock_android.jar
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/data/mock_android.jardesc b/tools/layoutlib/create/tests/data/mock_android.jardesc
deleted file mode 100644
index 95f7591..0000000
--- a/tools/layoutlib/create/tests/data/mock_android.jardesc
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
-<jardesc>
-    <jar path="C:/ralf/google/src/raphael-lapdroid/device/tools/layoutlib/create/tests/data/mock_android.jar"/>
-    <options buildIfNeeded="true" compress="true" descriptionLocation="/layoutlib_create/tests/data/mock_android.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
-    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
-    <selectedProjects/>
-    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
-        <sealing sealJar="false">
-            <packagesToSeal/>
-            <packagesToUnSeal/>
-        </sealing>
-    </manifest>
-    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
-        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.widget"/>
-        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.view"/>
-        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.dummy"/>
-    </selectedElements>
-</jardesc>
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/mock_data/java/lang/JavaClass.java
similarity index 80%
copy from tools/layoutlib/create/tests/mock_android/view/View.java
copy to tools/layoutlib/create/tests/mock_data/java/lang/JavaClass.java
index a80a98d..59612e9 100644
--- a/tools/layoutlib/create/tests/mock_android/view/View.java
+++ b/tools/layoutlib/create/tests/mock_data/java/lang/JavaClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package mock_android.view;
+package java.lang;
 
-public class View {
+public class JavaClass {
 
+    public static String test = "test";
 }
diff --git a/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java b/tools/layoutlib/create/tests/mock_data/mock_android/dummy/InnerTest.java
similarity index 89%
rename from tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
rename to tools/layoutlib/create/tests/mock_data/mock_android/dummy/InnerTest.java
index e355ead..d3a1d05 100644
--- a/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/dummy/InnerTest.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 
 public class InnerTest {
 
@@ -66,13 +67,13 @@
         }
     }
     
-    public <X> void genericMethod1(X a, X[] a) {
+    public <X> void genericMethod1(X a, X[] b) {
     }
 
     public <X, Y> void genericMethod2(X a, List<Y> b) {
     }
 
-    public <X, Y> void genericMethod3(X a, List<Y extends InnerTest> b) {
+    public <X, Y extends InnerTest> void genericMethod3(X a, List<Y> b) {
     }
 
     public <T extends InnerTest> void genericMethod4(T[] a, Collection<T> b, Collection<?> c) {
@@ -85,6 +86,6 @@
         mInnerInstance = m;
         mTheIntEnum = null;
         mGeneric1 = new MyGenerics1();
-        genericMethod(new DerivingClass[0], new ArrayList<DerivingClass>(), new ArrayList<InnerTest>());
+        genericMethod4(new DerivingClass[0], new ArrayList<DerivingClass>(), new ArrayList<InnerTest>());
     }
 }
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/mock_data/mock_android/view/View.java
similarity index 91%
rename from tools/layoutlib/create/tests/mock_android/view/View.java
rename to tools/layoutlib/create/tests/mock_data/mock_android/view/View.java
index a80a98d..84ec8a9 100644
--- a/tools/layoutlib/create/tests/mock_android/view/View.java
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/view/View.java
@@ -16,6 +16,10 @@
 
 package mock_android.view;
 
+import java.lang.JavaClass;
+
 public class View {
 
+        String x = JavaClass.test;
+
 }
diff --git a/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java b/tools/layoutlib/create/tests/mock_data/mock_android/view/ViewGroup.java
similarity index 100%
rename from tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
rename to tools/layoutlib/create/tests/mock_data/mock_android/view/ViewGroup.java
diff --git a/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java b/tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java
similarity index 100%
rename from tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
rename to tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java
diff --git a/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java b/tools/layoutlib/create/tests/mock_data/mock_android/widget/TableLayout.java
similarity index 100%
rename from tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
rename to tools/layoutlib/create/tests/mock_data/mock_android/widget/TableLayout.java