Merge "Add new sensor types." into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index 43791c7..104293c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -180,6 +180,7 @@
 	core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
 	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
 	core/java/com/android/internal/backup/IBackupTransport.aidl \
+	core/java/com/android/internal/backup/IObbBackupService.aidl \
 	core/java/com/android/internal/policy/IFaceLockCallback.aidl \
 	core/java/com/android/internal/policy/IFaceLockInterface.aidl \
 	core/java/com/android/internal/os/IDropBoxManagerService.aidl \
diff --git a/api/current.txt b/api/current.txt
index b173c70..4eb6a0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2134,8 +2134,10 @@
   public abstract class AbstractAccountAuthenticator {
     ctor public AbstractAccountAuthenticator(android.content.Context);
     method public abstract android.os.Bundle addAccount(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
+    method public android.os.Bundle addAccountFromCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
+    method public android.os.Bundle getAccountCredentialsForCloning(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public android.os.Bundle getAccountRemovalAllowed(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
@@ -11282,6 +11284,7 @@
     method public static android.media.MediaCodec createByCodecName(java.lang.String);
     method public static android.media.MediaCodec createDecoderByType(java.lang.String);
     method public static android.media.MediaCodec createEncoderByType(java.lang.String);
+    method public final android.view.Surface createInputSurface();
     method public final int dequeueInputBuffer(long);
     method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
     method public final void flush();
@@ -11295,6 +11298,7 @@
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
     method public final void setVideoScalingMode(int);
+    method public final void signalEndOfInputStream();
     method public final void start();
     method public final void stop();
     field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
@@ -11374,6 +11378,7 @@
     field public static final int COLOR_FormatRawBayer10bit = 31; // 0x1f
     field public static final int COLOR_FormatRawBayer8bit = 30; // 0x1e
     field public static final int COLOR_FormatRawBayer8bitcompressed = 32; // 0x20
+    field public static final int COLOR_FormatSurface = 2130708361; // 0x7f000789
     field public static final int COLOR_FormatYCbYCr = 25; // 0x19
     field public static final int COLOR_FormatYCrYCb = 26; // 0x1a
     field public static final int COLOR_FormatYUV411PackedPlanar = 18; // 0x12
@@ -11596,6 +11601,20 @@
     field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public final class MediaMuxer {
+    ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
+    method public int addTrack(android.media.MediaFormat);
+    method public void release();
+    method public void start();
+    method public void stop();
+    method public void writeSampleData(int, java.nio.ByteBuffer, android.media.MediaCodec.BufferInfo);
+    field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
+  }
+
+  public static final class MediaMuxer.OutputFormat {
+    field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+  }
+
   public class MediaPlayer {
     ctor public MediaPlayer();
     method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -11809,6 +11828,7 @@
     method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
     method public android.media.MediaRouter.RouteCategory getCategoryAt(int);
     method public int getCategoryCount();
+    method public android.media.MediaRouter.RouteInfo getDefaultRoute();
     method public android.media.MediaRouter.RouteInfo getRouteAt(int);
     method public int getRouteCount();
     method public android.media.MediaRouter.RouteInfo getSelectedRoute(int);
@@ -14128,6 +14148,7 @@
     method public static int eglGetError();
     method public static boolean eglInitialize(android.opengl.EGLDisplay, int[], int, int[], int);
     method public static boolean eglMakeCurrent(android.opengl.EGLDisplay, android.opengl.EGLSurface, android.opengl.EGLSurface, android.opengl.EGLContext);
+    method public static boolean eglPresentationTimeANDROID(android.opengl.EGLDisplay, android.opengl.EGLSurface, long);
     method public static int eglQueryAPI();
     method public static boolean eglQueryContext(android.opengl.EGLDisplay, android.opengl.EGLContext, int, int[], int);
     method public static java.lang.String eglQueryString(android.opengl.EGLDisplay, int);
@@ -22528,19 +22549,19 @@
     method public static java.text.DateFormat getMediumDateFormat(android.content.Context);
     method public static java.text.DateFormat getTimeFormat(android.content.Context);
     method public static boolean is24HourFormat(android.content.Context);
-    field public static final char AM_PM = 97; // 0x0061 'a'
-    field public static final char CAPITAL_AM_PM = 65; // 0x0041 'A'
-    field public static final char DATE = 100; // 0x0064 'd'
-    field public static final char DAY = 69; // 0x0045 'E'
-    field public static final char HOUR = 104; // 0x0068 'h'
-    field public static final char HOUR_OF_DAY = 107; // 0x006b 'k'
-    field public static final char MINUTE = 109; // 0x006d 'm'
-    field public static final char MONTH = 77; // 0x004d 'M'
-    field public static final char QUOTE = 39; // 0x0027 '\''
-    field public static final char SECONDS = 115; // 0x0073 's'
-    field public static final char STANDALONE_MONTH = 76; // 0x004c 'L'
-    field public static final char TIME_ZONE = 122; // 0x007a 'z'
-    field public static final char YEAR = 121; // 0x0079 'y'
+    field public static final deprecated char AM_PM = 97; // 0x0061 'a'
+    field public static final deprecated char CAPITAL_AM_PM = 65; // 0x0041 'A'
+    field public static final deprecated char DATE = 100; // 0x0064 'd'
+    field public static final deprecated char DAY = 69; // 0x0045 'E'
+    field public static final deprecated char HOUR = 104; // 0x0068 'h'
+    field public static final deprecated char HOUR_OF_DAY = 107; // 0x006b 'k'
+    field public static final deprecated char MINUTE = 109; // 0x006d 'm'
+    field public static final deprecated char MONTH = 77; // 0x004d 'M'
+    field public static final deprecated char QUOTE = 39; // 0x0027 '\''
+    field public static final deprecated char SECONDS = 115; // 0x0073 's'
+    field public static final deprecated char STANDALONE_MONTH = 76; // 0x004c 'L'
+    field public static final deprecated char TIME_ZONE = 122; // 0x007a 'z'
+    field public static final deprecated char YEAR = 121; // 0x0079 'y'
   }
 
   public class DateUtils {
@@ -24167,6 +24188,7 @@
     field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
     field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
     field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
     field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
     field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
     field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
@@ -24178,6 +24200,7 @@
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
     field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
     field public static final int SOURCE_UNKNOWN = 0; // 0x0
   }
@@ -24190,6 +24213,7 @@
     method public float getMin();
     method public float getRange();
     method public int getSource();
+    method public boolean isFromSource(int);
   }
 
   public abstract class InputEvent implements android.os.Parcelable {
@@ -24198,6 +24222,7 @@
     method public abstract int getDeviceId();
     method public abstract long getEventTime();
     method public abstract int getSource();
+    method public boolean isFromSource(int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 046ccca..73fd660 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -22,6 +22,7 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.io.IOException;
 import java.util.ArrayList;
 
 public final class Backup {
@@ -64,6 +65,7 @@
     private void doFullBackup(int socketFd) {
         ArrayList<String> packages = new ArrayList<String>();
         boolean saveApks = false;
+        boolean saveObbs = false;
         boolean saveShared = false;
         boolean doEverything = false;
         boolean allIncludesSystem = true;
@@ -75,6 +77,10 @@
                     saveApks = true;
                 } else if ("-noapk".equals(arg)) {
                     saveApks = false;
+                } else if ("-obb".equals(arg)) {
+                    saveObbs = true;
+                } else if ("-noobb".equals(arg)) {
+                    saveObbs = false;
                 } else if ("-shared".equals(arg)) {
                     saveShared = true;
                 } else if ("-noshared".equals(arg)) {
@@ -104,23 +110,37 @@
             return;
         }
 
+        ParcelFileDescriptor fd = null;
         try {
-            ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
+            fd = ParcelFileDescriptor.adoptFd(socketFd);
             String[] packArray = new String[packages.size()];
-            mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything, allIncludesSystem,
-                    packages.toArray(packArray));
+            mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything,
+                    allIncludesSystem, packages.toArray(packArray));
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for backup");
+        } finally {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {}
+            }
         }
     }
 
     private void doFullRestore(int socketFd) {
         // No arguments to restore
+        ParcelFileDescriptor fd = null;
         try {
-            ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
+            fd = ParcelFileDescriptor.adoptFd(socketFd);
             mBackupManager.fullRestore(fd);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for restore");
+        } finally {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {}
+            }
         }
     }
 
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 22ce841..98c82b5 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1149,10 +1149,7 @@
 
         ClearDataObserver obs = new ClearDataObserver();
         try {
-            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, userId)) {
-                System.err.println("Failed");
-            }
-
+            ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, userId);
             synchronized (obs) {
                 while (!obs.finished) {
                     try {
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index fa46689..dbc9051 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -505,7 +505,6 @@
     }
 
     /**
-     * @hide
      * Returns a Bundle that contains whatever is required to clone the account on a different
      * user. The Bundle is passed to the authenticator instance in the target user via
      * {@link #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)}.
@@ -529,7 +528,6 @@
     }
 
     /**
-     * @hide
      * Creates an account based on credentials provided by the authenticator instance of another
      * user on the device, who has chosen to share the account with this user.
      * @param response to send the result back to the AccountManager, will never be null
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index a1a147a..7e0a27a 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -123,13 +123,13 @@
 
         if (mToggleMode) {
             if (mRemoteActive) {
-                mRouter.selectRouteInt(mRouteTypes, mRouter.getSystemAudioRoute());
+                mRouter.selectRouteInt(mRouteTypes, mRouter.getDefaultRoute());
             } else {
                 final int N = mRouter.getRouteCount();
                 for (int i = 0; i < N; i++) {
                     final RouteInfo route = mRouter.getRouteAt(i);
                     if ((route.getSupportedTypes() & mRouteTypes) != 0 &&
-                            route != mRouter.getSystemAudioRoute()) {
+                            route != mRouter.getDefaultRoute()) {
                         mRouter.selectRouteInt(mRouteTypes, route);
                     }
                 }
@@ -216,7 +216,7 @@
 
     void updateRemoteIndicator() {
         final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes);
-        final boolean isRemote = selected != mRouter.getSystemAudioRoute();
+        final boolean isRemote = selected != mRouter.getDefaultRoute();
         final boolean isConnecting = selected != null &&
                 selected.getStatusCode() == RouteInfo.STATUS_CONNECTING;
 
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 44aa06f..3425765 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -472,6 +472,7 @@
                 File efLocation = getExternalFilesDir(null);
                 if (efLocation != null) {
                     basePath = getExternalFilesDir(null).getCanonicalPath();
+                    mode = -1;  // < 0 is a token to skip attempting a chmod()
                 }
             }
         } else {
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 2fe08f3..cb0737e 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -89,7 +89,7 @@
      *    last modification time of the output file.  if the {@code mode} parameter is
      *    negative then this parameter will be ignored.
      * @param outFile Location within the filesystem to place the data.  This must point
-     *    to a location that is writeable by the caller, prefereably using an absolute path.
+     *    to a location that is writeable by the caller, preferably using an absolute path.
      * @throws IOException
      */
     static public void restoreFile(ParcelFileDescriptor data,
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index acdd0b5..bb4f5f1 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -152,6 +152,8 @@
      * @param fd The file descriptor to which a 'tar' file stream is to be written
      * @param includeApks If <code>true</code>, the resulting tar stream will include the
      *     application .apk files themselves as well as their data.
+     * @param includeObbs If <code>true</code>, the resulting tar stream will include any
+     *     application expansion (OBB) files themselves belonging to each application.
      * @param includeShared If <code>true</code>, the resulting tar stream will include
      *     the contents of the device's shared storage (SD card or equivalent).
      * @param allApps If <code>true</code>, the resulting tar stream will include all
@@ -164,8 +166,9 @@
      * @param packageNames The package names of the apps whose data (and optionally .apk files)
      *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
      */
-    void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
-            boolean allApps, boolean allIncludesSystem, in String[] packageNames);
+    void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+            boolean includeShared, boolean allApps, boolean allIncludesSystem,
+            in String[] packageNames);
 
     /**
      * Restore device content from the data stream passed through the given socket.  The
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index a470e70..f7933d3 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -408,12 +408,10 @@
      * @hide
      */
     protected void onProvidersChanged(int userId) {
-        checkUserMatch(userId);
         // Does nothing
     }
 
     void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) {
-        checkUserMatch(userId);
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
@@ -424,7 +422,6 @@
     }
 
     void viewDataChanged(int appWidgetId, int viewId, int userId) {
-        checkUserMatch(userId);
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
@@ -434,16 +431,6 @@
         }
     }
 
-    // Ensure that the userId passed to us agrees with the one associated with this instance
-    // of AppWidgetHost.
-    // TODO: This should be removed in production code.
-    private void checkUserMatch(int userId) {
-        if (userId != mContext.getUserId()) {
-            throw new IllegalStateException(
-                "User ids don't match, userId=" + userId + ", mUserId=" + mContext.getUserId());
-        }
-    }
-
     /**
      * Clear the list of Views that have been created by this AppWidgetHost.
      */
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index aaa0917..288d55f 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -488,7 +488,8 @@
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(fis, null);
             int eventType = parser.getEventType();
-            while (eventType != XmlPullParser.START_TAG) {
+            while (eventType != XmlPullParser.START_TAG
+                    && eventType != XmlPullParser.END_DOCUMENT) {
                 eventType = parser.next();
             }
             String tagName = parser.getName();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 24a0bb5..805b05e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -233,11 +233,13 @@
     }
 
     /**
-     * Return the character sequence associated with a particular resource ID for a particular
-     * numerical quantity.
-     *
-     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
-     * Resources</a> for more on quantity strings.
+     * Returns the character sequence necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity.
+     * Note that the character sequence is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
      *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
@@ -345,14 +347,17 @@
     }
 
     /**
-     * Return the string value associated with a particular resource ID for a particular
-     * numerical quantity, substituting the format arguments as defined in
-     * {@link java.util.Formatter} and {@link java.lang.String#format}. It will be
-     * stripped of any styled text information.
-     * {@more}
+     * Formats the string necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity, using the given arguments.
+     * Note that the string is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
      *
-     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
-     * Resources</a> for more on quantity strings.
+     * <p>Substitution of format arguments works as if using
+     * {@link java.util.Formatter} and {@link java.lang.String#format}.
+     * The resulting string will be stripped of any styled text information.
      *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
@@ -373,11 +378,13 @@
     }
 
     /**
-     * Return the string value associated with a particular resource ID for a particular
-     * numerical quantity.
-     *
-     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
-     * Resources</a> for more on quantity strings.
+     * Returns the string necessary for grammatically correct pluralization
+     * of the given resource ID for the given quantity.
+     * Note that the string is selected based solely on grammatical necessity,
+     * and that such rules differ between languages. Do not assume you know which string
+     * will be returned for a given quantity. See
+     * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
+     * for more detail.
      *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index b9362da..5d13a18 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -92,6 +92,11 @@
 
     public void setInterfaceName(String iface) {
         mIfaceName = iface;
+        ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
+        for (RouteInfo route : mRoutes) {
+            newRoutes.add(routeWithInterface(route));
+        }
+        mRoutes = newRoutes;
     }
 
     public String getInterfaceName() {
@@ -130,9 +135,25 @@
         mDomains = domains;
     }
 
-    public void addRoute(RouteInfo route) {
-        if (route != null) mRoutes.add(route);
+    private RouteInfo routeWithInterface(RouteInfo route) {
+        return new RouteInfo(
+            route.getDestination(),
+            route.getGateway(),
+            mIfaceName);
     }
+
+    public void addRoute(RouteInfo route) {
+        if (route != null) {
+            String routeIface = route.getInterface();
+            if (routeIface != null && !routeIface.equals(mIfaceName)) {
+                throw new IllegalArgumentException(
+                   "Route added with non-matching interface: " + routeIface +
+                   " vs. " + mIfaceName);
+            }
+            mRoutes.add(routeWithInterface(route));
+        }
+    }
+
     public Collection<RouteInfo> getRoutes() {
         return Collections.unmodifiableCollection(mRoutes);
     }
@@ -349,7 +370,7 @@
     public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
         /*
          * Duplicate the RouteInfos into removed, we will be removing
-         * routes which are common between mDnses and target
+         * routes which are common between mRoutes and target
          * leaving the routes that are different. And route address which
          * are in target but not in mRoutes are placed in added.
          */
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 112e143..3a7abc0 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -29,6 +29,17 @@
 /**
  * A simple container for route information.
  *
+ * In order to be used, a route must have a destination prefix and:
+ *
+ * - A gateway address (next-hop, for gatewayed routes), or
+ * - An interface (for directly-connected routes), or
+ * - Both a gateway and an interface.
+ *
+ * This class does not enforce these constraints because there is code that
+ * uses RouteInfo objects to store directly-connected routes without interfaces.
+ * Such objects cannot be used directly, but can be put into a LinkProperties
+ * object which then specifies the interface.
+ *
  * @hide
  */
 public class RouteInfo implements Parcelable {
@@ -42,10 +53,30 @@
      */
     private final InetAddress mGateway;
 
+    /**
+     * The interface for this route.
+     */
+    private final String mInterface;
+
     private final boolean mIsDefault;
     private final boolean mIsHost;
 
-    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+    /**
+     * Constructs a RouteInfo object.
+     *
+     * If destination is null, then gateway must be specified and the
+     * constructed route is either the IPv4 default route <code>0.0.0.0</code>
+     * if @gateway is an instance of {@link Inet4Address}, or the IPv6 default
+     * route <code>::/0</code> if gateway is an instance of
+     * {@link Inet6Address}.
+     *
+     * destination and gateway may not both be null.
+     *
+     * @param destination the destination prefix
+     * @param gateway the IP address to route packets through
+     * @param iface the interface name to send packets on
+     */
+    public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
         if (destination == null) {
             if (gateway != null) {
                 if (gateway instanceof Inet4Address) {
@@ -55,7 +86,8 @@
                 }
             } else {
                 // no destination, no gateway. invalid.
-                throw new RuntimeException("Invalid arguments passed in.");
+                throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
+                                                   destination);
             }
         }
         if (gateway == null) {
@@ -68,29 +100,34 @@
         mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
                 destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
         mGateway = gateway;
+        mInterface = iface;
         mIsDefault = isDefault();
         mIsHost = isHost();
     }
 
+    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+        this(destination, gateway, null);
+    }
+
     public RouteInfo(InetAddress gateway) {
-        this(null, gateway);
+        this(null, gateway, null);
     }
 
     public RouteInfo(LinkAddress host) {
-        this(host, null);
+        this(host, null, null);
     }
 
-    public static RouteInfo makeHostRoute(InetAddress host) {
-        return makeHostRoute(host, null);
+    public static RouteInfo makeHostRoute(InetAddress host, String iface) {
+        return makeHostRoute(host, null, iface);
     }
 
-    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) {
+    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
         if (host == null) return null;
 
         if (host instanceof Inet4Address) {
-            return new RouteInfo(new LinkAddress(host, 32), gateway);
+            return new RouteInfo(new LinkAddress(host, 32), gateway, iface);
         } else {
-            return new RouteInfo(new LinkAddress(host, 128), gateway);
+            return new RouteInfo(new LinkAddress(host, 128), gateway, iface);
         }
     }
 
@@ -119,6 +156,10 @@
         return mGateway;
     }
 
+    public String getInterface() {
+        return mInterface;
+    }
+
     public boolean isDefaultRoute() {
         return mIsDefault;
     }
@@ -153,6 +194,8 @@
             dest.writeByte((byte) 1);
             dest.writeByteArray(mGateway.getAddress());
         }
+
+        dest.writeString(mInterface);
     }
 
     @Override
@@ -171,14 +214,19 @@
                 target.getGateway() == null
                 : mGateway.equals(target.getGateway());
 
-        return sameDestination && sameAddress
+        boolean sameInterface = (mInterface == null) ?
+                target.getInterface() == null
+                : mInterface.equals(target.getInterface());
+
+        return sameDestination && sameAddress && sameInterface
             && mIsDefault == target.mIsDefault;
     }
 
     @Override
     public int hashCode() {
-        return (mDestination == null ? 0 : mDestination.hashCode())
-            + (mGateway == null ? 0 :mGateway.hashCode())
+        return (mDestination == null ? 0 : mDestination.hashCode() * 41)
+            + (mGateway == null ? 0 :mGateway.hashCode() * 47)
+            + (mInterface == null ? 0 :mInterface.hashCode() * 67)
             + (mIsDefault ? 3 : 7);
     }
 
@@ -206,13 +254,15 @@
                 } catch (UnknownHostException e) {}
             }
 
+            String iface = in.readString();
+
             LinkAddress dest = null;
 
             if (destAddr != null) {
                 dest = new LinkAddress(destAddr, prefix);
             }
 
-            return new RouteInfo(dest, gateway);
+            return new RouteInfo(dest, gateway, iface);
         }
 
         public RouteInfo[] newArray(int size) {
@@ -220,13 +270,9 @@
         }
     };
 
-    private boolean matches(InetAddress destination) {
+    protected boolean matches(InetAddress destination) {
         if (destination == null) return false;
 
-        // if the destination is present and the route is default.
-        // return true
-        if (isDefault()) return true;
-
         // match the route destination and destination with prefix length
         InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
                 mDestination.getNetworkPrefixLength());
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index c0a894b..2a2f7cf 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -23,8 +23,8 @@
 import java.net.Socket;
 import java.net.SocketException;
 import java.security.KeyManagementException;
+import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
-import java.security.interfaces.ECPrivateKey;
 import javax.net.SocketFactory;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -89,7 +89,7 @@
     private TrustManager[] mTrustManagers = null;
     private KeyManager[] mKeyManagers = null;
     private byte[] mNpnProtocols = null;
-    private ECPrivateKey mChannelIdPrivateKey = null;
+    private PrivateKey mChannelIdPrivateKey = null;
 
     private final int mHandshakeTimeoutMillis;
     private final SSLClientSessionCache mSessionCache;
@@ -321,7 +321,7 @@
     }
 
     /**
-     * Sets the {@link ECPrivateKey} to be used for TLS Channel ID by connections made by this
+     * Sets the private key to be used for TLS Channel ID by connections made by this
      * factory.
      *
      * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
@@ -330,7 +330,7 @@
      *
      * @hide
      */
-    public void setChannelIdPrivateKey(ECPrivateKey privateKey) {
+    public void setChannelIdPrivateKey(PrivateKey privateKey) {
         mChannelIdPrivateKey = privateKey;
     }
 
diff --git a/core/java/android/nfc/tech/NfcBarcode.java b/core/java/android/nfc/tech/NfcBarcode.java
index 3149857..76627de 100644
--- a/core/java/android/nfc/tech/NfcBarcode.java
+++ b/core/java/android/nfc/tech/NfcBarcode.java
@@ -86,6 +86,28 @@
     /**
      * Returns the barcode of an NfcBarcode tag.
      *
+     * <p> Tags of {@link #TYPE_KOVIO} return 16 bytes:
+     *     <ul>
+     *     <p> The first byte is 0x80 ORd with a manufacturer ID, corresponding
+     *       to ISO/IEC 7816-6.
+     *     <p> The second byte describes the payload data format. Defined data
+     *       format types include the following:<ul>
+     *       <li>0x00: Reserved for manufacturer assignment</li>
+     *       <li>0x01: 96-bit URL with "http://www." prefix</li>
+     *       <li>0x02: 96-bit URL with "https://www." prefix</li>
+     *       <li>0x03: 96-bit URL with "http://" prefix</li>
+     *       <li>0x04: 96-bit URL with "https://" prefix</li>
+     *       <li>0x05: 96-bit GS1 EPC</li>
+     *       <li>0x06-0xFF: reserved</li>
+     *       </ul>
+     *     <p>The following 12 bytes are payload:<ul>
+     *       <li> In case of a URL payload, the payload is encoded in US-ASCII,
+     *            following the limitations defined in RF3987,
+     *            {@see http://www.ietf.org/rfc/rfc3987.txt}</li>
+     *       <li> In case of GS1 EPC daya, {@see http://www.gs1.org/gsmp/kc/epcglobal/tds/}
+     *            for more details.</li></ul>
+     *     <p>The last 2 bytes comprise the CRC.
+     *     </ul>
      * <p>Does not cause any RF activity and does not block.
      *
      * @return a byte array containing the barcode
diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java
index 3493ea7..0e2c7c1 100644
--- a/core/java/android/nfc/tech/TagTechnology.java
+++ b/core/java/android/nfc/tech/TagTechnology.java
@@ -50,6 +50,7 @@
  * <ul>
  * <li>{@link MifareClassic}
  * <li>{@link MifareUltralight}
+ * <li>{@link NfcBarcode}
  * <li>{@link NdefFormatable} must only be enumerated on tags for which this Android device
  * is capable of formatting. Proprietary knowledge is often required to format a tag
  * to make it NDEF compatible.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 18a0018..b8769b4 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -218,7 +218,7 @@
             return;
         }
         if (mMap == null) {
-            mMap = new HashMap<String, Object>();
+            mMap = new HashMap<String, Object>(N);
         }
         mParcelledData.readMapInternal(mMap, N, mClassLoader);
         mParcelledData.recycle();
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 7e230ac..be4663d 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -34,178 +34,72 @@
 import libcore.icu.LocaleData;
 
 /**
-    Utility class for producing strings with formatted date/time.
-
-    <p>
-    Most callers should avoid supplying their own format strings to this
-    class' {@code format} methods and rely on the correctly localized ones
-    supplied by the system. This class' factory methods return
-    appropriately-localized {@link java.text.DateFormat} instances, suitable
-    for both formatting and parsing dates. For the canonical documentation
-    of format strings, see {@link java.text.SimpleDateFormat}.
-    </p>
-    <p>
-    The format methods in this class takes as inputs a format string and a representation of a date/time.
-    The format string controls how the output is generated.
-    This class only supports a subset of the full Unicode specification.
-    Use {@link java.text.SimpleDateFormat} if you need more.
-    Formatting characters may be repeated in order to get more detailed representations
-    of that field.  For instance, the format character &apos;M&apos; is used to
-    represent the month.  Depending on how many times that character is repeated
-    you get a different representation.
-    </p>
-    <p>
-    For the month of September:<br/>
-    M -&gt; 9<br/>
-    MM -&gt; 09<br/>
-    MMM -&gt; Sep<br/>
-    MMMM -&gt; September
-    </p>
-    <p>
-    The effects of the duplication vary depending on the nature of the field.
-    See the notes on the individual field formatters for details.  For purely numeric
-    fields such as <code>HOUR</code> adding more copies of the designator will
-    zero-pad the value to that number of characters.
-    </p>
-    <p>
-    For 7 minutes past the hour:<br/>
-    m -&gt; 7<br/>
-    mm -&gt; 07<br/>
-    mmm -&gt; 007<br/>
-    mmmm -&gt; 0007
-    </p>
-    <p>
-    Examples for April 6, 1970 at 3:23am:<br/>
-    &quot;MM/dd/yy h:mmaa&quot; -&gt; &quot;04/06/70 3:23am&quot<br/>
-    &quot;MMM dd, yyyy h:mmaa&quot; -&gt; &quot;Apr 6, 1970 3:23am&quot<br/>
-    &quot;MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;April 6, 1970 3:23am&quot<br/>
-    &quot;E, MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;Mon, April 6, 1970 3:23am&<br/>
-    &quot;EEEE, MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;Monday, April 6, 1970 3:23am&quot;<br/>
-    &quot;&apos;Noteworthy day: &apos;M/d/yy&quot; -&gt; &quot;Noteworthy day: 4/6/70&quot;
+ * Utility class for producing strings with formatted date/time.
+ *
+ * <p>Most callers should avoid supplying their own format strings to this
+ * class' {@code format} methods and rely on the correctly localized ones
+ * supplied by the system. This class' factory methods return
+ * appropriately-localized {@link java.text.DateFormat} instances, suitable
+ * for both formatting and parsing dates. For the canonical documentation
+ * of format strings, see {@link java.text.SimpleDateFormat}.
+ *
+ * <p>The format methods in this class implement a subset of Unicode
+ * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns.
+ * The subset supported by this class includes the following format characters:
+ * {@code acdEHhLKkLMmsyz}. See {@link java.text.SimpleDateFormat} for more documentation
+ * about patterns, or if you need a more compete implementation.
  */
-
 public class DateFormat {
-    /**
-        Text in the format string that should be copied verbatim rather that
-        interpreted as formatting codes must be surrounded by the <code>QUOTE</code>
-        character.  If you need to embed a literal <code>QUOTE</code> character in
-        the output text then use two in a row.
-     */
+    /** @deprecated Use a literal {@code '} instead. */
+    @Deprecated
     public  static final char    QUOTE                  =    '\'';
-    
-    /**
-        This designator indicates whether the <code>HOUR</code> field is before
-        or after noon.  The output is lower-case.
-     
-        Examples:
-        a -> a or p
-        aa -> am or pm
-     */
+
+    /** @deprecated Use a literal {@code 'a'} instead. */
+    @Deprecated
     public  static final char    AM_PM                  =    'a';
 
-    /**
-        This designator indicates whether the <code>HOUR</code> field is before
-        or after noon.  The output is capitalized.
-     
-        Examples:
-        A -> A or P
-        AA -> AM or PM
-     */
+    /** @deprecated Use a literal {@code 'a'} instead; 'A' was always equivalent to 'a'. */
+    @Deprecated
     public  static final char    CAPITAL_AM_PM          =    'A';
 
-    /**
-        This designator indicates the day of the month.
-         
-        Examples for the 9th of the month:
-        d -> 9
-        dd -> 09
-     */
+    /** @deprecated Use a literal {@code 'd'} instead. */
+    @Deprecated
     public  static final char    DATE                   =    'd';
 
-    /**
-        This designator indicates the name of the day of the week.
-     
-        Examples for Sunday:
-        E -> Sun
-        EEEE -> Sunday
-     */
+    /** @deprecated Use a literal {@code 'E'} instead. */
+    @Deprecated
     public  static final char    DAY                    =    'E';
 
-    /**
-        This designator indicates the hour of the day in 12 hour format.
-     
-        Examples for 3pm:
-        h -> 3
-        hh -> 03
-     */
+    /** @deprecated Use a literal {@code 'h'} instead. */
+    @Deprecated
     public  static final char    HOUR                   =    'h';
 
-    /**
-        This designator indicates the hour of the day in 24 hour format.
-     
-        Example for 3pm:
-        k -> 15
-
-        Examples for midnight:
-        k -> 0
-        kk -> 00
-     */
+    /** @deprecated Use a literal {@code 'k'} instead. */
+    @Deprecated
     public  static final char    HOUR_OF_DAY            =    'k';
 
-    /**
-        This designator indicates the minute of the hour.
-     
-        Examples for 7 minutes past the hour:
-        m -> 7
-        mm -> 07
-     */
+    /** @deprecated Use a literal {@code 'm'} instead. */
+    @Deprecated
     public  static final char    MINUTE                 =    'm';
 
-    /**
-        This designator indicates the month of the year. See also
-        {@link #STANDALONE_MONTH}.
-     
-        Examples for September:
-        M -> 9
-        MM -> 09
-        MMM -> Sep
-        MMMM -> September
-     */
+    /** @deprecated Use a literal {@code 'M'} instead. */
+    @Deprecated
     public  static final char    MONTH                  =    'M';
 
-    /**
-        This designator indicates the standalone month of the year,
-        necessary in some format strings in some languages. For
-        example, Russian distinguishes between the "June" in
-        "June" and that in "June 2010".
-     */
+    /** @deprecated Use a literal {@code 'L'} instead. */
+    @Deprecated
     public  static final char    STANDALONE_MONTH       =    'L';
 
-    /**
-        This designator indicates the seconds of the minute.
-     
-        Examples for 7 seconds past the minute:
-        s -> 7
-        ss -> 07
-     */
+    /** @deprecated Use a literal {@code 's'} instead. */
+    @Deprecated
     public  static final char    SECONDS                =    's';
 
-    /**
-        This designator indicates the offset of the timezone from GMT.
-     
-        Example for US/Pacific timezone:
-        z -> -0800
-        zz -> PST
-     */
+    /** @deprecated Use a literal {@code 'z'} instead. */
+    @Deprecated
     public  static final char    TIME_ZONE              =    'z';
 
-    /**
-        This designator indicates the year.
-     
-        Examples for 2006
-        y -> 06
-        yyyy -> 2006
-     */
+    /** @deprecated Use a literal {@code 'y'} instead. */
+    @Deprecated
     public  static final char    YEAR                   =    'y';
 
 
@@ -233,8 +127,7 @@
             }
 
             java.text.DateFormat natural =
-                java.text.DateFormat.getTimeInstance(
-                    java.text.DateFormat.LONG, locale);
+                java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
 
             if (natural instanceof SimpleDateFormat) {
                 SimpleDateFormat sdf = (SimpleDateFormat) natural;
@@ -273,7 +166,7 @@
     }
 
     /**
-     * Returns a {@link java.text.DateFormat} object that can format the date 
+     * Returns a {@link java.text.DateFormat} object that can format the date
      * in short form (such as 12/31/1999) according
      * to the current locale and the user's date-order preference.
      * @param context the application context
@@ -298,7 +191,6 @@
     public static java.text.DateFormat getDateFormatForSetting(Context context,
                                                                String value) {
         String format = getDateFormatStringForSetting(context, value);
-
         return new java.text.SimpleDateFormat(format);
     }
 
@@ -342,10 +234,10 @@
         value = context.getString(R.string.numeric_date_format);
         return value;
     }
-    
+
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in long form (such as December 31, 1999) for the current locale.
+     * in long form (such as {@code Monday, January 3, 2000}) for the current locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
@@ -355,7 +247,7 @@
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in medium form (such as Dec. 31, 1999) for the current locale.
+     * in medium form (such as {@code Jan 3, 2000}) for the current locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
@@ -365,13 +257,13 @@
 
     /**
      * Gets the current date format stored as a char array. The array will contain
-     * 3 elements ({@link #DATE}, {@link #MONTH}, and {@link #YEAR}) in the order    
+     * 3 elements ({@link #DATE}, {@link #MONTH}, and {@link #YEAR}) in the order
      * specified by the user's format preference.  Note that this order is
      * only appropriate for all-numeric dates; spelled-out (MEDIUM and LONG)
      * dates will generally contain other punctuation, spaces, or words,
      * not just the day, month, and year, and not necessarily in the same
      * order returned here.
-     */    
+     */
     public static char[] getDateFormatOrder(Context context) {
         char[] order = new char[] {DATE, MONTH, YEAR};
         String value = getDateFormatString(context);
@@ -401,7 +293,7 @@
         }
         return order;
     }
-    
+
     private static String getDateFormatString(Context context) {
         String value = Settings.System.getString(context.getContentResolver(),
                 Settings.System.DATE_FORMAT);
@@ -410,7 +302,7 @@
     }
 
     /**
-     * Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a 
+     * Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a
      * CharSequence containing the requested date.
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
      * @param inTimeInMillis in milliseconds since Jan 1, 1970 GMT
@@ -428,22 +320,20 @@
      * @return a {@link CharSequence} containing the requested text
      */
     public static CharSequence format(CharSequence inFormat, Date inDate) {
-        Calendar    c = new GregorianCalendar();
-        
+        Calendar c = new GregorianCalendar();
         c.setTime(inDate);
-        
         return format(inFormat, c);
     }
 
     /**
      * Indicates whether the specified format string contains seconds.
-     * 
+     *
      * Always returns false if the input format is null.
-     * 
+     *
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
-     *                 
+     *
      * @return true if the format string contains {@link #SECONDS}, false otherwise
-     * 
+     *
      * @hide
      */
     public static boolean hasSeconds(CharSequence inFormat) {
@@ -508,24 +398,23 @@
     }
 
     /**
-     * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence 
+     * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence
      * containing the requested date.
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
      * @param inDate the date to format
      * @return a {@link CharSequence} containing the requested text
      */
     public static CharSequence format(CharSequence inFormat, Calendar inDate) {
-        SpannableStringBuilder      s = new SpannableStringBuilder(inFormat);
-        int             c;
-        int             count;
+        SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
+        int count;
+
+        LocaleData localeData = LocaleData.get(Locale.getDefault());
 
         int len = inFormat.length();
 
         for (int i = 0; i < len; i += count) {
-            int temp;
-
             count = 1;
-            c = s.charAt(i);
+            int c = s.charAt(i);
 
             if (c == QUOTE) {
                 count = appendQuotedText(s, i, len);
@@ -538,102 +427,102 @@
             }
 
             String replacement;
-
             switch (c) {
-                case AM_PM:
-                    replacement = DateUtils.getAMPMString(inDate.get(Calendar.AM_PM));
+                case 'A':
+                case 'a':
+                    replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
                     break;
-                                        
-                case CAPITAL_AM_PM:
-                    //FIXME: this is the same as AM_PM? no capital?
-                    replacement = DateUtils.getAMPMString(inDate.get(Calendar.AM_PM));
-                    break;
-                
-                case DATE:
+                case 'd':
                     replacement = zeroPad(inDate.get(Calendar.DATE), count);
                     break;
-                    
-                case DAY:
-                    temp = inDate.get(Calendar.DAY_OF_WEEK);
-                    replacement = DateUtils.getDayOfWeekString(temp,
-                                                               count < 4 ? 
-                                                               DateUtils.LENGTH_MEDIUM : 
-                                                               DateUtils.LENGTH_LONG);
+                case 'c':
+                case 'E':
+                    replacement = getDayOfWeekString(localeData,
+                                                     inDate.get(Calendar.DAY_OF_WEEK), count, c);
                     break;
-                    
-                case HOUR:
-                    temp = inDate.get(Calendar.HOUR);
-
-                    if (0 == temp)
-                        temp = 12;
-                    
-                    replacement = zeroPad(temp, count);
+                case 'K': // hour in am/pm (0-11)
+                case 'h': // hour in am/pm (1-12)
+                    {
+                        int hour = inDate.get(Calendar.HOUR);
+                        if (c == 'h' && hour == 0) {
+                            hour = 12;
+                        }
+                        replacement = zeroPad(hour, count);
+                    }
                     break;
-                    
-                case HOUR_OF_DAY:
-                    replacement = zeroPad(inDate.get(Calendar.HOUR_OF_DAY), count);
+                case 'H': // hour in day (0-23)
+                case 'k': // hour in day (1-24)
+                    {
+                        int hour = inDate.get(Calendar.HOUR_OF_DAY);
+                        if (c == 'k' && hour == 0) {
+                            hour = 24;
+                        }
+                        replacement = zeroPad(hour, count);
+                    }
                     break;
-                    
-                case MINUTE:
+                case 'L':
+                case 'M':
+                    replacement = getMonthString(localeData,
+                                                 inDate.get(Calendar.MONTH), count, c);
+                    break;
+                case 'm':
                     replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
                     break;
-                    
-                case MONTH:
-                case STANDALONE_MONTH:
-                    replacement = getMonthString(inDate, count, c);
-                    break;
-
-                case SECONDS:
+                case 's':
                     replacement = zeroPad(inDate.get(Calendar.SECOND), count);
                     break;
-                    
-                case TIME_ZONE:
+                case 'y':
+                    replacement = getYearString(inDate.get(Calendar.YEAR), count);
+                    break;
+                case 'z':
                     replacement = getTimeZoneString(inDate, count);
                     break;
-                    
-                case YEAR:
-                    replacement = getYearString(inDate, count);
-                    break;
-
                 default:
                     replacement = null;
                     break;
             }
-            
+
             if (replacement != null) {
                 s.replace(i, i + count, replacement);
                 count = replacement.length(); // CARE: count is used in the for loop above
                 len = s.length();
             }
         }
-        
-        if (inFormat instanceof Spanned)
+
+        if (inFormat instanceof Spanned) {
             return new SpannedString(s);
-        else
+        } else {
             return s.toString();
+        }
     }
-    
-    private static String getMonthString(Calendar inDate, int count, int kind) {
-        boolean standalone = (kind == STANDALONE_MONTH);
-        int month = inDate.get(Calendar.MONTH);
-        
-        if (count >= 4) {
-            return standalone
-                ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_LONG)
-                : DateUtils.getMonthString(month, DateUtils.LENGTH_LONG);
+
+    private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+        boolean standalone = (kind == 'c');
+        if (count == 5) {
+            return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+        } else if (count == 4) {
+            return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+        } else {
+            return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+        }
+    }
+
+    private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+        boolean standalone = (kind == 'L');
+        if (count == 5) {
+            return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+        } else if (count == 4) {
+            return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
         } else if (count == 3) {
-            return standalone
-                ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_MEDIUM)
-                : DateUtils.getMonthString(month, DateUtils.LENGTH_MEDIUM);
+            return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
         } else {
             // Calendar.JANUARY == 0, so add 1 to month.
             return zeroPad(month+1, count);
         }
     }
-        
+
     private static String getTimeZoneString(Calendar inDate, int count) {
         TimeZone tz = inDate.getTimeZone();
-        
         if (count < 2) { // FIXME: shouldn't this be <= 2 ?
             return formatZoneOffset(inDate.get(Calendar.DST_OFFSET) +
                                     inDate.get(Calendar.ZONE_OFFSET),
@@ -662,13 +551,12 @@
         tb.append(zeroPad(minutes, 2));
         return tb.toString();
     }
-    
-    private static String getYearString(Calendar inDate, int count) {
-        int year = inDate.get(Calendar.YEAR);
+
+    private static String getYearString(int year, int count) {
         return (count <= 2) ? zeroPad(year % 100, 2)
                             : String.format(Locale.getDefault(), "%d", year);
     }
-   
+
     private static int appendQuotedText(SpannableStringBuilder s, int i, int len) {
         if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
             s.delete(i, i + 1);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 8920b24..6c8a737 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -274,10 +274,6 @@
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
-        // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.
-        // This is a shortcut to not spam the translators with too many variations
-        // of the same string.  If we find that in a language the distinction
-        // is necessary, we can can add more without changing this API.
         LocaleData d = LocaleData.get(Locale.getDefault());
         String[] names;
         switch (abbrev) {
@@ -308,19 +304,14 @@
      */
     @Deprecated
     public static String getStandaloneMonthString(int month, int abbrev) {
-        // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.
-        // This is a shortcut to not spam the translators with too many variations
-        // of the same string.  If we find that in a language the distinction
-        // is necessary, we can can add more without changing this API.
         LocaleData d = LocaleData.get(Locale.getDefault());
         String[] names;
         switch (abbrev) {
-            case LENGTH_LONG:       names = d.longStandAloneMonthNames;
-                                                            break;
+            case LENGTH_LONG:       names = d.longStandAloneMonthNames; break;
             case LENGTH_MEDIUM:     names = d.shortMonthNames; break;
             case LENGTH_SHORT:      names = d.shortMonthNames; break;
             case LENGTH_SHORTER:    names = d.shortMonthNames; break;
-            case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;
+            case LENGTH_SHORTEST:   names = d.tinyStandAloneMonthNames; break;
             default:                names = d.shortMonthNames; break;
         }
         return names[month];
@@ -429,20 +420,7 @@
                 }
             }
         } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
-            count = getNumberOfDaysPassed(time, now);
-            if (past) {
-                if (abbrevRelative) {
-                    resId = com.android.internal.R.plurals.abbrev_num_days_ago;
-                } else {
-                    resId = com.android.internal.R.plurals.num_days_ago;
-                }
-            } else {
-                if (abbrevRelative) {
-                    resId = com.android.internal.R.plurals.abbrev_in_num_days;
-                } else {
-                    resId = com.android.internal.R.plurals.in_num_days;
-                }
-            }
+            return getRelativeDayString(r, time, now);
         } else {
             // We know that we won't be showing the time, so it is safe to pass
             // in a null context.
@@ -454,24 +432,6 @@
     }
 
     /**
-     * Returns the number of days passed between two dates.
-     *
-     * @param date1 first date
-     * @param date2 second date
-     * @return number of days passed between to dates.
-     */
-    private synchronized static long getNumberOfDaysPassed(long date1, long date2) {
-        if (sThenTime == null) {
-            sThenTime = new Time();
-        }
-        sThenTime.set(date1);
-        int day1 = Time.getJulianDay(date1, sThenTime.gmtoff);
-        sThenTime.set(date2);
-        int day2 = Time.getJulianDay(date2, sThenTime.gmtoff);
-        return Math.abs(day2 - day1);
-    }
-
-    /**
      * Return string describing the elapsed time since startTime formatted like
      * "[relative time/date], [time]".
      * <p>
@@ -529,28 +489,29 @@
      * today this function returns "Today", if the day was a week ago it returns "7 days ago", and
      * if the day is in 2 weeks it returns "in 14 days".
      *
-     * @param r the resources to get the strings from
+     * @param r the resources
      * @param day the relative day to describe in UTC milliseconds
      * @param today the current time in UTC milliseconds
-     * @return a formatting string
      */
     private static final String getRelativeDayString(Resources r, long day, long today) {
+        Locale locale = r.getConfiguration().locale;
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
+
+        // TODO: use TimeZone.getOffset instead.
         Time startTime = new Time();
         startTime.set(day);
+        int startDay = Time.getJulianDay(day, startTime.gmtoff);
+
         Time currentTime = new Time();
         currentTime.set(today);
-
-        int startDay = Time.getJulianDay(day, startTime.gmtoff);
         int currentDay = Time.getJulianDay(today, currentTime.gmtoff);
 
         int days = Math.abs(currentDay - startDay);
         boolean past = (today > day);
 
         // TODO: some locales name other days too, such as de_DE's "Vorgestern" (today - 2).
-        Locale locale = r.getConfiguration().locale;
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
         if (days == 1) {
             if (past) {
                 return LocaleData.get(locale).yesterday;
@@ -1096,30 +1057,34 @@
         // computation below that'd otherwise be thrown out.
         boolean isInstant = (startMillis == endMillis);
 
-        Time startDate;
+        Calendar startCalendar, endCalendar;
+        Time startDate = new Time();
         if (timeZone != null) {
-            startDate = new Time(timeZone);
+            startCalendar = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
         } else if (useUTC) {
-            startDate = new Time(Time.TIMEZONE_UTC);
+            startCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
         } else {
-            startDate = new Time();
+            startCalendar = Calendar.getInstance();
         }
-        startDate.set(startMillis);
+        startCalendar.setTimeInMillis(startMillis);
+        setTimeFromCalendar(startDate, startCalendar);
 
-        Time endDate;
+        Time endDate = new Time();
         int dayDistance;
         if (isInstant) {
             endDate = startDate;
             dayDistance = 0;
         } else {
             if (timeZone != null) {
-                endDate = new Time(timeZone);
+                endCalendar = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
             } else if (useUTC) {
-                endDate = new Time(Time.TIMEZONE_UTC);
+                endCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
             } else {
-                endDate = new Time();
+                endCalendar = Calendar.getInstance();
             }
-            endDate.set(endMillis);
+            endCalendar.setTimeInMillis(endMillis);
+            setTimeFromCalendar(endDate, endCalendar);
+
             int startJulianDay = Time.getJulianDay(startMillis, startDate.gmtoff);
             int endJulianDay = Time.getJulianDay(endMillis, endDate.gmtoff);
             dayDistance = endJulianDay - startJulianDay;
@@ -1462,6 +1427,20 @@
         return formatter.format(fullFormat, timeString, startWeekDayString, dateString);
     }
 
+    private static void setTimeFromCalendar(Time t, Calendar c) {
+        t.hour = c.get(Calendar.HOUR_OF_DAY);
+        t.minute = c.get(Calendar.MINUTE);
+        t.month = c.get(Calendar.MONTH);
+        t.monthDay = c.get(Calendar.DAY_OF_MONTH);
+        t.second = c.get(Calendar.SECOND);
+        t.weekDay = c.get(Calendar.DAY_OF_WEEK) - 1;
+        t.year = c.get(Calendar.YEAR);
+        t.yearDay = c.get(Calendar.DAY_OF_YEAR);
+        t.isDst = (c.get(Calendar.DST_OFFSET) != 0) ? 1 : 0;
+        t.gmtoff = c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET);
+        t.timezone = c.getTimeZone().getID();
+    }
+
     /**
      * Formats a date or a time according to the local conventions. There are
      * lots of options that allow the caller to control, for example, if the
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 3bb9c01..dd523d2 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -62,7 +62,14 @@
      * specify the desired interpretation for its input events.
      */
     public static final int SOURCE_CLASS_MASK = 0x000000ff;
-    
+
+    /**
+     * The input source has no class.
+     *
+     * It is up to the application to determine how to handle the device based on the device type.
+     */
+    public static final int SOURCE_CLASS_NONE = 0x00000000;
+
     /**
      * The input source has buttons or keys.
      * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
@@ -202,6 +209,17 @@
     public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
 
     /**
+     * The input source is a touch device whose motions should be interpreted as navigation events.
+     *
+     * For example, an upward swipe should be as an upward focus traversal in the same manner as
+     * pressing up on a D-Pad would be. Swipes to the left, right and down should be treated in a
+     * similar manner.
+     *
+     * @see #SOURCE_CLASS_NONE
+     */
+    public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
+
+    /**
      * The input source is a joystick.
      * (It may also be a {@link #SOURCE_GAMEPAD}).
      *
@@ -633,6 +651,19 @@
             return mSource;
         }
 
+
+        /**
+         * Determines whether the event is from the given source.
+         *
+         * @param source The input source to check against. This can be a specific device type,
+         * such as {@link InputDevice#SOURCE_TOUCH_NAVIGATION}, or a more generic device class,
+         * such as {@link InputDevice#SOURCE_CLASS_POINTER}.
+         * @return Whether the event is from the given source.
+         */
+        public boolean isFromSource(int source) {
+            return (getSource() & source) == source;
+        }
+
         /**
          * Gets the inclusive minimum value for the axis.
          * @return The inclusive minimum value.
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index ef810a3..24c3128 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -83,6 +83,18 @@
     public abstract void setSource(int source);
 
     /**
+     * Determines whether the event is from the given source.
+     *
+     * @param source The input source to check against. This can be a specific device type, such as
+     * {@link InputDevice#SOURCE_TOUCH_NAVIGATION}, or a more generic device class, such as
+     * {@link InputDevice#SOURCE_CLASS_POINTER}.
+     * @return Whether the event is from the given source.
+     */
+    public boolean isFromSource(int source) {
+        return (getSource() & source) == source;
+    }
+
+    /**
      * Copies the event.
      *
      * @return A deep copy of the event.
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
index 883fd49..c889328 100644
--- a/core/java/android/view/SimulatedDpad.java
+++ b/core/java/android/view/SimulatedDpad.java
@@ -28,7 +28,7 @@
 import android.util.Log;
 
 /**
- * This class creates DPAD events from touchpad events.
+ * This class creates DPAD events from TouchNavigation events.
  * 
  * @see ViewRootImpl
  */
@@ -47,18 +47,18 @@
     private static final int MSG_FLICK = 313;
     // TODO: Pass touch slop from the input device
     private static final int TOUCH_SLOP = 30;
-    // The position of the previous touchpad event
-    private float mLastTouchpadXPosition;
-    private float mLastTouchpadYPosition;
-    // Where the touchpad was initially pressed
-    private float mTouchpadEnterXPosition;
-    private float mTouchpadEnterYPosition;
+    // The position of the previous TouchNavigation event
+    private float mLastTouchNavigationXPosition;
+    private float mLastTouchNavigationYPosition;
+    // Where the Touch Navigation was initially pressed
+    private float mTouchNavigationEnterXPosition;
+    private float mTouchNavigationEnterYPosition;
     // When the most recent ACTION_HOVER_ENTER occurred
-    private long mLastTouchPadStartTimeMs = 0;
+    private long mLastTouchNavigationStartTimeMs = 0;
     // When the most recent direction key was sent
-    private long mLastTouchPadKeySendTimeMs = 0;
+    private long mLastTouchNavigationKeySendTimeMs = 0;
     // When the most recent touch event of any type occurred
-    private long mLastTouchPadEventTimeMs = 0;
+    private long mLastTouchNavigationEventTimeMs = 0;
     // Did the swipe begin in a valid region
     private boolean mEdgeSwipePossible;
 
@@ -140,7 +140,7 @@
         }
     };
 
-    public void updateTouchPad(ViewRootImpl viewroot, MotionEvent event,
+    public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
             boolean synthesizeNewKeys) {
         if (!synthesizeNewKeys) {
             mHandler.removeMessages(MSG_FLICK);
@@ -149,14 +149,14 @@
         if (device == null) {
             return;
         }
-        // Store what time the touchpad event occurred
+        // Store what time the TouchNavigation event occurred
         final long time = SystemClock.uptimeMillis();
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                mLastTouchPadStartTimeMs = time;
+                mLastTouchNavigationStartTimeMs = time;
                 mAlwaysInTapRegion = true;
-                mTouchpadEnterXPosition = event.getX();
-                mTouchpadEnterYPosition = event.getY();
+                mTouchNavigationEnterXPosition = event.getX();
+                mTouchNavigationEnterYPosition = event.getY();
                 mAccumulatedX = 0;
                 mAccumulatedY = 0;
                 mLastMoveX = 0;
@@ -173,8 +173,8 @@
                 break;
             case MotionEvent.ACTION_MOVE:
                 // Determine whether the move is slop or an intentional move
-                float deltaX = event.getX() - mTouchpadEnterXPosition;
-                float deltaY = event.getY() - mTouchpadEnterYPosition;
+                float deltaX = event.getX() - mTouchNavigationEnterXPosition;
+                float deltaY = event.getY() - mTouchNavigationEnterYPosition;
                 if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
                     mAlwaysInTapRegion = false;
                 }
@@ -199,9 +199,9 @@
                     }
                 }
                 // Find the difference in position between the two most recent
-                // touchpad events
-                mLastMoveX = event.getX() - mLastTouchpadXPosition;
-                mLastMoveY = event.getY() - mLastTouchpadYPosition;
+                // TouchNavigation events
+                mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
+                mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
                 mAccumulatedX += mLastMoveX;
                 mAccumulatedY += mLastMoveY;
                 float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
@@ -251,28 +251,28 @@
                     mAccumulatedY = isXAxis ? 0 : dominantAxis;
 
                     mLastKeySent = key;
-                    mKeySendRateMs = (int) ((time - mLastTouchPadKeySendTimeMs) / repeatCount);
-                    mLastTouchPadKeySendTimeMs = time;
+                    mKeySendRateMs = (int) (time - mLastTouchNavigationKeySendTimeMs) / repeatCount;
+                    mLastTouchNavigationKeySendTimeMs = time;
                 }
                 break;
             case MotionEvent.ACTION_UP:
-                if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
+                if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
                     if (synthesizeNewKeys) {
-                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
-                                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
-                                event.getMetaState(), event.getDeviceId(), 0,
-                                KeyEvent.FLAG_FALLBACK, event.getSource()));
-                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
-                                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
-                                event.getMetaState(), event.getDeviceId(), 0,
-                                KeyEvent.FLAG_FALLBACK, event.getSource()));
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
+                                    time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                    event.getMetaState(), event.getDeviceId(), 0,
+                                    KeyEvent.FLAG_FALLBACK, event.getSource()));
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
+                                    time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                    event.getMetaState(), event.getDeviceId(), 0,
+                                    KeyEvent.FLAG_FALLBACK, event.getSource()));
                     }
                 } else {
                     float xMoveSquared = mLastMoveX * mLastMoveX;
                     float yMoveSquared = mLastMoveY * mLastMoveY;
                     // Determine whether the last gesture was a fling.
                     if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
-                            time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
+                            time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
                             mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
                         mLastDeviceId = event.getDeviceId();
                         mLastSource = event.getSource();
@@ -291,8 +291,8 @@
         }
 
         // Store touch event position and time
-        mLastTouchPadEventTimeMs = time;
-        mLastTouchpadXPosition = event.getX();
-        mLastTouchpadYPosition = event.getY();
+        mLastTouchNavigationEventTimeMs = time;
+        mLastTouchNavigationXPosition = event.getX();
+        mLastTouchNavigationYPosition = event.getY();
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7e0d115..2e60f51 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8112,13 +8112,13 @@
      * delivered to the focused view.
      * </p>
      * <pre> public boolean onGenericMotionEvent(MotionEvent event) {
-     *     if ((event.getSource() &amp; InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+     *     if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) {
      *         if (event.getAction() == MotionEvent.ACTION_MOVE) {
      *             // process the joystick movement...
      *             return true;
      *         }
      *     }
-     *     if ((event.getSource() &amp; InputDevice.SOURCE_CLASS_POINTER) != 0) {
+     *     if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
      *         switch (event.getAction()) {
      *             case MotionEvent.ACTION_HOVER_MOVE:
      *                 // process the mouse hover movement...
@@ -10642,7 +10642,7 @@
         // Opaque if:
         //   - Has a background
         //   - Background is opaque
-        //   - Doesn't have scrollbars or scrollbars are inside overlay
+        //   - Doesn't have scrollbars or scrollbars overlay
 
         if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
             mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
@@ -10652,7 +10652,8 @@
 
         final int flags = mViewFlags;
         if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
-                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY) {
+                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
+                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
             mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
         } else {
             mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b8fae865..a937882 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3559,15 +3559,15 @@
     private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
         final int source = event.getSource();
-        final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
-        final boolean isTouchPad = (source & InputDevice.SOURCE_CLASS_POSITION) != 0;
+        final boolean isJoystick = event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK);
+        final boolean isTouchNavigation = event.isFromSource(InputDevice.SOURCE_TOUCH_NAVIGATION);
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
             if (isJoystick) {
                 updateJoystickDirection(event, false);
-            } else if (isTouchPad) {
-              mSimulatedDpad.updateTouchPad(this, event, false);
+            } else if (isTouchNavigation) {
+                mSimulatedDpad.updateTouchNavigation(this, event, false);
             }
             return EVENT_NOT_HANDLED;
         }
@@ -3576,8 +3576,8 @@
         if (mView.dispatchGenericMotionEvent(event)) {
             if (isJoystick) {
                 updateJoystickDirection(event, false);
-            } else if (isTouchPad) {
-              mSimulatedDpad.updateTouchPad(this, event, false);
+            } else if (isTouchNavigation) {
+                mSimulatedDpad.updateTouchNavigation(this, event, false);
             }
             return EVENT_HANDLED;
         }
@@ -3588,8 +3588,8 @@
             updateJoystickDirection(event, true);
             return EVENT_HANDLED;
         }
-        if (isTouchPad) {
-            mSimulatedDpad.updateTouchPad(this, event, true);
+        if (isTouchNavigation) {
+            mSimulatedDpad.updateTouchNavigation(this, event, true);
             return EVENT_HANDLED;
         }
         return EVENT_NOT_HANDLED;
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 24ac54f..33e8364 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -70,6 +70,7 @@
      * request's LoadListener
      */
     private final static int MAX_OUTSTANDING_REQUESTS = 300;
+    private final static String SCHEME_HOST_DELIMITER = "://";
 
     private final CallbackProxy mCallbackProxy;
     private final WebSettingsClassic mSettings;
@@ -498,9 +499,14 @@
                             .getCurrentItem();
                     if (item != null) {
                         WebAddress uri = new WebAddress(item.getUrl());
-                        String schemePlusHost = uri.getScheme() + uri.getHost();
+                        String schemePlusHost = uri.getScheme() + SCHEME_HOST_DELIMITER +
+                                uri.getHost();
                         String[] up = mDatabase.getUsernamePassword(
                                 schemePlusHost);
+                        if (up == null) { // no row found, try again using the legacy method
+                            schemePlusHost = uri.getScheme() + uri.getHost();
+                            up = mDatabase.getUsernamePassword(schemePlusHost);
+                        }
                         if (up != null && up[0] != null) {
                             setUsernamePassword(up[0], up[1]);
                         }
@@ -815,7 +821,7 @@
             }
             WebAddress uri = new WebAddress(mCallbackProxy
                     .getBackForwardList().getCurrentItem().getUrl());
-            String schemePlusHost = uri.getScheme() + uri.getHost();
+            String schemePlusHost = uri.getScheme() + SCHEME_HOST_DELIMITER + 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.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index aa9a329..1f00c9c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -573,7 +573,8 @@
      * forms. Note that this is unrelated to the credentials used for HTTP
      * authentication.
      *
-     * @param host the host that required the credentials
+     * @param host the host that required the credentials. It is recommended that
+     *             the host is given using scheme://hostname format.
      * @param username the username for the given host
      * @param password the password for the given host
      * @see WebViewDatabase#clearUsernamePassword
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 67041ac..c7dacf3 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -5419,7 +5419,7 @@
         ClipData clipData = cm.getPrimaryClip();
         if (clipData != null) {
             ClipData.Item clipItem = clipData.getItemAt(0);
-            CharSequence pasteText = clipItem.getText();
+            CharSequence pasteText = clipItem.coerceToText(mContext);
             if (mInputConnection != null) {
                 mInputConnection.replaceSelection(pasteText);
             }
diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java
index be01028..5ad4fa5 100644
--- a/core/java/android/webkit/WebViewDatabaseClassic.java
+++ b/core/java/android/webkit/WebViewDatabaseClassic.java
@@ -37,7 +37,7 @@
     private static final String DATABASE_FILE = "webview.db";
     private static final String CACHE_DATABASE_FILE = "webviewCache.db";
 
-    private static final int DATABASE_VERSION = 11;
+    private static final int DATABASE_VERSION = 12;
     // 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
@@ -50,6 +50,7 @@
     // 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.
+    // 11 -> 12 Add a delimiter between scheme and host when storing passwords
 
     private static WebViewDatabaseClassic sInstance = null;
     private static final Object sInstanceLock = new Object();
@@ -169,11 +170,23 @@
     private static void upgradeDatabase() {
         upgradeDatabaseToV10();
         upgradeDatabaseFromV10ToV11();
+        upgradeDatabaseFromV11ToV12();
         // Add future database upgrade functions here, one version at a
         // time.
         sDatabase.setVersion(DATABASE_VERSION);
     }
 
+    private static void upgradeDatabaseFromV11ToV12() {
+        int oldVersion = sDatabase.getVersion();
+
+        if (oldVersion >= 12) {
+            // Nothing to do.
+            return;
+        }
+        // delete the rows in the database.
+        sDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null);
+    }
+
     private static void upgradeDatabaseFromV10ToV11() {
         int oldVersion = sDatabase.getVersion();
 
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 07d3a7a..8f515f5 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -419,7 +419,7 @@
      * @see #getCalendarView()
      */
     public boolean getCalendarViewShown() {
-        return mCalendarView.isShown();
+        return (mCalendarView.getVisibility() == View.VISIBLE);
     }
 
     /**
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 5d6ec1e..c9d2d95 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -41,6 +41,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.RemoteViews.RemoteView;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 import static android.util.Log.d;
 
 /**
@@ -1215,6 +1216,7 @@
         private int mEnd = DEFAULT_RELATIVE;
 
         private boolean mRulesChanged = false;
+        private boolean mIsRtlCompatibilityMode = false;
 
         /**
          * When true, uses the parent as the anchor if the anchor doesn't exist or if
@@ -1229,6 +1231,10 @@
             TypedArray a = c.obtainStyledAttributes(attrs,
                     com.android.internal.R.styleable.RelativeLayout_Layout);
 
+            final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
+            mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
+                    !c.getApplicationInfo().hasRtlSupport());
+
             final int[] rules = mRules;
             //noinspection MismatchedReadAndWriteOfArray
             final int[] initialRules = mInitialRules;
@@ -1397,28 +1403,132 @@
                     mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
         }
 
+        // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
+        // or not.
+        //
+        // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
+        // predominance over any "start/end" rules that could have been defined. A special case:
+        // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
+        // resolve those "start"/"end" rules to "left"/"right" respectively.
+        //
+        // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
+        // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
+        //
+        // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
+        // only the "left"/"right" rules at the end.
         private void resolveRules(int layoutDirection) {
             final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
+
             // Reset to initial state
             System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
-            // Apply rules depending on direction
-            if (mRules[ALIGN_START] != 0) {
-                mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
-            }
-            if (mRules[ALIGN_END] != 0) {
-                mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
-            }
-            if (mRules[START_OF] != 0) {
-                mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
-            }
-            if (mRules[END_OF] != 0) {
-                mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
-            }
-            if (mRules[ALIGN_PARENT_START] != 0) {
-                mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
-            }
-            if (mRules[ALIGN_PARENT_END] != 0) {
-                mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
+
+            // Apply rules depending on direction and if we are in RTL compatibility mode
+            if (mIsRtlCompatibilityMode) {
+                if (mRules[ALIGN_START] != 0) {
+                    if (mRules[ALIGN_LEFT] == 0) {
+                        // "left" rule is not defined but "start" rule is: use the "start" rule as
+                        // the "left" rule
+                        mRules[ALIGN_LEFT] = mRules[ALIGN_START];
+                    }
+                    mRules[ALIGN_START] = 0;
+                }
+
+                if (mRules[ALIGN_END] != 0) {
+                    if (mRules[ALIGN_RIGHT] == 0) {
+                        // "right" rule is not defined but "end" rule is: use the "end" rule as the
+                        // "right" rule
+                        mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
+                    }
+                    mRules[ALIGN_END] = 0;
+                }
+
+                if (mRules[START_OF] != 0) {
+                    if (mRules[LEFT_OF] == 0) {
+                        // "left" rule is not defined but "start" rule is: use the "start" rule as
+                        // the "left" rule
+                        mRules[LEFT_OF] = mRules[START_OF];
+                    }
+                    mRules[START_OF] = 0;
+                }
+
+                if (mRules[END_OF] != 0) {
+                    if (mRules[RIGHT_OF] == 0) {
+                        // "right" rule is not defined but "end" rule is: use the "end" rule as the
+                        // "right" rule
+                        mRules[RIGHT_OF] = mRules[END_OF];
+                    }
+                    mRules[END_OF] = 0;
+                }
+
+                if (mRules[ALIGN_PARENT_START] != 0) {
+                    if (mRules[ALIGN_PARENT_LEFT] == 0) {
+                        // "left" rule is not defined but "start" rule is: use the "start" rule as
+                        // the "left" rule
+                        mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
+                    }
+                    mRules[ALIGN_PARENT_START] = 0;
+                }
+
+                if (mRules[ALIGN_PARENT_RIGHT] == 0) {
+                    if (mRules[ALIGN_PARENT_RIGHT] == 0) {
+                        // "right" rule is not defined but "end" rule is: use the "end" rule as the
+                        // "right" rule
+                        mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
+                    }
+                    mRules[ALIGN_PARENT_END] = 0;
+                }
+            } else {
+                // JB MR1+ case
+                if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
+                        (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
+                    // "start"/"end" rules take precedence over "left"/"right" rules
+                    mRules[ALIGN_LEFT] = 0;
+                    mRules[ALIGN_RIGHT] = 0;
+                }
+                if (mRules[ALIGN_START] != 0) {
+                    // "start" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
+                    mRules[ALIGN_START] = 0;
+                }
+                if (mRules[ALIGN_END] != 0) {
+                    // "end" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
+                    mRules[ALIGN_END] = 0;
+                }
+
+                if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
+                        (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
+                    // "start"/"end" rules take precedence over "left"/"right" rules
+                    mRules[LEFT_OF] = 0;
+                    mRules[RIGHT_OF] = 0;
+                }
+                if (mRules[START_OF] != 0) {
+                    // "start" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
+                    mRules[START_OF] = 0;
+                }
+                if (mRules[END_OF] != 0) {
+                    // "end" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
+                    mRules[END_OF] = 0;
+                }
+
+                if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
+                        (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
+                    // "start"/"end" rules take precedence over "left"/"right" rules
+                    mRules[ALIGN_PARENT_LEFT] = 0;
+                    mRules[ALIGN_PARENT_RIGHT] = 0;
+                }
+                if (mRules[ALIGN_PARENT_START] != 0) {
+                    // "start" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
+                    mRules[ALIGN_PARENT_START] = 0;
+                }
+                if (mRules[ALIGN_PARENT_END] != 0) {
+                    // "end" rule resolved to "left" or "right" depending on the direction
+                    mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
+                    mRules[ALIGN_PARENT_END] = 0;
+                }
             }
             mRulesChanged = false;
         }
diff --git a/core/java/com/android/internal/backup/IObbBackupService.aidl b/core/java/com/android/internal/backup/IObbBackupService.aidl
new file mode 100644
index 0000000..426dbc4
--- /dev/null
+++ b/core/java/com/android/internal/backup/IObbBackupService.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.internal.backup;
+ 
+import android.app.backup.IBackupManager;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Interface for the Backup Manager Service to communicate with a helper service that
+ * handles local (whole-file) backup & restore of OBB content on behalf of applications.
+ * This can't be done within the Backup Manager Service itself because of the restrictions
+ * on system-user access to external storage, and can't be left to the apps because even
+ * apps that do not have permission to access external storage in the usual way can still
+ * use OBBs.
+ *
+ * {@hide}
+ */
+oneway interface IObbBackupService {
+    /*
+     * Back up a package's OBB directory tree
+     */
+    void backupObbs(in String packageName, in ParcelFileDescriptor data,
+            int token, in IBackupManager callbackBinder);
+
+    /*
+     * Restore an OBB file for the given package from the incoming stream
+     */
+    void restoreObbFile(in String pkgName, in ParcelFileDescriptor data,
+            long fileSize, int type, in String path, long mode, long mtime,
+            int token, in IBackupManager callbackBinder);
+}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 58d4aa7..e547f23 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1606,6 +1606,19 @@
      *
      * Message is ignored if state machine has quit.
      */
+    public final void sendMessage(int what, int arg1, int arg2, Object obj) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
+    }
+
+    /**
+     * Enqueue a message to this state machine.
+     *
+     * Message is ignored if state machine has quit.
+     */
     public final void sendMessage(Message msg) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
@@ -1645,6 +1658,20 @@
      *
      * Message is ignored if state machine has quit.
      */
+    public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
+            long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis);
+    }
+
+    /**
+     * Enqueue a message to this state machine after a delay.
+     *
+     * Message is ignored if state machine has quit.
+     */
     public final void sendMessageDelayed(Message msg, long delayMillis) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
@@ -1687,6 +1714,20 @@
      *
      * Message is ignored if state machine has quit.
      */
+    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj));
+    }
+
+    /**
+     * Enqueue a message to the front of the queue for this state machine.
+     * Protected, may only be called by instances of StateMachine.
+     *
+     * Message is ignored if state machine has quit.
+     */
     protected final void sendMessageAtFrontOfQueue(Message msg) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 7a76ab0..1b088b3 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -38,6 +38,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
+import com.android.internal.widget.LockPatternView.Cell;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -72,6 +73,12 @@
      */
     private static final int MILLIS_PER_CIRCLE_ANIMATING = 700;
 
+    /**
+     * This can be used to avoid updating the display for very small motions or noisy panels.
+     * It didn't seem to have much impact on the devices tested, so currently set to 0.
+     */
+    private static final float DRAG_THRESHHOLD = 0.0f;
+
     private OnPatternListener mOnPatternListener;
     private ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
 
@@ -674,10 +681,11 @@
         // Handle all recent motion events so we don't skip any cells even when the device
         // is busy...
         final int historySize = event.getHistorySize();
+        Rect invalidateRect = mInvalidate;
+        boolean invalidateNow = false;
         for (int i = 0; i < historySize + 1; i++) {
             final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
             final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
-            final int patternSizePreHitDetect = mPattern.size();
             Cell hitCell = detectAndAddHit(x, y);
             final int patternSize = mPattern.size();
             if (hitCell != null && patternSize == 1) {
@@ -687,113 +695,47 @@
             // note current x and y for rubber banding of in progress patterns
             final float dx = Math.abs(x - mInProgressX);
             final float dy = Math.abs(y - mInProgressY);
-            if (dx + dy > mSquareWidth * 0.01f) {
-                float oldX = mInProgressX;
-                float oldY = mInProgressY;
-
-                mInProgressX = x;
-                mInProgressY = y;
-
-                if (mPatternInProgress && patternSize > 0) {
-                    final ArrayList<Cell> pattern = mPattern;
-                    final float radius = mSquareWidth * mDiameterFactor * 0.5f;
-
-                    final Cell lastCell = pattern.get(patternSize - 1);
-
-                    float startX = getCenterXForColumn(lastCell.column);
-                    float startY = getCenterYForRow(lastCell.row);
-
-                    float left;
-                    float top;
-                    float right;
-                    float bottom;
-
-                    final Rect invalidateRect = mInvalidate;
-
-                    if (startX < x) {
-                        left = startX;
-                        right = x;
-                    } else {
-                        left = x;
-                        right = startX;
-                    }
-
-                    if (startY < y) {
-                        top = startY;
-                        bottom = y;
-                    } else {
-                        top = y;
-                        bottom = startY;
-                    }
-
-                    // Invalidate between the pattern's last cell and the current location
-                    invalidateRect.set((int) (left - radius), (int) (top - radius),
-                            (int) (right + radius), (int) (bottom + radius));
-
-                    if (startX < oldX) {
-                        left = startX;
-                        right = oldX;
-                    } else {
-                        left = oldX;
-                        right = startX;
-                    }
-
-                    if (startY < oldY) {
-                        top = startY;
-                        bottom = oldY;
-                    } else {
-                        top = oldY;
-                        bottom = startY;
-                    }
-
-                    // Invalidate between the pattern's last cell and the previous location
-                    invalidateRect.union((int) (left - radius), (int) (top - radius),
-                            (int) (right + radius), (int) (bottom + radius));
-
-                    // Invalidate between the pattern's new cell and the pattern's previous cell
-                    if (hitCell != null) {
-                        startX = getCenterXForColumn(hitCell.column);
-                        startY = getCenterYForRow(hitCell.row);
-
-                        if (patternSize >= 2) {
-                            // (re-using hitcell for old cell)
-                            hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
-                            oldX = getCenterXForColumn(hitCell.column);
-                            oldY = getCenterYForRow(hitCell.row);
-
-                            if (startX < oldX) {
-                                left = startX;
-                                right = oldX;
-                            } else {
-                                left = oldX;
-                                right = startX;
-                            }
-
-                            if (startY < oldY) {
-                                top = startY;
-                                bottom = oldY;
-                            } else {
-                                top = oldY;
-                                bottom = startY;
-                            }
-                        } else {
-                            left = right = startX;
-                            top = bottom = startY;
-                        }
-
-                        final float widthOffset = mSquareWidth / 2f;
-                        final float heightOffset = mSquareHeight / 2f;
-
-                        invalidateRect.set((int) (left - widthOffset),
-                                (int) (top - heightOffset), (int) (right + widthOffset),
-                                (int) (bottom + heightOffset));
-                    }
-
-                    invalidate(invalidateRect);
-                } else {
-                    invalidate();
-                }
+            if (dx > DRAG_THRESHHOLD || dy > DRAG_THRESHHOLD) {
+                invalidateNow = true;
             }
+
+            if (mPatternInProgress && patternSize > 0) {
+                final ArrayList<Cell> pattern = mPattern;
+                final Cell lastCell = pattern.get(patternSize - 1);
+                float startX = getCenterXForColumn(lastCell.column);
+                float startY = getCenterYForRow(lastCell.row);
+
+                // Adjust for current position. Radius accounts for line width.
+                final float radius = (mSquareWidth * mDiameterFactor * 0.5f);
+                float left = Math.min(startX, x) - radius;
+                float right = Math.max(startX, x) + radius;
+                float top = Math.min(startY, y) - radius;
+                float bottom = Math.max(startY, y) + radius;
+
+                // Invalidate between the pattern's new cell and the pattern's previous cell
+                if (hitCell != null && patternSize >= 2) {
+                    final float width = mSquareWidth * 0.5f;
+                    final float height = mSquareHeight * 0.5f;
+                    final float x2 = getCenterXForColumn(hitCell.column);
+                    final float y2 = getCenterYForRow(hitCell.row);
+                    left = Math.min(x2, left - width);
+                    right = Math.max(x2, right + width);
+                    top = Math.min(y2, top - height);
+                    bottom = Math.max(y2, bottom + height);
+                }
+
+                // Invalidate between the pattern's last cell and the previous location
+                invalidateRect.union(Math.round(left), Math.round(top),
+                        Math.round(right), Math.round(bottom));
+            }
+        }
+        mInProgressX = event.getX();
+        mInProgressY = event.getY();
+
+        // To save updates, we only invalidate if the user moved beyond a certain amount.
+        if (invalidateNow) {
+            invalidate(invalidateRect);
+            invalidateRect.setEmpty();
         }
     }
 
diff --git a/core/res/res/drawable-hdpi/ic_btn_search.png b/core/res/res/drawable-hdpi/ic_btn_search.png
deleted file mode 100644
index 98e61a3..0000000
--- a/core/res/res/drawable-hdpi/ic_btn_search.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search.png b/core/res/res/drawable-hdpi/ic_menu_search.png
index a7f9bbe..ae2f44b 100644
--- a/core/res/res/drawable-hdpi/ic_menu_search.png
+++ b/core/res/res/drawable-hdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_search_holo_dark.png
index a7f9bbe..420d680 100644
--- a/core/res/res/drawable-hdpi/ic_menu_search_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_search_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_search_holo_light.png
index 1cb61fa..cc661e3 100644
--- a/core/res/res/drawable-hdpi/ic_menu_search_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_search_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_btn_search.png b/core/res/res/drawable-ldpi/ic_btn_search.png
deleted file mode 100644
index bdefbf5..0000000
--- a/core/res/res/drawable-ldpi/ic_btn_search.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_search.png b/core/res/res/drawable-mdpi/ic_btn_search.png
deleted file mode 100644
index 662f5de..0000000
--- a/core/res/res/drawable-mdpi/ic_btn_search.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search.png b/core/res/res/drawable-mdpi/ic_menu_search.png
index 5d3155e..d18f542 100644
--- a/core/res/res/drawable-mdpi/ic_menu_search.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_search_holo_dark.png
index 5d3155e..906da53 100644
--- a/core/res/res/drawable-mdpi/ic_menu_search_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_search_holo_light.png
index 2369d03..0350a43 100644
--- a/core/res/res/drawable-mdpi/ic_menu_search_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_btn_search.png b/core/res/res/drawable-xhdpi/ic_btn_search.png
deleted file mode 100644
index a267c0a..0000000
--- a/core/res/res/drawable-xhdpi/ic_btn_search.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search.png b/core/res/res/drawable-xhdpi/ic_menu_search.png
index 5c18f9e..4444495 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_search.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png
index d49c7e2..1208859 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png
index 578cb24..6811782 100644
--- a/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png
+++ b/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png
Binary files differ
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index e3b6b5f..fffaa00 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -197,4 +197,63 @@
         }
     }
 
+    private void assertAllRoutesHaveInterface(String iface, LinkProperties lp) {
+        for (RouteInfo r : lp.getRoutes()) {
+            assertEquals(iface, r.getInterface());
+        }
+    }
+
+    @SmallTest
+    public void testRouteInterfaces() {
+        LinkAddress prefix = new LinkAddress(
+            NetworkUtils.numericToInetAddress("2001:db8::"), 32);
+        InetAddress address = NetworkUtils.numericToInetAddress(ADDRV6);
+
+        // Add a route with no interface to a LinkProperties with no interface. No errors.
+        LinkProperties lp = new LinkProperties();
+        RouteInfo r = new RouteInfo(prefix, address, null);
+        lp.addRoute(r);
+        assertEquals(1, lp.getRoutes().size());
+        assertAllRoutesHaveInterface(null, lp);
+
+        // Add a route with an interface. Except an exception.
+        r = new RouteInfo(prefix, address, "wlan0");
+        try {
+          lp.addRoute(r);
+          fail("Adding wlan0 route to LP with no interface, expect exception");
+        } catch (IllegalArgumentException expected) {}
+
+        // Change the interface name. All the routes should change their interface name too.
+        lp.setInterfaceName("rmnet0");
+        assertAllRoutesHaveInterface("rmnet0", lp);
+
+        // Now add a route with the wrong interface. This causes an exception too.
+        try {
+          lp.addRoute(r);
+          fail("Adding wlan0 route to rmnet0 LP, expect exception");
+        } catch (IllegalArgumentException expected) {}
+
+        // If the interface name matches, the route is added.
+        lp.setInterfaceName("wlan0");
+        lp.addRoute(r);
+        assertEquals(2, lp.getRoutes().size());
+        assertAllRoutesHaveInterface("wlan0", lp);
+
+        // Routes with null interfaces are converted to wlan0.
+        r = RouteInfo.makeHostRoute(NetworkUtils.numericToInetAddress(ADDRV6), null);
+        lp.addRoute(r);
+        assertEquals(3, lp.getRoutes().size());
+        assertAllRoutesHaveInterface("wlan0", lp);
+
+        // Check comparisons work.
+        LinkProperties lp2 = new LinkProperties(lp);
+        assertAllRoutesHaveInterface("wlan0", lp);
+        assertEquals(0, lp.compareRoutes(lp2).added.size());
+        assertEquals(0, lp.compareRoutes(lp2).removed.size());
+
+        lp2.setInterfaceName("p2p0");
+        assertAllRoutesHaveInterface("p2p0", lp2);
+        assertEquals(3, lp.compareRoutes(lp2).added.size());
+        assertEquals(3, lp.compareRoutes(lp2).removed.size());
+    }
 }
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
new file mode 100644
index 0000000..59eb601
--- /dev/null
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.net;
+
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+
+import android.net.LinkAddress;
+import android.net.RouteInfo;
+import android.os.Parcel;
+
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class RouteInfoTest extends TestCase {
+
+    private InetAddress Address(String addr) {
+        return InetAddress.parseNumericAddress(addr);
+    }
+
+    private LinkAddress Prefix(String prefix) {
+        String[] parts = prefix.split("/");
+        return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1]));
+    }
+
+    @SmallTest
+    public void testConstructor() {
+        RouteInfo r;
+
+        // Invalid input.
+        try {
+            r = new RouteInfo(null, null, "rmnet0");
+            fail("Expected RuntimeException:  destination and gateway null");
+        } catch(RuntimeException e) {}
+
+        // Null destination is default route.
+        r = new RouteInfo(null, Address("2001:db8::1"), null);
+        assertEquals(Prefix("::/0"), r.getDestination());
+        assertEquals(Address("2001:db8::1"), r.getGateway());
+        assertNull(r.getInterface());
+
+        r = new RouteInfo(null, Address("192.0.2.1"), "wlan0");
+        assertEquals(Prefix("0.0.0.0/0"), r.getDestination());
+        assertEquals(Address("192.0.2.1"), r.getGateway());
+        assertEquals("wlan0", r.getInterface());
+
+        // Null gateway sets gateway to unspecified address (why?).
+        r = new RouteInfo(Prefix("2001:db8:beef:cafe::/48"), null, "lo");
+        assertEquals(Prefix("2001:db8:beef::/48"), r.getDestination());
+        assertEquals(Address("::"), r.getGateway());
+        assertEquals("lo", r.getInterface());
+
+        r = new RouteInfo(Prefix("192.0.2.5/24"), null);
+        assertEquals(Prefix("192.0.2.0/24"), r.getDestination());
+        assertEquals(Address("0.0.0.0"), r.getGateway());
+        assertNull(r.getInterface());
+    }
+
+    public void testMatches() {
+        class PatchedRouteInfo extends RouteInfo {
+            public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
+                super(destination, gateway, iface);
+            }
+
+            public boolean matches(InetAddress destination) {
+                return super.matches(destination);
+            }
+        }
+
+        RouteInfo r;
+
+        r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0");
+        assertTrue(r.matches(Address("2001:db8:f00::ace:d00c")));
+        assertTrue(r.matches(Address("2001:db8:f00::ace:d00d")));
+        assertFalse(r.matches(Address("2001:db8:f00::ace:d00e")));
+        assertFalse(r.matches(Address("2001:db8:f00::bad:d00d")));
+        assertFalse(r.matches(Address("2001:4868:4860::8888")));
+
+        r = new PatchedRouteInfo(Prefix("192.0.2.0/23"), null, "wlan0");
+        assertTrue(r.matches(Address("192.0.2.43")));
+        assertTrue(r.matches(Address("192.0.3.21")));
+        assertFalse(r.matches(Address("192.0.0.21")));
+        assertFalse(r.matches(Address("8.8.8.8")));
+
+        RouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0");
+        assertTrue(ipv6Default.matches(Address("2001:db8::f00")));
+        assertFalse(ipv6Default.matches(Address("192.0.2.1")));
+
+        RouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0");
+        assertTrue(ipv4Default.matches(Address("255.255.255.255")));
+        assertTrue(ipv4Default.matches(Address("192.0.2.1")));
+        assertFalse(ipv4Default.matches(Address("2001:db8::f00")));
+    }
+
+    private void assertAreEqual(Object o1, Object o2) {
+        assertTrue(o1.equals(o2));
+        assertTrue(o2.equals(o1));
+    }
+
+    private void assertAreNotEqual(Object o1, Object o2) {
+        assertFalse(o1.equals(o2));
+        assertFalse(o2.equals(o1));
+    }
+
+    public void testEquals() {
+        // IPv4
+        RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0");
+        RouteInfo r2 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0");
+        assertAreEqual(r1, r2);
+
+        RouteInfo r3 = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "wlan0");
+        RouteInfo r4 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::2"), "wlan0");
+        RouteInfo r5 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "rmnet0");
+        assertAreNotEqual(r1, r3);
+        assertAreNotEqual(r1, r4);
+        assertAreNotEqual(r1, r5);
+
+        // IPv6
+        r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0");
+        r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0");
+        assertAreEqual(r1, r2);
+
+        r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0");
+        r4 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.2"), "wlan0");
+        r5 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "rmnet0");
+        assertAreNotEqual(r1, r3);
+        assertAreNotEqual(r1, r4);
+        assertAreNotEqual(r1, r5);
+
+        // Interfaces (but not destinations or gateways) can be null.
+        r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null);
+        r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null);
+        r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0");
+        assertAreEqual(r1, r2);
+        assertAreNotEqual(r1, r3);
+    }
+
+    public RouteInfo passThroughParcel(RouteInfo r) {
+        Parcel p = Parcel.obtain();
+        RouteInfo r2 = null;
+        try {
+            r.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            r2 = RouteInfo.CREATOR.createFromParcel(p);
+        } finally {
+            p.recycle();
+        }
+        assertNotNull(r2);
+        return r2;
+    }
+
+    public void assertParcelingIsLossless(RouteInfo r) {
+      RouteInfo r2 = passThroughParcel(r);
+      assertEquals(r, r2);
+    }
+
+    public void testParceling() {
+        RouteInfo r;
+
+        r = new RouteInfo(Prefix("::/0"), Address("2001:db8::"), null);
+        assertParcelingIsLossless(r);
+
+        r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
+        assertParcelingIsLossless(r);
+    }
+}
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index cf42bb1..c5f6236 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -21,8 +21,9 @@
 import junit.framework.TestCase;
 
 public class DateUtilsTest extends TestCase {
+    // This test is not in CTS because formatDuration is @hidden.
     @SmallTest
-    public void testFormatDurationSeconds() throws Exception {
+    public void test_formatDuration_seconds() throws Exception {
         assertEquals("0 seconds", DateUtils.formatDuration(0));
         assertEquals("0 seconds", DateUtils.formatDuration(1));
         assertEquals("0 seconds", DateUtils.formatDuration(499));
@@ -31,16 +32,18 @@
         assertEquals("2 seconds", DateUtils.formatDuration(1500));
     }
 
+    // This test is not in CTS because formatDuration is @hidden.
     @SmallTest
-    public void testFormatDurationMinutes() throws Exception {
+    public void test_formatDuration_Minutes() throws Exception {
         assertEquals("59 seconds", DateUtils.formatDuration(59000));
         assertEquals("60 seconds", DateUtils.formatDuration(59500));
         assertEquals("1 minute", DateUtils.formatDuration(60000));
         assertEquals("2 minutes", DateUtils.formatDuration(120000));
     }
 
+    // This test is not in CTS because formatDuration is @hidden.
     @SmallTest
-    public void testFormatDurationHours() throws Exception {
+    public void test_formatDuration_Hours() throws Exception {
         assertEquals("59 minutes", DateUtils.formatDuration(3540000));
         assertEquals("1 hour", DateUtils.formatDuration(3600000));
         assertEquals("48 hours", DateUtils.formatDuration(172800000));
diff --git a/docs/downloads/training/InteractiveChart.zip b/docs/downloads/training/InteractiveChart.zip
new file mode 100644
index 0000000..95248ad
--- /dev/null
+++ b/docs/downloads/training/InteractiveChart.zip
Binary files differ
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 93876f8..b2d50ce 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -32,11 +32,11 @@
 </tr>
 <tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td>    <td>4</td><td>0.2%</td></tr>
 <tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td>   <td>7</td><td>1.9%</td></tr>
-<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td>    <td>8</td><td>7.6%</td></tr>
+<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td>    <td>8</td><td>7.5%</td></tr>
 <tr><td><a href="/about/versions/android-2.3.html">2.3 - 2.3.2</a>
                                    </td><td rowspan="2">Gingerbread</td>    <td>9</td><td>0.2%</td></tr>
 <tr><td><a href="/about/versions/android-2.3.3.html">2.3.3 - 2.3.7
-        </a></td><!-- Gingerbread -->                                       <td>10</td><td>44%</td></tr>
+        </a></td><!-- Gingerbread -->                                       <td>10</td><td>43.9%</td></tr>
 <tr><td><a href="/about/versions/android-3.1.html">3.1</a></td>
                                                    <td rowspan="2">Honeycomb</td>      <td>12</td><td>0.3%</td></tr>
 <tr><td><a href="/about/versions/android-3.2.html">3.2</a></td>      <!-- Honeycomb --><td>13</td><td>0.9%</td></tr>
@@ -51,7 +51,7 @@
 
 <div class="col-8" style="margin-right:0">
 <img style="margin-left:30px" alt=""
-src="//chart.apis.google.com/chart?&cht=p&chs=460x245&chf=bg,s,00000000&chd=t:2.0,7.6,44.2,1.2,28.6,16.5&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c"
+src="//chart.apis.google.com/chart?&cht=p&chs=460x245&chf=bg,s,00000000&chd=t:2.1,7.5,44.1,1.2,28.6,16.5&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c"
 />
 
 </div><!-- end dashboard-panel -->
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 190a6d9..0cb2635 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -345,7 +345,7 @@
   var playlistId = "PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0"; /* DevBytes */
   var script = "<script type='text/javascript' src='//gdata.youtube.com/feeds/api/playlists/"
                 + playlistId +
-                "?v=2&alt=json-in-script&max-results=10&callback=renderDevelopersLivePlaylist&orderby=published'><\/script > ";
+                "?v=2&alt=json-in-script&max-results=10&callback=renderDevelopersLivePlaylist&orderby=reversedPosition'><\/script > ";
   $("body").append(script);
 }
 
diff --git a/docs/html/google/play/licensing/adding-licensing.jd b/docs/html/google/play/licensing/adding-licensing.jd
index f991e14..3f2460f 100644
--- a/docs/html/google/play/licensing/adding-licensing.jd
+++ b/docs/html/google/play/licensing/adding-licensing.jd
@@ -598,15 +598,15 @@
 <p>First, open the class file of the application's main Activity and import
 {@code LicenseChecker} and {@code LicenseCheckerCallback} from the LVL package.</p>
 
-<pre>    import com.android.vending.licensing.LicenseChecker;
-    import com.android.vending.licensing.LicenseCheckerCallback;</pre>
+<pre>    import com.google.android.vending.licensing.LicenseChecker;
+    import com.google.android.vending.licensing.LicenseCheckerCallback;</pre>
 
 <p>If you are using the default {@code Policy} implementation provided with the LVL,
 ServerManagedPolicy, import it also, together with the AESObfuscator. If you are
 using a custom {@code Policy} or {@code Obfuscator}, import those instead. </p>
 
-<pre>    import com.android.vending.licensing.ServerManagedPolicy;
-    import com.android.vending.licensing.AESObfuscator;</pre>
+<pre>    import com.google.android.vending.licensing.ServerManagedPolicy;
+    import com.google.android.vending.licensing.AESObfuscator;</pre>
 
 <h3 id="lc-impl">Implement LicenseCheckerCallback as a private inner class</h3>
 
diff --git a/docs/html/guide/topics/manifest/intent-filter-element.jd b/docs/html/guide/topics/manifest/intent-filter-element.jd
index f90541c..68da981 100644
--- a/docs/html/guide/topics/manifest/intent-filter-element.jd
+++ b/docs/html/guide/topics/manifest/intent-filter-element.jd
@@ -119,7 +119,11 @@
 
 <p>
 The value must be an integer, such as "{@code 100}".  Higher numbers have a
-higher priority.
+higher priority. The default value is 0.
+The value must be greater than -1000 and less than 1000.</p>
+
+<p>Also see {@link android.content.IntentFilter#setPriority
+setPriority()}.
 </p></dd>
 
 </dl></dd>
diff --git a/docs/html/guide/topics/resources/animation-resource.jd b/docs/html/guide/topics/resources/animation-resource.jd
index 3af52aa..ef64f07 100644
--- a/docs/html/guide/topics/resources/animation-resource.jd
+++ b/docs/html/guide/topics/resources/animation-resource.jd
@@ -217,7 +217,7 @@
     </dd>
 
 <dt id="val-animator-element"><code>&lt;animator&gt;</code></dt>
-    <dd>Animates a over a specified amount of time.
+    <dd>Performs an animation over a specified amount of time.
     Represents a {@link android.animation.ValueAnimator}.
 
       <p class="caps">attributes:</p>
diff --git a/docs/html/images/home/io-logo-2013.png b/docs/html/images/home/io-logo-2013.png
index c95719e..1a200e1 100644
--- a/docs/html/images/home/io-logo-2013.png
+++ b/docs/html/images/home/io-logo-2013.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index a0029b5..f2df7be 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -24,7 +24,7 @@
                     <p>For more information about event details and planned sessions,
                     stay tuned to <a
                     href="http://google.com/+GoogleDevelopers">+Google Developers</a>.</p>
-                    <p><a href="https://developers.google.com/events/io/" class="button">Register here</a></p>
+                    <p><a href="https://developers.google.com/events/io/register" class="button">Register here</a></p>
                     </div>
                 </li>
                 <li class="item carousel-home">
diff --git a/docs/html/training/gestures/detector.jd b/docs/html/training/gestures/detector.jd
index 06d0e98..65ddb1b 100644
--- a/docs/html/training/gestures/detector.jd
+++ b/docs/html/training/gestures/detector.jd
@@ -25,12 +25,18 @@
    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
diff --git a/docs/html/training/gestures/index.jd b/docs/html/training/gestures/index.jd
index 0191450..16ca7b0 100644
--- a/docs/html/training/gestures/index.jd
+++ b/docs/html/training/gestures/index.jd
@@ -20,12 +20,18 @@
     <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
diff --git a/docs/html/training/gestures/movement.jd b/docs/html/training/gestures/movement.jd
index f2c49d7..fdc1ea4 100644
--- a/docs/html/training/gestures/movement.jd
+++ b/docs/html/training/gestures/movement.jd
@@ -24,12 +24,18 @@
     <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
diff --git a/docs/html/training/gestures/multi.jd b/docs/html/training/gestures/multi.jd
index d4c5b1d..6a0df11 100644
--- a/docs/html/training/gestures/multi.jd
+++ b/docs/html/training/gestures/multi.jd
@@ -25,12 +25,18 @@
    <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
diff --git a/docs/html/training/gestures/scale.jd b/docs/html/training/gestures/scale.jd
index 17e4085..f2e4eb8 100644
--- a/docs/html/training/gestures/scale.jd
+++ b/docs/html/training/gestures/scale.jd
@@ -15,6 +15,7 @@
 <h2>This lesson teaches you to</h2>
 <ol>
   <li><a href="#drag">Drag an Object</a></li>
+  <li><a href="#pan">Drag to Pan</a></li>
   <li><a href="#scale">Use Touch to Perform Scaling</a></li>
 </ol>
 
@@ -25,20 +26,25 @@
     <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
+
 <p>This lesson describes how to use touch gestures to drag and scale on-screen
 objects, using {@link android.view.View#onTouchEvent onTouchEvent()} to intercept
-touch events. Here is the original <a
-href="http://code.google.com/p/android-touchexample/">source code</a>
-for the examples used in this lesson.
+touch events. 
 </p>
 
 <h2 id="drag">Drag an Object</h2>
@@ -128,17 +134,15 @@
         final float x = MotionEventCompat.getX(ev, pointerIndex);
         final float y = MotionEventCompat.getY(ev, pointerIndex);
             
-        // Only move if the ScaleGestureDetector isn't processing a gesture.
-        if (!mScaleDetector.isInProgress()) {
-            // Calculate the distance moved
-            final float dx = x - mLastTouchX;
-            final float dy = y - mLastTouchY;
+        // Calculate the distance moved
+        final float dx = x - mLastTouchX;
+        final float dy = y - mLastTouchY;
 
-            mPosX += dx;
-            mPosY += dy;
+        mPosX += dx;
+        mPosY += dy;
 
-            invalidate();
-        }
+        invalidate();
+
         // Remember this touch position for the next move event
         mLastTouchX = x;
         mLastTouchY = y;
@@ -175,6 +179,88 @@
     return true;
 }</pre>
 
+<h2 id="pan">Drag to Pan</h2>
+
+<p>The previous section showed an example of dragging an object around the screen. Another 
+common scenario is <em>panning</em>, which is when a user's dragging motion causes scrolling 
+in both the x and y axes. The above snippet directly intercepted the {@link android.view.MotionEvent} 
+actions to implement dragging. The snippet in this section takes advantage of the platform's 
+built-in support for common gestures. It overrides 
+{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} in 
+{@link android.view.GestureDetector.SimpleOnGestureListener}.</p>
+
+<p>To provide a little more context, {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} 
+is called when a user is dragging his finger to pan the content. 
+{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} is only called when 
+a finger is down; as soon as the finger is lifted from the screen, the gesture either ends, 
+or a fling gesture is started (if the finger was moving with some speed just before it was lifted). 
+For more discussion of scrolling vs. flinging, see <a href="scroll.html">Animating a Scroll Gesture</a>.</p>
+
+<p>Here is the snippet for {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()}:
+
+
+<pre>// The current viewport. This rectangle represents the currently visible 
+// chart domain and range. 
+private RectF mCurrentViewport = 
+        new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
+
+// The current destination rectangle (in pixel coordinates) into which the 
+// chart data should be drawn.
+private Rect mContentRect;
+
+private final GestureDetector.SimpleOnGestureListener mGestureListener
+            = new GestureDetector.SimpleOnGestureListener() {
+...
+
+&#64;Override
+public boolean onScroll(MotionEvent e1, MotionEvent e2, 
+            float distanceX, float distanceY) {
+    // Scrolling uses math based on the viewport (as opposed to math using pixels).
+    
+    // Pixel offset is the offset in screen pixels, while viewport offset is the
+    // offset within the current viewport. 
+    float viewportOffsetX = distanceX * mCurrentViewport.width() 
+            / mContentRect.width();
+    float viewportOffsetY = -distanceY * mCurrentViewport.height() 
+            / mContentRect.height();
+    ...
+    // Updates the viewport, refreshes the display. 
+    setViewportBottomLeft(
+            mCurrentViewport.left + viewportOffsetX,
+            mCurrentViewport.bottom + viewportOffsetY);
+    ...
+    return true;
+}</pre>
+
+<p>The implementation of {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} 
+scrolls the viewport in response to the touch gesture:</p>
+
+<pre>
+/**
+ * Sets the current viewport (defined by mCurrentViewport) to the given
+ * X and Y positions. Note that the Y value represents the topmost pixel position, 
+ * and thus the bottom of the mCurrentViewport rectangle.
+ */
+private void setViewportBottomLeft(float x, float y) {
+    /*
+     * Constrains within the scroll range. The scroll range is simply the viewport 
+     * extremes (AXIS_X_MAX, etc.) minus the viewport size. For example, if the 
+     * extremes were 0 and 10, and the viewport size was 2, the scroll range would 
+     * be 0 to 8.
+     */
+
+    float curWidth = mCurrentViewport.width();
+    float curHeight = mCurrentViewport.height();
+    x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth));
+    y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX));
+
+    mCurrentViewport.set(x, y - curHeight, x + curWidth, y);
+
+    // Invalidates the View to update the display.
+    ViewCompat.postInvalidateOnAnimation(this);
+}
+</pre>
+
 <h2 id="scale">Use Touch to Perform Scaling</h2>
 
 <p>As discussed in <a href="detector.html">Detecting Common Gestures</a>,
@@ -191,10 +277,10 @@
 {@link android.view.ScaleGestureDetector.SimpleOnScaleGestureListener} 
 as a helper class that you can extend if you don’t care about all of the reported events.</p>
 
-<p>Here is a snippet that gives you the basic idea of how to perform scaling.
-Here is the original <a
-href="http://code.google.com/p/android-touchexample/">source code</a>
-for the examples.</p>
+
+<h3>Basic scaling example</h3>
+
+<p>Here is a snippet that illustrates the basic ingredients involved in scaling.</p>
 
 <pre>private ScaleGestureDetector mScaleDetector;
 private float mScaleFactor = 1.f;
@@ -238,3 +324,88 @@
         return true;
     }
 }</pre>
+
+
+
+
+<h3>More complex scaling example</h3>
+<p>Here is a more complex example from the {@code InteractiveChart} sample provided with this class. 
+The {@code InteractiveChart} sample supports both scrolling (panning) and scaling with multiple fingers,
+using the {@link android.view.ScaleGestureDetector} "span" 
+({@link android.view.ScaleGestureDetector#getCurrentSpanX getCurrentSpanX/Y}) and 
+"focus" ({@link android.view.ScaleGestureDetector#getFocusX getFocusX/Y}) features:</p>
+
+<pre>&#64;Override
+private RectF mCurrentViewport = 
+        new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
+private Rect mContentRect;
+private ScaleGestureDetector mScaleGestureDetector;
+...
+public boolean onTouchEvent(MotionEvent event) {
+    boolean retVal = mScaleGestureDetector.onTouchEvent(event);
+    retVal = mGestureDetector.onTouchEvent(event) || retVal;
+    return retVal || super.onTouchEvent(event);
+}
+
+/**
+ * The scale listener, used for handling multi-finger scale gestures.
+ */
+private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener
+        = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+    /**
+     * This is the active focal point in terms of the viewport. Could be a local
+     * variable but kept here to minimize per-frame allocations.
+     */
+    private PointF viewportFocus = new PointF();
+    private float lastSpanX;
+    private float lastSpanY;
+
+    // Detects that new pointers are going down.
+    &#64;Override
+    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
+        lastSpanX = ScaleGestureDetectorCompat.
+                getCurrentSpanX(scaleGestureDetector);
+        lastSpanY = ScaleGestureDetectorCompat.
+                getCurrentSpanY(scaleGestureDetector);
+        return true;
+    }
+
+    &#64;Override
+    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+
+        float spanX = ScaleGestureDetectorCompat.
+                getCurrentSpanX(scaleGestureDetector);
+        float spanY = ScaleGestureDetectorCompat.
+                getCurrentSpanY(scaleGestureDetector);
+
+        float newWidth = lastSpanX / spanX * mCurrentViewport.width();
+        float newHeight = lastSpanY / spanY * mCurrentViewport.height();
+
+        float focusX = scaleGestureDetector.getFocusX();
+        float focusY = scaleGestureDetector.getFocusY();
+        // Makes sure that the chart point is within the chart region.
+        // See the sample for the implementation of hitTest().
+        hitTest(scaleGestureDetector.getFocusX(),
+                scaleGestureDetector.getFocusY(),
+                viewportFocus);
+
+        mCurrentViewport.set(
+                viewportFocus.x
+                        - newWidth * (focusX - mContentRect.left)
+                        / mContentRect.width(),
+                viewportFocus.y
+                        - newHeight * (mContentRect.bottom - focusY)
+                        / mContentRect.height(),
+                0,
+                0);
+        mCurrentViewport.right = mCurrentViewport.left + newWidth;
+        mCurrentViewport.bottom = mCurrentViewport.top + newHeight;     
+        ...
+        // Invalidates the View to update the display.
+        ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
+
+        lastSpanX = spanX;
+        lastSpanY = spanY;
+        return true;
+    }
+};</pre>
diff --git a/docs/html/training/gestures/scroll.jd b/docs/html/training/gestures/scroll.jd
index 8576948..bd1537a 100644
--- a/docs/html/training/gestures/scroll.jd
+++ b/docs/html/training/gestures/scroll.jd
@@ -14,6 +14,7 @@
 <!-- table of contents -->
 <h2>This lesson teaches you to</h2>
 <ol>
+  <li><a href="#term">Understand Scrolling Terminology</a></li>
   <li><a href="#scroll">Implement Touch-Based Scrolling</a></li>
 </ol>
 
@@ -24,12 +25,18 @@
     <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
 
 </div>
 </div>
@@ -45,7 +52,26 @@
 
 <p>You can use scrollers ({@link android.widget.Scroller} or {@link
 android.widget.OverScroller}) to collect the data you need to produce a
-scrolling animation in response to a touch event.</p>
+scrolling animation in response to a touch event. They are similar, but
+{@link android.widget.OverScroller}
+includes methods for indicating to users that they've reached the content edges 
+after a pan or fling gesture. The {@code InteractiveChart} sample 
+uses the the {@link android.widget.EdgeEffect} class 
+(actually the {@link android.support.v4.widget.EdgeEffectCompat} class)
+to display a "glow" effect when users reach the content edges.</p>
+
+<p class="note"><strong>Note:</strong> We recommend that you
+use {@link android.widget.OverScroller} rather than {@link
+android.widget.Scroller} for scrolling animations.
+{@link android.widget.OverScroller} provides the best backward
+compatibility with older devices.
+<br />
+Also note that you generally only need to use scrollers
+when implementing scrolling yourself. {@link android.widget.ScrollView} and
+{@link android.widget.HorizontalScrollView} do all of this for you if you nest your 
+layout within them.
+</p>
+
 
 <p>A scroller is used  to animate scrolling over time, using platform-standard
 scrolling physics (friction, velocity, etc.). The scroller itself doesn't
@@ -54,101 +80,280 @@
 responsibility to get and apply new coordinates at a rate that will make the
 scrolling animation look smooth.</p>
 
-<p class="note"><strong>Note:</strong> You generally only need to use scrollers
-when implementing scrolling yourself. {@link android.widget.ScrollView} and
-{@link android.widget.HorizontalScrollView} do all this for you do all of this for you if you nest your layout within them.</p>
-
-<h2 id = "scroll">Implement Touch-Based Scrolling</h2>
 
 
-<p>This snippet illustrates the basics of using a scroller. It uses a 
-{@link android.view.GestureDetector}, and overrides the  
-{@link android.view.GestureDetector.SimpleOnGestureListener} methods 
-{@link android.view.GestureDetector.OnGestureListener#onDown onDown()} and 
-{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}. It also 
-overrides {@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} 
-to return {@code false} since you don't need to animate a scroll.</p>
+<h2 id="term">Understand Scrolling Terminology</h2>
 
+<p>"Scrolling" is a word that can take on different meanings in Android, depending on the context.</p>
 
-<p>It's common to use scrollers in conjunction with a fling gesture, but they
+<p><strong>Scrolling</strong> is the general process of moving the viewport (that is, the 'window' 
+of content you're looking at). When scrolling is in both the x and y axes, it's called 
+<em>panning</em>. The sample application provided with this class, {@code InteractiveChart}, illustrates 
+two different types of scrolling, dragging and flinging:</p>
+<ul>
+    <li><strong>Dragging</strong> is the type of scrolling that occurs when a user drags her 
+finger across the touch screen. Simple dragging is often implemented by overriding 
+{@link android.view.GestureDetector.OnGestureListener#onScroll onScroll()} in 
+{@link android.view.GestureDetector.OnGestureListener}. For more discussion of dragging, see 
+<a href="dragging.jd">Dragging and Scaling</a>.</li>
+
+    <li><strong>Flinging</strong> is the type of scrolling that occurs when a user 
+drags and lifts her finger quickly. After the user lifts her finger, you generally 
+want to keep scrolling (moving the viewport), but decelerate until the viewport stops moving. 
+Flinging can be implemented by overriding 
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()} 
+in {@link android.view.GestureDetector.OnGestureListener}, and by using 
+a scroller object. This is the use 
+case that is the topic of this lesson.</li>
+</ul>
+
+<p>It's common to use scroller objects 
+in conjunction with a fling gesture, but they
 can be used in pretty much any context where you want the UI to display
-scrolling in response to a touch event. For example, you could override {@link
-android.view.View#onTouchEvent onTouchEvent()} to process touch events directly,
-and produce a scrolling effect in response to those touch events.</p>
+scrolling in response to a touch event. For example, you could override  
+{@link android.view.View#onTouchEvent onTouchEvent()} to process touch 
+events directly, and produce a scrolling effect or a "snapping to page" animation 
+in response to those touch events.</p>
 
-<pre>
-private OverScroller mScroller = new OverScroller(context);
 
-private GestureDetector.SimpleOnGestureListener mGestureListener
+<h2 id="#scroll">Implement Touch-Based Scrolling</h2> 
+
+<p>This section describes how to use a scroller.
+The snippet shown below comes from the {@code InteractiveChart} sample 
+provided with this class.
+It uses a 
+{@link android.view.GestureDetector}, and overrides the  
+{@link android.view.GestureDetector.SimpleOnGestureListener} method 
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}.
+It uses {@link android.widget.OverScroller} to track the fling gesture.
+If the user reaches the content edges 
+after the fling gesture, the app displays a "glow" effect.
+</p>
+
+<p class="note"><strong>Note:</strong> The {@code InteractiveChart} sample app displays a 
+chart that you can zoom, pan, scroll, and so on. In the following snippet, 
+{@code mContentRect} represents the rectangle coordinates within the view that the chart 
+will be drawn into. At any given time, a subset of the total chart domain and range are drawn 
+into this rectangular area. 
+{@code mCurrentViewport} represents the portion of the chart that is currently 
+visible in the screen. Because pixel offsets are generally treated as integers, 
+{@code mContentRect} is of the type {@link android.graphics.Rect}. Because the 
+graph domain and range are decimal/float values, {@code mCurrentViewport} is of 
+the type {@link android.graphics.RectF}.</p>
+
+<p>The first part of the snippet shows the implementation of 
+{@link android.view.GestureDetector.OnGestureListener#onFling onFling()}:</p>
+
+<pre>// The current viewport. This rectangle represents the currently visible 
+// chart domain and range. The viewport is the part of the app that the
+// user manipulates via touch gestures.
+private RectF mCurrentViewport = 
+        new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
+
+// The current destination rectangle (in pixel coordinates) into which the 
+// chart data should be drawn.
+private Rect mContentRect;
+
+private OverScroller mScroller;
+private RectF mScrollerStartViewport;
+...
+private final GestureDetector.SimpleOnGestureListener mGestureListener
         = new GestureDetector.SimpleOnGestureListener() {
     &#64;Override
     public boolean onDown(MotionEvent e) {
-        // Abort any active scroll animations and invalidate.
+        // Initiates the decay phase of any active edge effects.
+        releaseEdgeEffects();
+        mScrollerStartViewport.set(mCurrentViewport);
+        // Aborts any active scroll animations and invalidates.
         mScroller.forceFinished(true);
-        // There is also a compatibility version: 
-        // ViewCompat.postInvalidateOnAnimation
-        postInvalidateOnAnimation();
+        ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
         return true;
     }
-
-    &#64;Override
-    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
-            float distanceX, float distanceY) {
-        // You don't use a scroller in onScroll because you don't need to animate
-        // a scroll. The scroll occurs instantly in response to touch feedback.
-        return false;
-    }
-
+    ...
     &#64;Override
     public boolean onFling(MotionEvent e1, MotionEvent e2, 
             float velocityX, float velocityY) {
-        // Before flinging, abort the current animation.
-        mScroller.forceFinished(true);
-        // Begin the scroll animation
-        mScroller.fling(
-                // Current scroll position
-                startX,
-                startY,
-                // Velocities, negated for natural touch response
-                (int) -velocityX,
-                (int) -velocityY,
-                // Minimum and maximum scroll positions. The minimum scroll 
-                // position is generally zero and the maximum scroll position 
-                // is generally the content size less the screen size. So if the 
-                // content width is 1000 pixels and the screen width is 200  
-                // pixels, the maximum scroll offset should be 800 pixels.
-                minX, maxX,
-                minY, maxY,
-                // The maximum overscroll bounds. This is useful when using
-                // the EdgeEffect class to draw overscroll "glow" overlays.
-                mContentRect.width() / 2,
-                mContentRect.height() / 2);
-        // Invalidate to trigger computeScroll()
-        postInvalidateOnAnimation();
+        fling((int) -velocityX, (int) -velocityY);
         return true;
     }
 };
 
+private void fling(int velocityX, int velocityY) {
+    // Initiates the decay phase of any active edge effects.
+    releaseEdgeEffects();
+    // Flings use math in pixels (as opposed to math based on the viewport).
+    Point surfaceSize = computeScrollSurfaceSize();
+    mScrollerStartViewport.set(mCurrentViewport);
+    int startX = (int) (surfaceSize.x * (mScrollerStartViewport.left - 
+            AXIS_X_MIN) / (
+            AXIS_X_MAX - AXIS_X_MIN));
+    int startY = (int) (surfaceSize.y * (AXIS_Y_MAX - 
+            mScrollerStartViewport.bottom) / (
+            AXIS_Y_MAX - AXIS_Y_MIN));
+    // Before flinging, aborts the current animation.
+    mScroller.forceFinished(true);
+    // Begins the animation
+    mScroller.fling(
+            // Current scroll position
+            startX,
+            startY,
+            velocityX,
+            velocityY,
+            /*
+             * Minimum and maximum scroll positions. The minimum scroll 
+             * position is generally zero and the maximum scroll position 
+             * is generally the content size less the screen size. So if the 
+             * content width is 1000 pixels and the screen width is 200  
+             * pixels, the maximum scroll offset should be 800 pixels.
+             */
+            0, surfaceSize.x - mContentRect.width(),
+            0, surfaceSize.y - mContentRect.height(),
+            // The edges of the content. This comes into play when using
+            // the EdgeEffect class to draw "glow" overlays.
+            mContentRect.width() / 2,
+            mContentRect.height() / 2);
+    // Invalidates to trigger computeScroll()
+    ViewCompat.postInvalidateOnAnimation(this);
+}</pre>
+
+<p>When {@link android.view.GestureDetector.OnGestureListener#onFling onFling()} calls 
+{@link android.support.v4.view.ViewCompat#postInvalidateOnAnimation postInvalidateOnAnimation()}, 
+it triggers 
+{@link android.view.View#computeScroll computeScroll()} to update the values for x and y. 
+This is typically be done when a view child is animating a scroll using a scroller object, as in this example. </p>
+
+<p>Most views pass the scroller object's x and y position directly to 
+{@link android.view.View#scrollTo scrollTo()}. 
+The following implementation of {@link android.view.View#computeScroll computeScroll()} 
+takes a different approach&mdash;it calls 
+{@link android.widget.OverScroller#computeScrollOffset computeScrollOffset()} to get the current 
+location of x and y. When the criteria for displaying an overscroll "glow" edge effect are met 
+(the display is zoomed in, x or y is out of bounds, and the app isn't already showing an overscroll), 
+the code sets up the overscroll glow effect and calls 
+{@link android.support.v4.view.ViewCompat#postInvalidateOnAnimation postInvalidateOnAnimation()} 
+to trigger an invalidate on the view:</p>
+
+<pre>// Edge effect / overscroll tracking objects.
+private EdgeEffectCompat mEdgeEffectTop;
+private EdgeEffectCompat mEdgeEffectBottom;
+private EdgeEffectCompat mEdgeEffectLeft;
+private EdgeEffectCompat mEdgeEffectRight;
+
+private boolean mEdgeEffectTopActive;
+private boolean mEdgeEffectBottomActive;
+private boolean mEdgeEffectLeftActive;
+private boolean mEdgeEffectRightActive;
+
 &#64;Override
 public void computeScroll() {
     super.computeScroll();
 
-    // Compute the current scroll offsets. If this returns true, then the 
-    // scroll has not yet finished.
+    boolean needsInvalidate = false;
+
+    // The scroller isn't finished, meaning a fling or programmatic pan 
+    // operation is currently active.
     if (mScroller.computeScrollOffset()) {
+        Point surfaceSize = computeScrollSurfaceSize();
         int currX = mScroller.getCurrX();
         int currY = mScroller.getCurrY();
 
-        // Actually render the scrolled viewport, or actually scroll the 
-        // view using View.scrollTo.
+        boolean canScrollX = (mCurrentViewport.left > AXIS_X_MIN
+                || mCurrentViewport.right < AXIS_X_MAX);
+        boolean canScrollY = (mCurrentViewport.top > AXIS_Y_MIN
+                || mCurrentViewport.bottom < AXIS_Y_MAX);
 
-        // If currX or currY are outside the bounds, render the overscroll 
-        // glow using EdgeEffect.
+        /*          
+         * If you are zoomed in and currX or currY is
+         * outside of bounds and you're not already
+         * showing overscroll, then render the overscroll
+         * glow edge effect.
+         */
+        if (canScrollX
+                && currX < 0
+                && mEdgeEffectLeft.isFinished()
+                && !mEdgeEffectLeftActive) {
+            mEdgeEffectLeft.onAbsorb((int) 
+                    OverScrollerCompat.getCurrVelocity(mScroller));
+            mEdgeEffectLeftActive = true;
+            needsInvalidate = true;
+        } else if (canScrollX
+                && currX > (surfaceSize.x - mContentRect.width())
+                && mEdgeEffectRight.isFinished()
+                && !mEdgeEffectRightActive) {
+            mEdgeEffectRight.onAbsorb((int) 
+                    OverScrollerCompat.getCurrVelocity(mScroller));
+            mEdgeEffectRightActive = true;
+            needsInvalidate = true;
+        }
 
-    } else {
-        // The scroll has finished.
-    }
+        if (canScrollY
+                && currY < 0
+                && mEdgeEffectTop.isFinished()
+                && !mEdgeEffectTopActive) {
+            mEdgeEffectTop.onAbsorb((int) 
+                    OverScrollerCompat.getCurrVelocity(mScroller));
+            mEdgeEffectTopActive = true;
+            needsInvalidate = true;
+        } else if (canScrollY
+                && currY > (surfaceSize.y - mContentRect.height())
+                && mEdgeEffectBottom.isFinished()
+                && !mEdgeEffectBottomActive) {
+            mEdgeEffectBottom.onAbsorb((int) 
+                    OverScrollerCompat.getCurrVelocity(mScroller));
+            mEdgeEffectBottomActive = true;
+            needsInvalidate = true;
+        }
+        ...
+    }</pre>
+
+<p>Here is the section of the code that performs the actual zoom:</p>
+
+<pre>// Custom object that is functionally similar to Scroller
+Zoomer mZoomer;
+private PointF mZoomFocalPoint = new PointF();
+...
+
+// If a zoom is in progress (either programmatically or via double
+// touch), performs the zoom.
+if (mZoomer.computeZoom()) {
+    float newWidth = (1f - mZoomer.getCurrZoom()) * 
+            mScrollerStartViewport.width();
+    float newHeight = (1f - mZoomer.getCurrZoom()) * 
+            mScrollerStartViewport.height();
+    float pointWithinViewportX = (mZoomFocalPoint.x - 
+            mScrollerStartViewport.left)
+            / mScrollerStartViewport.width();
+    float pointWithinViewportY = (mZoomFocalPoint.y - 
+            mScrollerStartViewport.top)
+            / mScrollerStartViewport.height();
+    mCurrentViewport.set(
+            mZoomFocalPoint.x - newWidth * pointWithinViewportX,
+            mZoomFocalPoint.y - newHeight * pointWithinViewportY,
+            mZoomFocalPoint.x + newWidth * (1 - pointWithinViewportX),
+            mZoomFocalPoint.y + newHeight * (1 - pointWithinViewportY));
+    constrainViewport();
+    needsInvalidate = true;
+}
+if (needsInvalidate) {
+    ViewCompat.postInvalidateOnAnimation(this);
+}
+</pre>
+
+<p>This is the {@code computeScrollSurfaceSize()} method that's called in the above snippet. It 
+computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, 
+this is simply the current size of {@code mContentRect}. If the chart is zoomed in 200% in both directions, 
+the returned size will be twice as large horizontally and vertically.</p>
+
+<pre>private Point computeScrollSurfaceSize() {
+    return new Point(
+            (int) (mContentRect.width() * (AXIS_X_MAX - AXIS_X_MIN)
+                    / mCurrentViewport.width()),
+            (int) (mContentRect.height() * (AXIS_Y_MAX - AXIS_Y_MIN)
+                    / mCurrentViewport.height()));
 }</pre>
 
-<p>For another example of scroller usage, see the <a href="http://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java">source code</a> for the 
-{@link android.support.v4.view.ViewPager} class.</p>
+<p>For another example of scroller usage, see the 
+<a href="http://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/view/ViewPager.java">source code</a> for the 
+{@link android.support.v4.view.ViewPager} class. It scrolls in response to flings, 
+and uses scrolling to implement the "snapping to page" animation.</p>
+
diff --git a/docs/html/training/gestures/viewgroup.jd b/docs/html/training/gestures/viewgroup.jd
index 257a5d8..5b32300 100644
--- a/docs/html/training/gestures/viewgroup.jd
+++ b/docs/html/training/gestures/viewgroup.jd
@@ -26,12 +26,19 @@
     <li><a href="http://developer.android.com/guide/topics/ui/ui-events.html">Input Events</a> API Guide
     </li>
     <li><a href="{@docRoot}guide/topics/sensors/sensors_overview.html">Sensors Overview</a></li>
-    <li><a href="http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html">Making Sense of Multitouch</a> blog post</li>
     <li><a href="{@docRoot}training/custom-views/making-interactive.html">Making the View Interactive</a> </li>
     <li>Design Guide for <a href="{@docRoot}design/patterns/gestures.html">Gestures</a></li>
     <li>Design Guide for <a href="{@docRoot}design/style/touch-feedback.html">Touch Feedback</a></li>
 </ul>
 
+<h2>Try it out</h2>
+
+<div class="download-box">
+  <a href="{@docRoot}shareables/training/InteractiveChart.zip"
+class="button">Download the sample</a>
+ <p class="filename">InteractiveChart.zip</p>
+</div>
+
 
 </div>
 </div>
diff --git a/docs/html/training/id-auth/authenticate.jd b/docs/html/training/id-auth/authenticate.jd
index 592fe1c..3084bea 100644
--- a/docs/html/training/id-auth/authenticate.jd
+++ b/docs/html/training/id-auth/authenticate.jd
@@ -114,7 +114,7 @@
     new Handler(new OnError()));    // Callback called if an error occurs
 </pre>
 
-<p>In this example, <code>OnTokenAcquired</code> is a class that extends
+<p>In this example, <code>OnTokenAcquired</code> is a class that implements
 {@link android.accounts.AccountManagerCallback}. {@link android.accounts.AccountManager} calls
 {@link android.accounts.AccountManagerCallback#run run()} on <code>OnTokenAcquired</code> with an
 {@link android.accounts.AccountManagerFuture} that contains a {@link android.os.Bundle}. If
@@ -179,7 +179,7 @@
     &#64;Override
     public void run(AccountManagerFuture&lt;Bundle&gt; result) {
         ...
-        Intent launch = (Intent) result.get(AccountManager.KEY_INTENT);
+        Intent launch = (Intent) result.getResult().get(AccountManager.KEY_INTENT);
         if (launch != null) {
             startActivityForResult(launch, 0);
             return;
diff --git a/docs/html/training/implementing-navigation/lateral.jd b/docs/html/training/implementing-navigation/lateral.jd
index b59bab2..9a31d7a 100644
--- a/docs/html/training/implementing-navigation/lateral.jd
+++ b/docs/html/training/implementing-navigation/lateral.jd
@@ -131,6 +131,9 @@
     ViewPager mViewPager;
 
     public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_collection_demo);
+
         // ViewPager and its adapters use support library
         // fragments, so use getSupportFragmentManager.
         mDemoCollectionPagerAdapter =
diff --git a/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java b/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
index 24af6ea..3e58b87 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsic3DLUT.java
@@ -45,7 +45,7 @@
         int id = rs.nScriptIntrinsicCreate(8, e.getID(rs));
 
         if (!e.isCompatible(Element.U8_4(rs))) {
-            throw new RSIllegalArgumentException("Element must be compatibile with uchar4.");
+            throw new RSIllegalArgumentException("Element must be compatible with uchar4.");
         }
 
         return new ScriptIntrinsic3DLUT(id, rs, e);
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index dcf41b7..b8d3f48 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -553,7 +553,7 @@
     if (buf == NULL) {
         ALOGE("Out of mem allocating transfer buffer");
         err = ENOMEM;
-        goto cleanup;
+        goto done;
     }
 
     // Magic fields for the ustar file format
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 33d8063..1618110 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -5,7 +5,9 @@
 # defined in the current device/board configuration
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SRC_FILES:= \
+		utils/Blur.cpp \
 		utils/SortedListImpl.cpp \
+		thread/TaskManager.cpp \
 		font/CacheTexture.cpp \
 		font/Font.cpp \
 		FontRenderer.cpp \
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index ca699d5..dc32a7e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -25,6 +25,9 @@
 
 #include <cutils/compiler.h>
 
+#include "thread/TaskProcessor.h"
+#include "thread/TaskManager.h"
+
 #include "FontRenderer.h"
 #include "GammaFontRenderer.h"
 #include "TextureCache.h"
@@ -278,6 +281,8 @@
 
     GammaFontRenderer* fontRenderer;
 
+    TaskManager tasks;
+
     Dither dither;
     Stencil stencil;
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index d231907..105f45f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1005,7 +1005,7 @@
             : DrawBoundedOp(paint), mPath(path) {
         float left, top, offset;
         uint32_t width, height;
-        computePathBounds(path, paint, left, top, offset, width, height);
+        PathCache::computePathBounds(path, paint, left, top, offset, width, height);
         left -= offset;
         top -= offset;
         mLocalBounds.set(left, top, left + width, top + height);
@@ -1016,6 +1016,11 @@
         return renderer.drawPath(mPath, getPaint(renderer));
     }
 
+    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+        SkPaint* paint = getPaint(renderer);
+        renderer.getCaches().pathCache.precache(mPath, paint);
+    }
+
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
     }
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 77b8df1..f0dcb30 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -23,9 +23,11 @@
 
 #include <utils/Log.h>
 
-#include "RenderScript.h"
+#include <RenderScript.h>
 
+#include "utils/Blur.h"
 #include "utils/Timing.h"
+
 #include "Caches.h"
 #include "Debug.h"
 #include "FontRenderer.h"
@@ -569,7 +571,7 @@
     }
 
     int size = paddedWidth * paddedHeight;
-    uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
+    uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
     memset(dataBuffer, 0, size);
 
     int penX = radius - bounds.left;
@@ -655,147 +657,21 @@
     }
 }
 
-void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
-    // Compute gaussian weights for the blur
-    // e is the euler's number
-    float e = 2.718281828459045f;
-    float pi = 3.1415926535897932f;
-    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
-    // x is of the form [-radius .. 0 .. radius]
-    // and sigma varies with radius.
-    // Based on some experimental radius values and sigma's
-    // we approximately fit sigma = f(radius) as
-    // sigma = radius * 0.3  + 0.6
-    // The larger the radius gets, the more our gaussian blur
-    // will resemble a box blur since with large sigma
-    // the gaussian curve begins to lose its shape
-    float sigma = 0.3f * (float) radius + 0.6f;
-
-    // Now compute the coefficints
-    // We will store some redundant values to save some math during
-    // the blur calculations
-    // precompute some values
-    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
-    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
-
-    float normalizeFactor = 0.0f;
-    for (int32_t r = -radius; r <= radius; r ++) {
-        float floatR = (float) r;
-        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
-        normalizeFactor += weights[r + radius];
-    }
-
-    //Now we need to normalize the weights because all our coefficients need to add up to one
-    normalizeFactor = 1.0f / normalizeFactor;
-    for (int32_t r = -radius; r <= radius; r ++) {
-        weights[r + radius] *= normalizeFactor;
-    }
-}
-
-void FontRenderer::horizontalBlur(float* weights, int32_t radius,
-        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
-    float blurredPixel = 0.0f;
-    float currentPixel = 0.0f;
-
-    for (int32_t y = 0; y < height; y ++) {
-
-        const uint8_t* input = source + y * width;
-        uint8_t* output = dest + y * width;
-
-        for (int32_t x = 0; x < width; x ++) {
-            blurredPixel = 0.0f;
-            const float* gPtr = weights;
-            // Optimization for non-border pixels
-            if (x > radius && x < (width - radius)) {
-                const uint8_t *i = input + (x - radius);
-                for (int r = -radius; r <= radius; r ++) {
-                    currentPixel = (float) (*i);
-                    blurredPixel += currentPixel * gPtr[0];
-                    gPtr++;
-                    i++;
-                }
-            } else {
-                for (int32_t r = -radius; r <= radius; r ++) {
-                    // Stepping left and right away from the pixel
-                    int validW = x + r;
-                    if (validW < 0) {
-                        validW = 0;
-                    }
-                    if (validW > width - 1) {
-                        validW = width - 1;
-                    }
-
-                    currentPixel = (float) input[validW];
-                    blurredPixel += currentPixel * gPtr[0];
-                    gPtr++;
-                }
-            }
-            *output = (uint8_t)blurredPixel;
-            output ++;
-        }
-    }
-}
-
-void FontRenderer::verticalBlur(float* weights, int32_t radius,
-        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
-    float blurredPixel = 0.0f;
-    float currentPixel = 0.0f;
-
-    for (int32_t y = 0; y < height; y ++) {
-        uint8_t* output = dest + y * width;
-
-        for (int32_t x = 0; x < width; x ++) {
-            blurredPixel = 0.0f;
-            const float* gPtr = weights;
-            const uint8_t* input = source + x;
-            // Optimization for non-border pixels
-            if (y > radius && y < (height - radius)) {
-                const uint8_t *i = input + ((y - radius) * width);
-                for (int32_t r = -radius; r <= radius; r ++) {
-                    currentPixel = (float)(*i);
-                    blurredPixel += currentPixel * gPtr[0];
-                    gPtr++;
-                    i += width;
-                }
-            } else {
-                for (int32_t r = -radius; r <= radius; r ++) {
-                    int validH = y + r;
-                    // Clamp to zero and width
-                    if (validH < 0) {
-                        validH = 0;
-                    }
-                    if (validH > height - 1) {
-                        validH = height - 1;
-                    }
-
-                    const uint8_t *i = input + validH * width;
-                    currentPixel = (float) (*i);
-                    blurredPixel += currentPixel * gPtr[0];
-                    gPtr++;
-                }
-            }
-            *output = (uint8_t) blurredPixel;
-            output++;
-        }
-    }
-}
-
 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
     if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
         float *gaussian = new float[2 * radius + 1];
-        computeGaussianWeights(gaussian, radius);
+        Blur::generateGaussianWeights(gaussian, radius);
 
         uint8_t* scratch = new uint8_t[width * height];
-
-        horizontalBlur(gaussian, radius, *image, scratch, width, height);
-        verticalBlur(gaussian, radius, scratch, *image, width, height);
+        Blur::horizontal(gaussian, radius, *image, scratch, width, height);
+        Blur::vertical(gaussian, radius, scratch, *image, width, height);
 
         delete[] gaussian;
         delete[] scratch;
         return;
     }
 
-    uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
+    uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
 
     if (mRs.get() == 0) {
         mRs = new RSC::RS();
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 78f9cf5..d681609 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <utils/JenkinsHash.h>
-#include <utils/threads.h>
 
 #include "Caches.h"
 #include "Debug.h"
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 22ec93b..7fe0a69 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -276,6 +276,12 @@
     renderOverdraw();
     endTiling();
 
+    // When finish() is invoked on FBO 0 we've reached the end
+    // of the current frame
+    if (getTargetFbo() == 0) {
+        mCaches.pathCache.trim();
+    }
+
     if (!suppressErrorChecks()) {
 #if DEBUG_OPENGL
         GLenum status = GL_NO_ERROR;
@@ -2633,16 +2639,7 @@
         fontTransform = mat4::identity();
     } else {
         if (CC_UNLIKELY(transform.isPerspective())) {
-            // When the below condition is true, we are rendering text with a
-            // perspective transform inside a layer (either an inline layer
-            // created by Canvas.saveLayer() or a hardware layer.)
-            if (hasLayer() || getTargetFbo() != 0) {
-                float sx, sy;
-                currentTransform().decomposeScale(sx, sy);
-                fontTransform.loadScale(sx, sy, 1.0f);
-            } else {
-                fontTransform = mat4::identity();
-            }
+            fontTransform = mat4::identity();
         } else {
             float sx, sy;
             currentTransform().decomposeScale(sx, sy);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 4f682ed..afdc2c9 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -16,34 +16,47 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
+#include <sys/sysinfo.h>
+
+#include "Caches.h"
 #include "PathCache.h"
 #include "Properties.h"
 
 namespace android {
 namespace uirenderer {
 
-// Defined in ShapeCache.h
+///////////////////////////////////////////////////////////////////////////////
+// Path precaching
+///////////////////////////////////////////////////////////////////////////////
 
-void computePathBounds(const SkPath* path, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-    const SkRect& bounds = path->getBounds();
-    computeBounds(bounds, paint, left, top, offset, width, height);
+PathCache::PathProcessor::PathProcessor(Caches& caches):
+        TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
 }
 
-void computeBounds(const SkRect& bounds, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-    const float pathWidth = fmax(bounds.width(), 1.0f);
-    const float pathHeight = fmax(bounds.height(), 1.0f);
+void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
+    sp<PathTask> t = static_cast<PathTask* >(task.get());
+    ATRACE_NAME("pathPrecache");
 
-    left = bounds.fLeft;
-    top = bounds.fTop;
+    float left, top, offset;
+    uint32_t width, height;
+    PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height);
 
-    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+    PathTexture* texture = t->texture;
+    texture->left = left;
+    texture->top = top;
+    texture->offset = offset;
+    texture->width = width;
+    texture->height = height;
 
-    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+    if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
+        SkBitmap* bitmap = new SkBitmap();
+        PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+        t->setResult(bitmap);
+    } else {
+        t->setResult(NULL);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -54,13 +67,16 @@
         PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
 }
 
+PathCache::~PathCache() {
+}
+
 void PathCache::remove(SkPath* path) {
     Vector<PathCacheEntry> pathsToRemove;
     LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
 
     while (i.next()) {
         const PathCacheEntry& key = i.key();
-        if (key.path == path) {
+        if (key.path == path || key.path == path->getSourcePath()) {
             pathsToRemove.push(key);
         }
     }
@@ -71,12 +87,12 @@
 }
 
 void PathCache::removeDeferred(SkPath* path) {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock l(mLock);
     mGarbage.push(path);
 }
 
 void PathCache::clearGarbage() {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock l(mLock);
     size_t count = mGarbage.size();
     for (size_t i = 0; i < count; i++) {
         remove(mGarbage.itemAt(i));
@@ -84,24 +100,97 @@
     mGarbage.clear();
 }
 
-PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+/**
+ * To properly handle path mutations at draw time we always make a copy
+ * of paths objects when recording display lists. The source path points
+ * to the path we originally copied the path from. This ensures we use
+ * the original path as a cache key the first time a path is inserted
+ * in the cache. The source path is also used to reclaim garbage when a
+ * Dalvik Path object is collected.
+ */
+static SkPath* getSourcePath(SkPath* path) {
     const SkPath* sourcePath = path->getSourcePath();
     if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
-        path = const_cast<SkPath*>(sourcePath);
+        return const_cast<SkPath*>(sourcePath);
     }
+    return path;
+}
+
+PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+    path = getSourcePath(path);
 
     PathCacheEntry entry(path, paint);
     PathTexture* texture = mCache.get(entry);
 
     if (!texture) {
         texture = addTexture(entry, path, paint);
-    } else if (path->getGenerationID() != texture->generation) {
-        mCache.remove(entry);
-        texture = addTexture(entry, path, paint);
+    } else {
+        // A bitmap is attached to the texture, this means we need to
+        // upload it as a GL texture
+        const sp<Task<SkBitmap*> >& task = texture->task();
+        if (task != NULL) {
+            // But we must first wait for the worker thread to be done
+            // producing the bitmap, so let's wait
+            SkBitmap* bitmap = task->getResult();
+            if (bitmap) {
+                addTexture(entry, bitmap, texture);
+                texture->clearTask();
+            } else {
+                ALOGW("Path too large to be rendered into a texture (%dx%d)",
+                        texture->width, texture->height);
+                texture->clearTask();
+                texture = NULL;
+                mCache.remove(entry);
+            }
+        } else if (path->getGenerationID() != texture->generation) {
+            mCache.remove(entry);
+            texture = addTexture(entry, path, paint);
+        }
     }
 
     return texture;
 }
 
+void PathCache::precache(SkPath* path, SkPaint* paint) {
+    if (!Caches::getInstance().tasks.canRunTasks()) {
+        return;
+    }
+
+    path = getSourcePath(path);
+
+    PathCacheEntry entry(path, paint);
+    PathTexture* texture = mCache.get(entry);
+
+    bool generate = false;
+    if (!texture) {
+        generate = true;
+    } else if (path->getGenerationID() != texture->generation) {
+        mCache.remove(entry);
+        generate = true;
+    }
+
+    if (generate) {
+        // It is important to specify the generation ID so we do not
+        // attempt to precache the same path several times
+        texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID());
+        sp<PathTask> task = new PathTask(path, paint, texture);
+        texture->setTask(task);
+
+        // During the precaching phase we insert path texture objects into
+        // the cache that do not point to any GL texture. They are instead
+        // treated as a task for the precaching worker thread. This is why
+        // we do not check the cache limit when inserting these objects.
+        // The conversion into GL texture will happen in get(), when a client
+        // asks for a path texture. This is also when the cache limit will
+        // be enforced.
+        mCache.put(entry, texture);
+
+        if (mProcessor == NULL) {
+            mProcessor = new PathProcessor(Caches::getInstance());
+        }
+        mProcessor->add(task);
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 8a0235b..27031a5 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -17,14 +17,23 @@
 #ifndef ANDROID_HWUI_PATH_CACHE_H
 #define ANDROID_HWUI_PATH_CACHE_H
 
+#include <utils/Thread.h>
 #include <utils/Vector.h>
 
 #include "Debug.h"
 #include "ShapeCache.h"
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+
+class SkPaint;
+class SkPath;
 
 namespace android {
 namespace uirenderer {
 
+class Caches;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
@@ -69,6 +78,7 @@
 class PathCache: public ShapeCache<PathCacheEntry> {
 public:
     PathCache();
+    ~PathCache();
 
     /**
      * Returns the texture associated with the specified path. If the texture
@@ -89,7 +99,36 @@
      */
     void clearGarbage();
 
+    void precache(SkPath* path, SkPaint* paint);
+
 private:
+    class PathTask: public Task<SkBitmap*> {
+    public:
+        PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
+            path(path), paint(paint), texture(texture) {
+        }
+
+        ~PathTask() {
+            delete future()->get();
+        }
+
+        SkPath* path;
+        SkPaint* paint;
+        PathTexture* texture;
+    };
+
+    class PathProcessor: public TaskProcessor<SkBitmap*> {
+    public:
+        PathProcessor(Caches& caches);
+        ~PathProcessor() { }
+
+        virtual void onProcess(const sp<Task<SkBitmap*> >& task);
+
+    private:
+        uint32_t mMaxTextureSize;
+    };
+
+    sp<PathProcessor> mProcessor;
     Vector<SkPath*> mGarbage;
     mutable Mutex mLock;
 }; // class PathCache
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 47cab83e..58fea08 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_HWUI_SHAPE_CACHE_H
 #define ANDROID_HWUI_SHAPE_CACHE_H
 
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
 #include <GLES2/gl2.h>
 
 #include <SkBitmap.h>
@@ -27,10 +29,12 @@
 
 #include <utils/JenkinsHash.h>
 #include <utils/LruCache.h>
+#include <utils/Trace.h>
 
 #include "Debug.h"
 #include "Properties.h"
 #include "Texture.h"
+#include "thread/Task.h"
 
 namespace android {
 namespace uirenderer {
@@ -57,6 +61,10 @@
     PathTexture(): Texture() {
     }
 
+    ~PathTexture() {
+        clearTask();
+    }
+
     /**
      * Left coordinate of the path bounds.
      */
@@ -69,6 +77,23 @@
      * Offset to draw the path at the correct origin.
      */
     float offset;
+
+    sp<Task<SkBitmap*> > task() const {
+        return mTask;
+    }
+
+    void setTask(const sp<Task<SkBitmap*> >& task) {
+        mTask = task;
+    }
+
+    void clearTask() {
+        if (mTask != NULL) {
+            mTask.clear();
+        }
+    }
+
+private:
+    sp<Task<SkBitmap*> > mTask;
 }; // struct PathTexture
 
 /**
@@ -449,6 +474,52 @@
      */
     uint32_t getSize();
 
+    /**
+     * Trims the contents of the cache, removing items until it's under its
+     * specified limit.
+     *
+     * Trimming is used for caches that support pre-caching from a worker
+     * thread. During pre-caching the maximum limit of the cache can be
+     * exceeded for the duration of the frame. It is therefore required to
+     * trim the cache at the end of the frame to keep the total amount of
+     * memory used under control.
+     *
+     * Only the PathCache currently supports pre-caching.
+     */
+    void trim();
+
+    static void computePathBounds(const SkPath* path, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+        const SkRect& bounds = path->getBounds();
+        computeBounds(bounds, paint, left, top, offset, width, height);
+    }
+
+    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+        const float pathWidth = fmax(bounds.width(), 1.0f);
+        const float pathHeight = fmax(bounds.height(), 1.0f);
+
+        left = bounds.fLeft;
+        top = bounds.fTop;
+
+        offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+        width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+        height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+    }
+
+    static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
+            float left, float top, float offset, uint32_t width, uint32_t height) {
+        initBitmap(bitmap, width, height);
+
+        SkPaint pathPaint(*paint);
+        initPaint(pathPaint);
+
+        SkCanvas canvas(bitmap);
+        canvas.translate(-left + offset, -top + offset);
+        canvas.drawPath(*path, pathPaint);
+    }
+
 protected:
     PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
     PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
@@ -460,17 +531,51 @@
      */
     void purgeCache(uint32_t width, uint32_t height);
 
-    void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
-    void initPaint(SkPaint& paint);
-
-    bool checkTextureSize(uint32_t width, uint32_t height);
-
     PathTexture* get(Entry entry) {
         return mCache.get(entry);
     }
 
     void removeTexture(PathTexture* texture);
 
+    bool checkTextureSize(uint32_t width, uint32_t height) {
+        if (width > mMaxTextureSize || height > mMaxTextureSize) {
+            ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
+                    mName, width, height, mMaxTextureSize, mMaxTextureSize);
+            return false;
+        }
+        return true;
+    }
+
+    static PathTexture* createTexture(float left, float top, float offset,
+            uint32_t width, uint32_t height, uint32_t id) {
+        PathTexture* texture = new PathTexture();
+        texture->left = left;
+        texture->top = top;
+        texture->offset = offset;
+        texture->width = width;
+        texture->height = height;
+        texture->generation = id;
+        return texture;
+    }
+
+    static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
+        bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+        bitmap.allocPixels();
+        bitmap.eraseColor(0);
+    }
+
+    static void initPaint(SkPaint& paint) {
+        // Make sure the paint is opaque, color, alpha, filter, etc.
+        // will be applied later when compositing the alpha8 texture
+        paint.setColor(0xff000000);
+        paint.setAlpha(255);
+        paint.setColorFilter(NULL);
+        paint.setMaskFilter(NULL);
+        paint.setShader(NULL);
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+        SkSafeUnref(paint.setXfermode(mode));
+    }
+
     LruCache<Entry, PathTexture*> mCache;
     uint32_t mSize;
     uint32_t mMaxSize;
@@ -617,23 +722,6 @@
     }
 }
 
-void computePathBounds(const SkPath* path, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
-void computeBounds(const SkRect& bounds, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
-
-static PathTexture* createTexture(float left, float top, float offset,
-        uint32_t width, uint32_t height, uint32_t id) {
-    PathTexture* texture = new PathTexture;
-    texture->left = left;
-    texture->top = top;
-    texture->offset = offset;
-    texture->width = width;
-    texture->height = height;
-    texture->generation = id;
-    return texture;
-}
-
 template<class Entry>
 void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
     const uint32_t size = width * height;
@@ -646,38 +734,16 @@
 }
 
 template<class Entry>
-void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-    bitmap.allocPixels();
-    bitmap.eraseColor(0);
-}
-
-template<class Entry>
-void ShapeCache<Entry>::initPaint(SkPaint& paint) {
-    // Make sure the paint is opaque, color, alpha, filter, etc.
-    // will be applied later when compositing the alpha8 texture
-    paint.setColor(0xff000000);
-    paint.setAlpha(255);
-    paint.setColorFilter(NULL);
-    paint.setMaskFilter(NULL);
-    paint.setShader(NULL);
-    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
-    SkSafeUnref(paint.setXfermode(mode));
-}
-
-template<class Entry>
-bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) {
-    if (width > mMaxTextureSize || height > mMaxTextureSize) {
-        ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
-                mName, width, height, mMaxTextureSize, mMaxTextureSize);
-        return false;
+void ShapeCache<Entry>::trim() {
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
     }
-    return true;
 }
 
 template<class Entry>
 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
         const SkPaint* paint) {
+    ATRACE_CALL();
 
     float left, top, offset;
     uint32_t width, height;
@@ -688,16 +754,10 @@
     purgeCache(width, height);
 
     SkBitmap bitmap;
-    initBitmap(bitmap, width, height);
+    drawPath(path, paint, bitmap, left, top, offset, width, height);
 
-    SkPaint pathPaint(*paint);
-    initPaint(pathPaint);
-
-    SkCanvas canvas(bitmap);
-    canvas.translate(-left + offset, -top + offset);
-    canvas.drawPath(*path, pathPaint);
-
-    PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
+    PathTexture* texture = createTexture(left, top, offset, width, height,
+            path->getGenerationID());
     addTexture(entry, &bitmap, texture);
 
     return texture;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 5cff5a5..2378eb5 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -20,7 +20,7 @@
 
 #include <SkCanvas.h>
 
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
 #include "Caches.h"
 #include "TextureCache.h"
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
new file mode 100644
index 0000000..6cb23e5
--- /dev/null
+++ b/libs/hwui/thread/Barrier.h
@@ -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.
+ */
+
+#ifndef ANDROID_HWUI_BARRIER_H
+#define ANDROID_HWUI_BARRIER_H
+
+#include <utils/Condition.h>
+
+namespace android {
+namespace uirenderer {
+
+class Barrier {
+public:
+    Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mOpened(false) { }
+    ~Barrier() { }
+
+    void open() {
+        Mutex::Autolock l(mLock);
+        mOpened = true;
+        mCondition.signal(mType);
+    }
+
+    void close() {
+        Mutex::Autolock l(mLock);
+        mOpened = false;
+    }
+
+    void wait() const {
+        Mutex::Autolock l(mLock);
+        while (!mOpened) {
+            mCondition.wait(mLock);
+        }
+    }
+
+private:
+    Condition::WakeUpType mType;
+    volatile bool mOpened;
+    mutable Mutex mLock;
+    mutable Condition mCondition;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
new file mode 100644
index 0000000..a3ff3bc
--- /dev/null
+++ b/libs/hwui/thread/Future.h
@@ -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.
+ */
+
+#ifndef ANDROID_HWUI_FUTURE_H
+#define ANDROID_HWUI_FUTURE_H
+
+#include <utils/RefBase.h>
+
+#include "Barrier.h"
+
+namespace android {
+namespace uirenderer {
+
+template<typename T>
+class Future: public LightRefBase<Future<T> > {
+public:
+    Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE): mBarrier(type), mResult() { }
+    ~Future() { }
+
+    /**
+     * Returns the result of this future, blocking if
+     * the result is not available yet.
+     */
+    T get() const {
+        mBarrier.wait();
+        return mResult;
+    }
+
+    /**
+     * This method must be called only once.
+     */
+    void produce(T result) {
+        mResult = result;
+        mBarrier.open();
+    }
+
+private:
+    Barrier mBarrier;
+    T mResult;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
new file mode 100644
index 0000000..dcf5449
--- /dev/null
+++ b/libs/hwui/thread/Signal.h
@@ -0,0 +1,56 @@
+/*
+ * 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_HWUI_SIGNAL_H
+#define ANDROID_HWUI_SIGNAL_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/threads.h>
+
+namespace android {
+namespace uirenderer {
+
+class Signal {
+public:
+    Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mSignaled(false) { }
+    ~Signal() { }
+
+    void signal() {
+        Mutex::Autolock l(mLock);
+        mSignaled = true;
+        mCondition.signal(mType);
+    }
+
+    void wait() {
+        Mutex::Autolock l(mLock);
+        while (!mSignaled) {
+            mCondition.wait(mLock);
+        }
+        mSignaled = false;
+    }
+
+private:
+    Condition::WakeUpType mType;
+    volatile bool mSignaled;
+    mutable Mutex mLock;
+    mutable Condition mCondition;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SIGNAL_H
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
new file mode 100644
index 0000000..9a211a2
--- /dev/null
+++ b/libs/hwui/thread/Task.h
@@ -0,0 +1,63 @@
+/*
+ * 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_HWUI_TASK_H
+#define ANDROID_HWUI_TASK_H
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/RefBase.h>
+#include <utils/Trace.h>
+
+#include "Future.h"
+
+namespace android {
+namespace uirenderer {
+
+class TaskBase: public RefBase {
+public:
+    TaskBase() { }
+    virtual ~TaskBase() { }
+};
+
+template<typename T>
+class Task: public TaskBase {
+public:
+    Task(): mFuture(new Future<T>()) { }
+    virtual ~Task() { }
+
+    T getResult() const {
+        ATRACE_NAME("waitForTask");
+        return mFuture->get();
+    }
+
+    void setResult(T result) {
+        mFuture->produce(result);
+    }
+
+protected:
+    const sp<Future<T> >& future() const {
+        return mFuture;
+    }
+
+private:
+    sp<Future<T> > mFuture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TASK_H
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
new file mode 100644
index 0000000..ce6c8c0
--- /dev/null
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -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.
+ */
+
+#include <sys/sysinfo.h>
+
+#include "Task.h"
+#include "TaskProcessor.h"
+#include "TaskManager.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Manager
+///////////////////////////////////////////////////////////////////////////////
+
+TaskManager::TaskManager() {
+    // Get the number of available CPUs. This value does not change over time.
+    int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
+
+    for (int i = 0; i < cpuCount / 2; i++) {
+        String8 name;
+        name.appendFormat("hwuiTask%d", i + 1);
+        mThreads.add(new WorkerThread(name));
+    }
+}
+
+TaskManager::~TaskManager() {
+    for (size_t i = 0; i < mThreads.size(); i++) {
+        mThreads[i]->exit();
+    }
+}
+
+bool TaskManager::canRunTasks() const {
+    return mThreads.size() > 0;
+}
+
+bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) {
+    if (mThreads.size() > 0) {
+        TaskWrapper wrapper(task, processor);
+
+        size_t minQueueSize = INT_MAX;
+        sp<WorkerThread> thread;
+
+        for (size_t i = 0; i < mThreads.size(); i++) {
+            if (mThreads[i]->getTaskCount() < minQueueSize) {
+                thread = mThreads[i];
+                minQueueSize = mThreads[i]->getTaskCount();
+            }
+        }
+
+        return thread->addTask(wrapper);
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Thread
+///////////////////////////////////////////////////////////////////////////////
+
+bool TaskManager::WorkerThread::threadLoop() {
+    mSignal.wait();
+    Vector<TaskWrapper> tasks;
+    {
+        Mutex::Autolock l(mLock);
+        tasks = mTasks;
+        mTasks.clear();
+    }
+
+    for (size_t i = 0; i < tasks.size(); i++) {
+        const TaskWrapper& task = tasks.itemAt(i);
+        task.mProcessor->process(task.mTask);
+    }
+
+    return true;
+}
+
+bool TaskManager::WorkerThread::addTask(TaskWrapper task) {
+    if (!isRunning()) {
+        run(mName.string(), PRIORITY_DEFAULT);
+    }
+
+    Mutex::Autolock l(mLock);
+    ssize_t index = mTasks.add(task);
+    mSignal.signal();
+
+    return index >= 0;
+}
+
+size_t TaskManager::WorkerThread::getTaskCount() const {
+    Mutex::Autolock l(mLock);
+    return mTasks.size();
+}
+
+void TaskManager::WorkerThread::exit() {
+    {
+        Mutex::Autolock l(mLock);
+        mTasks.clear();
+    }
+    requestExit();
+    mSignal.signal();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
new file mode 100644
index 0000000..bc86062
--- /dev/null
+++ b/libs/hwui/thread/TaskManager.h
@@ -0,0 +1,100 @@
+/*
+ * 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_HWUI_TASK_MANAGER_H
+#define ANDROID_HWUI_TASK_MANAGER_H
+
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Vector.h>
+
+#include "Signal.h"
+
+namespace android {
+namespace uirenderer {
+
+template <typename T>
+class Task;
+class TaskBase;
+
+template <typename T>
+class TaskProcessor;
+class TaskProcessorBase;
+
+class TaskManager {
+public:
+    TaskManager();
+    ~TaskManager();
+
+    /**
+     * Returns true if this task  manager can run tasks,
+     * false otherwise. This method will typically return
+     * true on a single CPU core device.
+     */
+    bool canRunTasks() const;
+
+private:
+    template <typename T>
+    friend class TaskProcessor;
+
+    template<typename T>
+    bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) {
+        return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor));
+    }
+
+    bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor);
+
+    struct TaskWrapper {
+        TaskWrapper(): mTask(), mProcessor() { }
+
+        TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor):
+            mTask(task), mProcessor(processor) {
+        }
+
+        sp<TaskBase> mTask;
+        sp<TaskProcessorBase> mProcessor;
+    };
+
+    class WorkerThread: public Thread {
+    public:
+        WorkerThread(const String8 name): mSignal(Condition::WAKE_UP_ONE), mName(name) { }
+
+        bool addTask(TaskWrapper task);
+        size_t getTaskCount() const;
+        void exit();
+
+    private:
+        virtual bool threadLoop();
+
+        // Lock for the list of tasks
+        mutable Mutex mLock;
+        Vector<TaskWrapper> mTasks;
+
+        // Signal used to wake up the thread when a new
+        // task is available in the list
+        mutable Signal mSignal;
+
+        const String8 mName;
+    };
+
+    Vector<sp<WorkerThread> > mThreads;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TASK_MANAGER_H
diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h
new file mode 100644
index 0000000..d1269f0
--- /dev/null
+++ b/libs/hwui/thread/TaskProcessor.h
@@ -0,0 +1,72 @@
+/*
+ * 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_HWUI_TASK_PROCESSOR_H
+#define ANDROID_HWUI_TASK_PROCESSOR_H
+
+#include <utils/RefBase.h>
+
+#include "Task.h"
+#include "TaskManager.h"
+
+namespace android {
+namespace uirenderer {
+
+class TaskProcessorBase: public RefBase {
+public:
+    TaskProcessorBase() { }
+    virtual ~TaskProcessorBase() { };
+
+private:
+    friend class TaskManager;
+
+    virtual void process(const sp<TaskBase>& task) = 0;
+};
+
+template<typename T>
+class TaskProcessor: public TaskProcessorBase {
+public:
+    TaskProcessor(TaskManager* manager): mManager(manager) { }
+    virtual ~TaskProcessor() { }
+
+    bool add(const sp<Task<T> >& task);
+
+    virtual void onProcess(const sp<Task<T> >& task) = 0;
+
+private:
+    virtual void process(const sp<TaskBase>& task) {
+        sp<Task<T> > realTask = static_cast<Task<T>* >(task.get());
+        // This is the right way to do it but sp<> doesn't play nice
+        // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task);
+        onProcess(realTask);
+    }
+
+    TaskManager* mManager;
+};
+
+template<typename T>
+bool TaskProcessor<T>::add(const sp<Task<T> >& task) {
+    if (mManager) {
+        sp<TaskProcessor<T> > self(this);
+        return mManager->addTask(task, self);
+    }
+    return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TASK_PROCESSOR_H
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
new file mode 100644
index 0000000..85d90d0
--- /dev/null
+++ b/libs/hwui/utils/Blur.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <math.h>
+
+#include "Blur.h"
+
+namespace android {
+namespace uirenderer {
+
+void Blur::generateGaussianWeights(float* weights, int32_t radius) {
+    // Compute gaussian weights for the blur
+    // e is the euler's number
+    static float e = 2.718281828459045f;
+    static float pi = 3.1415926535897932f;
+    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+    // x is of the form [-radius .. 0 .. radius]
+    // and sigma varies with radius.
+    // Based on some experimental radius values and sigma's
+    // we approximately fit sigma = f(radius) as
+    // sigma = radius * 0.3  + 0.6
+    // The larger the radius gets, the more our gaussian blur
+    // will resemble a box blur since with large sigma
+    // the gaussian curve begins to lose its shape
+    float sigma = 0.3f * (float) radius + 0.6f;
+
+    // Now compute the coefficints
+    // We will store some redundant values to save some math during
+    // the blur calculations
+    // precompute some values
+    float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+    float normalizeFactor = 0.0f;
+    for (int32_t r = -radius; r <= radius; r ++) {
+        float floatR = (float) r;
+        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += weights[r + radius];
+    }
+
+    //Now we need to normalize the weights because all our coefficients need to add up to one
+    normalizeFactor = 1.0f / normalizeFactor;
+    for (int32_t r = -radius; r <= radius; r ++) {
+        weights[r + radius] *= normalizeFactor;
+    }
+}
+
+void Blur::horizontal(float* weights, int32_t radius,
+        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
+    float blurredPixel = 0.0f;
+    float currentPixel = 0.0f;
+
+    for (int32_t y = 0; y < height; y ++) {
+
+        const uint8_t* input = source + y * width;
+        uint8_t* output = dest + y * width;
+
+        for (int32_t x = 0; x < width; x ++) {
+            blurredPixel = 0.0f;
+            const float* gPtr = weights;
+            // Optimization for non-border pixels
+            if (x > radius && x < (width - radius)) {
+                const uint8_t *i = input + (x - radius);
+                for (int r = -radius; r <= radius; r ++) {
+                    currentPixel = (float) (*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                    i++;
+                }
+            } else {
+                for (int32_t r = -radius; r <= radius; r ++) {
+                    // Stepping left and right away from the pixel
+                    int validW = x + r;
+                    if (validW < 0) {
+                        validW = 0;
+                    }
+                    if (validW > width - 1) {
+                        validW = width - 1;
+                    }
+
+                    currentPixel = (float) input[validW];
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                }
+            }
+            *output = (uint8_t)blurredPixel;
+            output ++;
+        }
+    }
+}
+
+void Blur::vertical(float* weights, int32_t radius,
+        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
+    float blurredPixel = 0.0f;
+    float currentPixel = 0.0f;
+
+    for (int32_t y = 0; y < height; y ++) {
+        uint8_t* output = dest + y * width;
+
+        for (int32_t x = 0; x < width; x ++) {
+            blurredPixel = 0.0f;
+            const float* gPtr = weights;
+            const uint8_t* input = source + x;
+            // Optimization for non-border pixels
+            if (y > radius && y < (height - radius)) {
+                const uint8_t *i = input + ((y - radius) * width);
+                for (int32_t r = -radius; r <= radius; r ++) {
+                    currentPixel = (float) (*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                    i += width;
+                }
+            } else {
+                for (int32_t r = -radius; r <= radius; r ++) {
+                    int validH = y + r;
+                    // Clamp to zero and width
+                    if (validH < 0) {
+                        validH = 0;
+                    }
+                    if (validH > height - 1) {
+                        validH = height - 1;
+                    }
+
+                    const uint8_t *i = input + validH * width;
+                    currentPixel = (float) (*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                }
+            }
+            *output = (uint8_t) blurredPixel;
+            output++;
+        }
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
new file mode 100644
index 0000000..6c176e9
--- /dev/null
+++ b/libs/hwui/utils/Blur.h
@@ -0,0 +1,37 @@
+/*
+ * 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_HWUI_BLUR_H
+#define ANDROID_HWUI_BLUR_H
+
+#include <stdint.h>
+
+namespace android {
+namespace uirenderer {
+
+class Blur {
+public:
+    static void generateGaussianWeights(float* weights, int32_t radius);
+    static void horizontal(float* weights, int32_t radius, const uint8_t* source,
+        uint8_t* dest, int32_t width, int32_t height);
+    static void vertical(float* weights, int32_t radius, const uint8_t* source,
+        uint8_t* dest, int32_t width, int32_t height);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BLUR_H
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 86976b8..135d2c8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2171,15 +2171,36 @@
     /**
      * @hide
      * Registers a remote control display that will be sent information by remote control clients.
-     * @param rcd
+     * Use this method if your IRemoteControlDisplay is not going to display artwork, otherwise
+     * use {@link #registerRemoteControlDisplay(IRemoteControlDisplay, int, int)} to pass the
+     * artwork size directly, or
+     * {@link #remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay, int, int)} later if artwork
+     * is not yet needed.
+     * @param rcd the IRemoteControlDisplay
      */
     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        // passing a negative value for art work width and height as they are unknown at this stage
+        registerRemoteControlDisplay(rcd, /*w*/-1, /*h*/ -1);
+    }
+
+    /**
+     * @hide
+     * Registers a remote control display that will be sent information by remote control clients.
+     * @param rcd
+     * @param w the maximum width of the expected bitmap. Negative values indicate it is
+     *   useless to send artwork.
+     * @param h the maximum height of the expected bitmap. Negative values indicate it is
+     *   useless to send artwork.
+     */
+    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
         if (rcd == null) {
             return;
         }
         IAudioService service = getService();
         try {
-            service.registerRemoteControlDisplay(rcd);
+            // passing a negative value for art work width and height as they are unknown at
+            // this stage
+            service.registerRemoteControlDisplay(rcd, w, h);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e);
         }
@@ -2223,63 +2244,6 @@
         }
     }
 
-    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
-    /**
-     * @hide
-     * Broadcast intent action indicating that the displays on the remote controls
-     * should be updated because a new remote control client is now active. If there is no
-     * {@link #EXTRA_REMOTE_CONTROL_CLIENT}, the remote control display should be cleared
-     * because there is no valid client to supply it with information.
-     *
-     * @see #EXTRA_REMOTE_CONTROL_CLIENT
-     */
-    public static final String REMOTE_CONTROL_CLIENT_CHANGED =
-            "android.media.REMOTE_CONTROL_CLIENT_CHANGED";
-
-    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
-    /**
-     * @hide
-     * The IRemoteControlClientDispatcher monotonically increasing generation counter.
-     *
-     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
-     */
-    public static final String EXTRA_REMOTE_CONTROL_CLIENT_GENERATION =
-            "android.media.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION";
-
-    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
-    /**
-     * @hide
-     * The name of the RemoteControlClient.
-     * This String is passed as the client name when calling methods from the
-     * IRemoteControlClientDispatcher interface.
-     *
-     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
-     */
-    public static final String EXTRA_REMOTE_CONTROL_CLIENT_NAME =
-            "android.media.EXTRA_REMOTE_CONTROL_CLIENT_NAME";
-
-    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
-    /**
-     * @hide
-     * The media button event receiver associated with the RemoteControlClient.
-     * The {@link android.content.ComponentName} value of the event receiver can be retrieved with
-     * {@link android.content.ComponentName#unflattenFromString(String)}
-     *
-     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
-     */
-    public static final String EXTRA_REMOTE_CONTROL_EVENT_RECEIVER =
-            "android.media.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER";
-
-    // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
-    /**
-     * @hide
-     * The flags describing what information has changed in the current remote control client.
-     *
-     * @see #REMOTE_CONTROL_CLIENT_CHANGED_ACTION
-     */
-    public static final String EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED =
-            "android.media.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED";
-
     /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index ed2a8da..f0a5c28 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -158,6 +158,8 @@
     private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
     private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
     private static final int MSG_PROMOTE_RCC = 29;
+    private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
+
 
     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
     // persisted
@@ -1947,7 +1949,14 @@
             return;
         }
         ScoClient client = getScoClient(cb, true);
+        // The calling identity must be cleared before calling ScoClient.incCount().
+        // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
+        // and this must be done on behalf of system server to make sure permissions are granted.
+        // The caller identity must be cleared after getScoClient() because it is needed if a new
+        // client is created.
+        final long ident = Binder.clearCallingIdentity();
         client.incCount();
+        Binder.restoreCallingIdentity(ident);
     }
 
     /** @see AudioManager#stopBluetoothSco() */
@@ -1957,9 +1966,14 @@
             return;
         }
         ScoClient client = getScoClient(cb, false);
+        // The calling identity must be cleared before calling ScoClient.decCount().
+        // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
+        // and this must be done on behalf of system server to make sure permissions are granted.
+        final long ident = Binder.clearCallingIdentity();
         if (client != null) {
             client.decCount();
         }
+        Binder.restoreCallingIdentity(ident);
     }
 
 
@@ -2209,6 +2223,11 @@
     }
 
     private void broadcastScoConnectionState(int state) {
+        sendMsg(mAudioHandler, MSG_BROADCAST_BT_CONNECTION_STATE,
+                SENDMSG_QUEUE, state, 0, null, 0);
+    }
+
+    private void onBroadcastScoConnectionState(int state) {
         if (state != mScoConnectionState) {
             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
@@ -3532,6 +3551,10 @@
                 case MSG_PROMOTE_RCC:
                     onPromoteRcc(msg.arg1);
                     break;
+
+                case MSG_BROADCAST_BT_CONNECTION_STATE:
+                    onBroadcastScoConnectionState(msg.arg1);
+                    break;
             }
         }
     }
@@ -4890,7 +4913,6 @@
                         "  -- vol: " + rcse.mPlaybackVolume +
                         "  -- volMax: " + rcse.mPlaybackVolumeMax +
                         "  -- volObs: " + rcse.mRemoteVolumeObs);
-
             }
         }
         synchronized (mMainRemote) {
@@ -4908,6 +4930,23 @@
 
     /**
      * Helper function:
+     * Display in the log the current entries in the list of remote control displays
+     */
+    private void dumpRCDList(PrintWriter pw) {
+        pw.println("\nRemote Control Display list entries:");
+        synchronized(mRCStack) {
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                pw.println("  IRCD: " + di.mRcDisplay +
+                        "  -- w:" + di.mArtworkExpectedWidth +
+                        "  -- h:" + di.mArtworkExpectedHeight);
+            }
+        }
+    }
+
+    /**
+     * Helper function:
      * Remove any entry in the remote control stack that has the same package name as packageName
      * Pre-condition: packageName != null
      */
@@ -5044,16 +5083,20 @@
      */
     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
             PendingIntent newMediaIntent, boolean clearing) {
-        // NOTE: Only one IRemoteControlDisplay supported in this implementation
-        if (mRcDisplay != null) {
-            try {
-                mRcDisplay.setCurrentClientId(
-                        newClientGeneration, newMediaIntent, clearing);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
-                // if we had a display before, stop monitoring its death
-                rcDisplay_stopDeathMonitor_syncRcStack();
-                mRcDisplay = null;
+        synchronized(mRCStack) {
+            if (mRcDisplays.size() > 0) {
+                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+                while (displayIterator.hasNext()) {
+                    final DisplayInfoForServer di = displayIterator.next();
+                    try {
+                        di.mRcDisplay.setCurrentClientId(
+                                newClientGeneration, newMediaIntent, clearing);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
+                        di.release();
+                        displayIterator.remove();
+                    }
+                }
             }
         }
     }
@@ -5071,7 +5114,7 @@
                 try {
                     se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
+                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
                     stackIterator.remove();
                     se.unlinkToRcClientDeath();
                 }
@@ -5129,8 +5172,7 @@
 
                     // tell the current client that it needs to send info
                     try {
-                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
-                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
+                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Current valid remote client is dead: "+e);
                         mCurrentRcClient = null;
@@ -5394,13 +5436,9 @@
                             rccId = rcse.mRccId;
 
                             // there is a new (non-null) client:
-                            // 1/ give the new client the current display (if any)
-                            if (mRcDisplay != null) {
-                                try {
-                                    rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
-                                } catch (RemoteException e) {
-                                    Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
-                                }
+                            // 1/ give the new client the displays (if any)
+                            if (mRcDisplays.size() > 0) {
+                                plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
                             }
                             // 2/ monitor the new client's death
                             IBinder b = rcse.mRcClient.asBinder();
@@ -5470,102 +5508,141 @@
         }
     }
 
-    /**
-     * The remote control displays.
-     * Access synchronized on mRCStack
-     * NOTE: Only one IRemoteControlDisplay supported in this implementation
-     */
-    private IRemoteControlDisplay mRcDisplay;
-    private RcDisplayDeathHandler mRcDisplayDeathHandler;
-    private int mArtworkExpectedWidth = -1;
-    private int mArtworkExpectedHeight = -1;
-    /**
-     * Inner class to monitor remote control display deaths, and unregister them from the list
-     * of displays if necessary.
-     */
-    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
 
-        public RcDisplayDeathHandler(IBinder b) {
-            if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
-            mCb = b;
+    /**
+     * A class to encapsulate all the information about a remote control display.
+     * After instanciation, init() must always be called before the object is added in the list
+     * of displays.
+     * Before being removed from the list of displays, release() must always be called (otherwise
+     * it will leak death handlers).
+     */
+    private class DisplayInfoForServer implements IBinder.DeathRecipient {
+        /** may never be null */
+        private IRemoteControlDisplay mRcDisplay;
+        private IBinder mRcDisplayBinder;
+        private int mArtworkExpectedWidth = -1;
+        private int mArtworkExpectedHeight = -1;
+
+        public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
+            if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
+            mRcDisplay = rcd;
+            mRcDisplayBinder = rcd.asBinder();
+            mArtworkExpectedWidth = w;
+            mArtworkExpectedHeight = h;
+        }
+
+        public boolean init() {
+            try {
+                mRcDisplayBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                // remote control display is DOA, disqualify it
+                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
+                return false;
+            }
+            return true;
+        }
+
+        public void release() {
+            try {
+                mRcDisplayBinder.unlinkToDeath(this, 0);
+            } catch (java.util.NoSuchElementException e) {
+                // not much we can do here, the display should have been unregistered anyway
+                Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
+            }
         }
 
         public void binderDied() {
             synchronized(mRCStack) {
-                Log.w(TAG, "RemoteControl: display died");
-                mRcDisplay = null;
+                Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
+                // remove the display from the list
+                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+                while (displayIterator.hasNext()) {
+                    final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                    if (di.mRcDisplay == mRcDisplay) {
+                        if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
+                        displayIterator.remove();
+                        return;
+                    }
+                }
             }
         }
-
-        public void unlinkToRcDisplayDeath() {
-            if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
-            try {
-                mCb.unlinkToDeath(this, 0);
-            } catch (java.util.NoSuchElementException e) {
-                // not much we can do here, the display was being unregistered anyway
-                Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
-                e.printStackTrace();
-            }
-        }
-
-    }
-
-    private void rcDisplay_stopDeathMonitor_syncRcStack() {
-        if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
-            // we had a display before, stop monitoring its death
-            mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
-        }
     }
 
-    private void rcDisplay_startDeathMonitor_syncRcStack() {
-        if (mRcDisplay != null) {
-            // new non-null display, monitor its death
-            IBinder b = mRcDisplay.asBinder();
-            mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
+    /**
+     * The remote control displays.
+     * Access synchronized on mRCStack
+     */
+    private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
+
+    /**
+     * Plug each registered display into the specified client
+     * @param rcc, guaranteed non null
+     */
+    private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
+        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+        while (displayIterator.hasNext()) {
+            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
             try {
-                b.linkToDeath(mRcDisplayDeathHandler, 0);
+                rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
+                        di.mArtworkExpectedHeight);
             } catch (RemoteException e) {
-                // remote control display is DOA, disqualify it
-                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
-                mRcDisplay = null;
+                Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
             }
         }
     }
 
     /**
+     * Is the remote control display interface already registered
+     * @param rcd
+     * @return true if the IRemoteControlDisplay is already in the list of displays
+     */
+    private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
+        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+        while (displayIterator.hasNext()) {
+            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+            if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Register an IRemoteControlDisplay.
      * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
      * at the top of the stack to update the new display with its information.
-     * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
+     * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
      * @param rcd the IRemoteControlDisplay to register. No effect if null.
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
      */
-    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                if ((mRcDisplay == rcd) || (rcd == null)) {
+                if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
                     return;
                 }
-                // if we had a display before, stop monitoring its death
-                rcDisplay_stopDeathMonitor_syncRcStack();
-                mRcDisplay = rcd;
-                // new display, start monitoring its death
-                rcDisplay_startDeathMonitor_syncRcStack();
+                DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
+                if (!di.init()) {
+                    if (DEBUG_RC) Log.e(TAG, " error registering RCD");
+                    return;
+                }
+                // add RCD to list of displays
+                mRcDisplays.add(di);
 
-                // let all the remote control clients know there is a new display, so the remote
-                //   control stack traversal order doesn't matter.
-                // No need to unplug the previous because we only support one display
-                // and the clients don't track the death of the display
+                // let all the remote control clients know there is a new display (so the remote
+                //   control stack traversal order doesn't matter).
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = stackIterator.next();
                     if(rcse.mRcClient != null) {
                         try {
-                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+                            rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
                         } catch (RemoteException e) {
-                            Log.e(TAG, "Error connecting remote control display to client: " + e);
-                            e.printStackTrace();
+                            Log.e(TAG, "Error connecting RCD to client: ", e);
                         }
                     }
                 }
@@ -5578,44 +5655,86 @@
 
     /**
      * Unregister an IRemoteControlDisplay.
-     * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
-     *    unregister is not the current one.
+     * No effect if the IRemoteControlDisplay hasn't been successfully registered.
+     * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
      * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
      */
     public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
         if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
         synchronized(mRCStack) {
-            // only one display here, so you can only unregister the current display
-            if ((rcd == null) || (rcd != mRcDisplay)) {
-                if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
+            if (rcd == null) {
                 return;
             }
-            // if we had a display before, stop monitoring its death
-            rcDisplay_stopDeathMonitor_syncRcStack();
-            mRcDisplay = null;
 
-            // disconnect this remote control display from all the clients, so the remote
-            //   control stack traversal order doesn't matter
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                if(rcse.mRcClient != null) {
-                    try {
-                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
-                        e.printStackTrace();
+            boolean displayWasPluggedIn = false;
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext() && !displayWasPluggedIn) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    displayWasPluggedIn = true;
+                    di.release();
+                    displayIterator.remove();
+                }
+            }
+
+            if (displayWasPluggedIn) {
+                // disconnect this remote control display from all the clients, so the remote
+                //   control stack traversal order doesn't matter
+                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    final RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.unplugRemoteControlDisplay(rcd);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error disconnecting remote control display to client: ", e);
+                        }
                     }
                 }
+            } else {
+                if (DEBUG_RC) Log.w(TAG, "  trying to unregister unregistered RCD");
             }
         }
     }
 
+    /**
+     * Update the size of the artwork used by an IRemoteControlDisplay.
+     * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
+     * @param rcd the IRemoteControlDisplay with the new artwork size requirement
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     */
     public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
         synchronized(mRCStack) {
-            // NOTE: Only one IRemoteControlDisplay supported in this implementation
-            mArtworkExpectedWidth = w;
-            mArtworkExpectedHeight = h;
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            boolean artworkSizeUpdate = false;
+            while (displayIterator.hasNext() && !artworkSizeUpdate) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
+                        di.mArtworkExpectedWidth = w;
+                        di.mArtworkExpectedHeight = h;
+                        artworkSizeUpdate = true;
+                    }
+                }
+            }
+            if (artworkSizeUpdate) {
+                // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
+                // stack traversal order doesn't matter
+                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    final RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -6218,6 +6337,7 @@
         dumpFocusStack(pw);
         dumpRCStack(pw);
         dumpRCCStack(pw);
+        dumpRCDList(pw);
         dumpStreamStates(pw);
         dumpRingerMode(pw);
         pw.println("\nAudio routes:");
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ea99069..312c252 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -131,8 +131,31 @@
     oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
            in IRemoteControlClient rcClient);
 
-    oneway void   registerRemoteControlDisplay(in IRemoteControlDisplay rcd);
+    /**
+     * Register an IRemoteControlDisplay.
+     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
+     * at the top of the stack to update the new display with its information.
+     * @param rcd the IRemoteControlDisplay to register. No effect if null.
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     */
+    oneway void   registerRemoteControlDisplay(in IRemoteControlDisplay rcd, int w, int h);
+    /**
+     * Unregister an IRemoteControlDisplay.
+     * No effect if the IRemoteControlDisplay hasn't been successfully registered.
+     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
+     */
     oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
+    /**
+     * Update the size of the artwork used by an IRemoteControlDisplay.
+     * @param rcd the IRemoteControlDisplay with the new artwork size requirement
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     */
     oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
 
     oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
index 0fbba20..5600263 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -34,18 +34,17 @@
      *   parameters are valid.
      * @param generationId
      * @param infoFlags
-     * @param artWidth if > 0, artHeight must be > 0 too.
-     * @param artHeight
      * FIXME: is infoFlags required? since the RCC pushes info, this might always be called
      *        with RC_INFO_ALL
      */
-    void onInformationRequested(int generationId, int infoFlags, int artWidth, int artHeight);
+    void onInformationRequested(int generationId, int infoFlags);
 
     /**
      * Sets the generation counter of the current client that is displayed on the remote control.
      */
     void setCurrentClientGenerationId(int clientGeneration);
 
-    void   plugRemoteControlDisplay(IRemoteControlDisplay rcd);
+    void   plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
     void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
+    void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
 }
\ No newline at end of file
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index e155385..b6b49a2 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -263,12 +263,11 @@
             Surface surface, MediaCrypto crypto, int flags);
 
     /**
-     * Requests a Surface to use instead of input buffers.  This may only be called after
-     * {@link #configure} and before {@link #start}.
+     * Requests a Surface to use as the input to an encoder, in place of input buffers.  This
+     * may only be called after {@link #configure} and before {@link #start}.
      * <p>
      * The application is responsible for calling release() on the Surface when
      * done.
-     * @hide -- TODO(fadden): make this public before release
      */
     public native final Surface createInputSurface();
 
@@ -471,7 +470,6 @@
      * Signals end-of-stream on input.  Equivalent to submitting an empty buffer with
      * {@link #BUFFER_FLAG_END_OF_STREAM} set.  This may only be used with
      * encoders receiving input from a Surface created by {@link #createInputSurface}.
-     * @hide -- TODO(fadden): make this public before release
      */
     public native final void signalEndOfInputStream();
 
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 6c1b87a..1501c79 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -93,8 +93,9 @@
         public final static int COLOR_Format24BitABGR6666           = 43;
 
         public final static int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
-        /** @hide -- TODO(fadden): make this public before release */
-        public final static int COLOR_FormatAndroidOpaque             = 0x7F000789;
+        // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference.
+        // In OMX this is called OMX_COLOR_FormatAndroidOpaque.
+        public final static int COLOR_FormatSurface                   = 0x7F000789;
         public final static int COLOR_QCOM_FormatYUV420SemiPlanar     = 0x7fa30c00;
 
         /**
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
new file mode 100644
index 0000000..dfd0e94
--- /dev/null
+++ b/media/java/android/media/MediaMuxer.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 android.media;
+
+import android.media.MediaCodec.BufferInfo;
+
+import dalvik.system.CloseGuard;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * MediaMuxer facilitates muxing elementary streams. Currently only supports an
+ * mp4 file as the output and at most one audio and/or one video elementary
+ * stream.
+ * <p>
+ * It is generally used like this:
+ *
+ * <pre>
+ * MediaMuxer muxer = new MediaMuxer(...);
+ * MediaFormat audioFormat = new MediaFormat(...);
+ * MediaFormat videoFormat = new MediaFormat(...);
+ * int audioTrackIndex = muxer.addTrack(audioFormat);
+ * int videoTrackIndex = muxer.addTrack(videoFormat);
+ * ByteBuffer inputBuffer = ByteBuffer.allocate(...);
+ * muxer.start();
+ * while(inputBuffer has new data) {
+ *   if (new data is audio sample) {
+ *     muxer.writeSampleData(audioTrackIndex, inputBuffer, ...);
+ *   } else if (new data is video sample) {
+ *     muxer.writeSampleData(videoTrackIndex, inputBuffer, ...);
+ *   }
+ * }
+ * muxer.stop();
+ * muxer.release();
+ * </pre>
+ */
+
+final public class MediaMuxer {
+
+    private int mNativeContext;
+
+    static {
+        System.loadLibrary("media_jni");
+    }
+
+    /**
+     * Defines the output format. These constants are used with constructor.
+     */
+    public static final class OutputFormat {
+        /* Do not change these values without updating their counterparts
+         * in include/media/stagefright/MediaMuxer.h!
+         */
+        private OutputFormat() {}
+        /** MPEG4 media file format*/
+        public static final int MUXER_OUTPUT_MPEG_4 = 0;
+    };
+
+    /**
+     * The sample is a sync sample, which does not require other video samples
+     * to decode. This flag is used in {@link #writeSampleData} to indicate
+     * which sample is a sync sample.
+     */
+    /* Keep this flag in sync with its equivalent in
+     * include/media/stagefright/MediaMuxer.h.
+     */
+    public static final int SAMPLE_FLAG_SYNC = 1;
+
+    // All the native functions are listed here.
+    private static native int nativeSetup(FileDescriptor fd, int format);
+    private static native void nativeRelease(int nativeObject);
+    private static native void nativeStart(int nativeObject);
+    private static native void nativeStop(int nativeObject);
+    private static native int nativeAddTrack(int nativeObject, String[] keys,
+            Object[] values);
+    private static native void nativeWriteSampleData(int nativeObject,
+            int trackIndex, ByteBuffer byteBuf,
+            int offset, int size, long presentationTimeUs, int flags);
+
+    // Muxer internal states.
+    private static final int MUXER_STATE_UNINITIALIZED  = -1;
+    private static final int MUXER_STATE_INITIALIZED    = 0;
+    private static final int MUXER_STATE_STARTED        = 1;
+    private static final int MUXER_STATE_STOPPED        = 2;
+
+    private int mState = MUXER_STATE_UNINITIALIZED;
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+    private int mLastTrackIndex = -1;
+
+    private int mNativeObject;
+
+    /**
+     * Constructor
+     * Creates a media muxer that writes to the specified path.
+     * @param path The path of the output media file.
+     * @param format The format of the output media file.
+     * @see android.media.MediaMuxer.OutputFormat
+     * @throws IOException if failed to open the file for write
+     */
+    public MediaMuxer(String path, int format) throws IOException {
+        if (path == null) {
+            throw new IllegalArgumentException("path must not be null");
+        }
+        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) {
+            throw new IllegalArgumentException("format is invalid");
+        }
+        FileOutputStream fos = null;
+        try {
+            File file = new File(path);
+            fos = new FileOutputStream(file);
+            FileDescriptor fd = fos.getFD();
+            mNativeObject = nativeSetup(fd, format);
+            mState = MUXER_STATE_INITIALIZED;
+            mCloseGuard.open("release");
+        } finally {
+            if (fos != null) {
+                fos.close();
+            }
+        }
+    }
+
+    /**
+     * Starts the muxer.
+     * <p>Make sure this is called after {@link #addTrack} and before
+     * {@link #writeSampleData}.</p>
+     */
+    public void start() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("Muxer has been released!");
+        }
+        if (mState == MUXER_STATE_INITIALIZED) {
+            nativeStart(mNativeObject);
+            mState = MUXER_STATE_STARTED;
+        } else {
+            throw new IllegalStateException("Can't start due to wrong state.");
+        }
+    }
+
+    /**
+     * Stops the muxer.
+     * <p>Once the muxer stops, it can not be restarted.</p>
+     */
+    public void stop() {
+        if (mState == MUXER_STATE_STARTED) {
+            nativeStop(mNativeObject);
+            mState = MUXER_STATE_STOPPED;
+        } else {
+            throw new IllegalStateException("Can't stop due to wrong state.");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            if (mNativeObject != 0) {
+                nativeRelease(mNativeObject);
+                mNativeObject = 0;
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Adds a track with the specified format.
+     * @param format The media format for the track.
+     * @return The track index for this newly added track, and it should be used
+     * in the {@link #writeSampleData}.
+     */
+    public int addTrack(MediaFormat format) {
+        if (format == null) {
+            throw new IllegalArgumentException("format must not be null.");
+        }
+        if (mState != MUXER_STATE_INITIALIZED) {
+            throw new IllegalStateException("Muxer is not initialized.");
+        }
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("Muxer has been released!");
+        }
+        int trackIndex = -1;
+        // Convert the MediaFormat into key-value pairs and send to the native.
+        Map<String, Object> formatMap = format.getMap();
+
+        String[] keys = null;
+        Object[] values = null;
+        int mapSize = formatMap.size();
+        if (mapSize > 0) {
+            keys = new String[mapSize];
+            values = new Object[mapSize];
+            int i = 0;
+            for (Map.Entry<String, Object> entry : formatMap.entrySet()) {
+                keys[i] = entry.getKey();
+                values[i] = entry.getValue();
+                ++i;
+            }
+            trackIndex = nativeAddTrack(mNativeObject, keys, values);
+        } else {
+            throw new IllegalArgumentException("format must not be empty.");
+        }
+
+        // Track index number is expected to incremented as addTrack succeed.
+        // However, if format is invalid, it will get a negative trackIndex.
+        if (mLastTrackIndex >= trackIndex) {
+            throw new IllegalArgumentException("Invalid format.");
+        }
+        mLastTrackIndex = trackIndex;
+        return trackIndex;
+    }
+
+    /**
+     * Writes an encoded sample into the muxer. The application needs to make
+     * sure that the samples are written into the right tracks.
+     * @param byteBuf The encoded sample.
+     * @param trackIndex The track index for this sample.
+     * @param bufferInfo The buffer information related to this sample.
+     */
+    public void writeSampleData(int trackIndex, ByteBuffer byteBuf,
+            BufferInfo bufferInfo) {
+        if (trackIndex < 0 || trackIndex > mLastTrackIndex) {
+            throw new IllegalArgumentException("trackIndex is invalid");
+        }
+
+        if (byteBuf == null) {
+            throw new IllegalArgumentException("byteBuffer must not be null");
+        }
+
+        if (bufferInfo == null) {
+            throw new IllegalArgumentException("bufferInfo must not be null");
+        }
+        if (bufferInfo.size < 0 || bufferInfo.offset < 0
+                || (bufferInfo.offset + bufferInfo.size) > byteBuf.capacity()
+                || bufferInfo.presentationTimeUs < 0) {
+            throw new IllegalArgumentException("bufferInfo must specify a" +
+                    " valid buffer offset, size and presentation time");
+        }
+
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("Muxer has been released!");
+        }
+
+        if (mState != MUXER_STATE_STARTED) {
+            throw new IllegalStateException("Can't write, muxer is not started");
+        }
+
+        nativeWriteSampleData(mNativeObject, trackIndex, byteBuf,
+                bufferInfo.offset, bufferInfo.size,
+                bufferInfo.presentationTimeUs, bufferInfo.flags);
+    }
+
+    /**
+     * Make sure you call this when you're done to free up any resources
+     * instead of relying on the garbage collector to do this for you at
+     * some point in the future.
+     */
+    public void release() {
+        if (mState == MUXER_STATE_STARTED) {
+            throw new IllegalStateException("Can't release when muxer is started");
+        }
+        if (mNativeObject != 0) {
+            nativeRelease(mNativeObject);
+            mNativeObject = 0;
+            mCloseGuard.close();
+        }
+        mState = MUXER_STATE_UNINITIALIZED;
+    }
+}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 8b489b1..795c3c2 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -299,16 +299,21 @@
     }
 
     /**
-     * @hide for use by framework routing UI
+     * Gets the default route for playing media content on the system.
+     * <p>
+     * The system always provides a default route.
+     * </p>
+     *
+     * @return The default route, which is guaranteed to never be null.
      */
-    public RouteInfo getSystemAudioRoute() {
+    public RouteInfo getDefaultRoute() {
         return sStatic.mDefaultAudioVideo;
     }
 
     /**
      * @hide for use by framework routing UI
      */
-    public RouteCategory getSystemAudioCategory() {
+    public RouteCategory getSystemCategory() {
         return sStatic.mSystemCategory;
     }
 
@@ -372,14 +377,17 @@
 
     /**
      * Select the specified route to use for output of the given media types.
+     * <p class="note">
+     * As API version 18, this function may be used to select any route.
+     * In prior versions, this function could only be used to select user
+     * routes and would ignore any attempt to select a system route.
+     * </p>
      *
      * @param types type flags indicating which types this route should be used for.
      *              The route must support at least a subset.
      * @param route Route to select
      */
     public void selectRoute(int types, RouteInfo route) {
-        // Applications shouldn't programmatically change anything but user routes.
-        types &= ROUTE_TYPE_USER;
         selectRouteStatic(types, route);
     }
     
@@ -454,7 +462,7 @@
      * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
      *
      * @param info Definition of the route to add
-     * @see #createUserRoute()
+     * @see #createUserRoute(RouteCategory)
      * @see #removeUserRoute(UserRouteInfo)
      */
     public void addUserRoute(UserRouteInfo info) {
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 4c71ace..9a0ecdf 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -36,6 +36,8 @@
 import android.util.Log;
 
 import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.util.Iterator;
 
 /**
  * RemoteControlClient enables exposing information meant to be consumed by remote controls
@@ -498,13 +500,7 @@
             if (key != BITMAP_KEY_ARTWORK) {
                 throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
             }
-            if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) {
-                mEditorArtwork = scaleBitmapIfTooBig(bitmap,
-                        mArtworkExpectedWidth, mArtworkExpectedHeight);
-            } else {
-                // no valid resize dimensions, store as is
-                mEditorArtwork = bitmap;
-            }
+            mEditorArtwork = bitmap;
             mArtworkChanged = true;
             return this;
         }
@@ -536,10 +532,10 @@
             synchronized(mCacheLock) {
                 // assign the edited data
                 mMetadata = new Bundle(mEditorMetadata);
-                if ((mArtwork != null) && (!mArtwork.equals(mEditorArtwork))) {
-                    mArtwork.recycle();
+                if ((mOriginalArtwork != null) && (!mOriginalArtwork.equals(mEditorArtwork))) {
+                    mOriginalArtwork.recycle();
                 }
-                mArtwork = mEditorArtwork;
+                mOriginalArtwork = mEditorArtwork;
                 mEditorArtwork = null;
                 if (mMetadataChanged & mArtworkChanged) {
                     // send to remote control display if conditions are met
@@ -571,7 +567,7 @@
             editor.mArtworkChanged = true;
         } else {
             editor.mEditorMetadata = new Bundle(mMetadata);
-            editor.mEditorArtwork = mArtwork;
+            editor.mEditorArtwork = mOriginalArtwork;
             editor.mMetadataChanged = false;
             editor.mArtworkChanged = false;
         }
@@ -766,11 +762,7 @@
      * accessed to be resized, in which case a copy will be made. This would add overhead in
      * Bundle operations.
      */
-    private Bitmap mArtwork;
-    private final int ARTWORK_DEFAULT_SIZE = 256;
-    private final int ARTWORK_INVALID_SIZE = -1;
-    private int mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
-    private int mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+    private Bitmap mOriginalArtwork;
     /**
      * Cache for the transport control mask.
      * Access synchronized on mCacheLock
@@ -802,10 +794,27 @@
     private final PendingIntent mRcMediaIntent;
 
     /**
-     * The remote control display to which this client will send information.
-     * NOTE: Only one IRemoteControlDisplay supported in this implementation
+     * A class to encapsulate all the information about a remote control display.
+     * A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay
      */
-    private IRemoteControlDisplay mRcDisplay;
+    private class DisplayInfoForClient {
+        /** may never be null */
+        private IRemoteControlDisplay mRcDisplay;
+        private int mArtworkExpectedWidth;
+        private int mArtworkExpectedHeight;
+
+        DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) {
+            mRcDisplay = rcd;
+            mArtworkExpectedWidth = w;
+            mArtworkExpectedHeight = h;
+        }
+    }
+
+    /**
+     * The list of remote control displays to which this client will send information.
+     * Accessed and modified synchronized on mCacheLock
+     */
+    private ArrayList<DisplayInfoForClient> mRcDisplays = new ArrayList<DisplayInfoForClient>(1);
 
     /**
      * @hide
@@ -827,17 +836,14 @@
      */
     private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
 
-        public void onInformationRequested(int clientGeneration, int infoFlags,
-                int artWidth, int artHeight) {
+        public void onInformationRequested(int clientGeneration, int infoFlags) {
             // only post messages, we can't block here
             if (mEventHandler != null) {
                 // signal new client
                 mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
                 mEventHandler.dispatchMessage(
-                        mEventHandler.obtainMessage(
-                                MSG_NEW_INTERNAL_CLIENT_GEN,
-                                artWidth, artHeight,
-                                new Integer(clientGeneration)));
+                        mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN,
+                                /*arg1*/ clientGeneration, /*arg2, ignored*/ 0));
                 // send the information
                 mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE);
                 mEventHandler.removeMessages(MSG_REQUEST_METADATA);
@@ -861,21 +867,29 @@
             }
         }
 
-        public void plugRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
             // only post messages, we can't block here
-            if (mEventHandler != null) {
+            if ((mEventHandler != null) && (rcd != null)) {
                 mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
-                        MSG_PLUG_DISPLAY, rcd));
+                        MSG_PLUG_DISPLAY, w, h, rcd));
             }
         }
 
         public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
             // only post messages, we can't block here
-            if (mEventHandler != null) {
+            if ((mEventHandler != null) && (rcd != null)) {
                 mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
                         MSG_UNPLUG_DISPLAY, rcd));
             }
         }
+
+        public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) {
+            // only post messages, we can't block here
+            if ((mEventHandler != null) && (rcd != null)) {
+                mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+                        MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd));
+            }
+        }
     };
 
     /**
@@ -915,6 +929,7 @@
     private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6;
     private final static int MSG_PLUG_DISPLAY = 7;
     private final static int MSG_UNPLUG_DISPLAY = 8;
+    private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9;
 
     private class EventHandler extends Handler {
         public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -945,17 +960,20 @@
                     }
                     break;
                 case MSG_NEW_INTERNAL_CLIENT_GEN:
-                    onNewInternalClientGen((Integer)msg.obj, msg.arg1, msg.arg2);
+                    onNewInternalClientGen(msg.arg1);
                     break;
                 case MSG_NEW_CURRENT_CLIENT_GEN:
                     onNewCurrentClientGen(msg.arg1);
                     break;
                 case MSG_PLUG_DISPLAY:
-                    onPlugDisplay((IRemoteControlDisplay)msg.obj);
+                    onPlugDisplay((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
                     break;
                 case MSG_UNPLUG_DISPLAY:
                     onUnplugDisplay((IRemoteControlDisplay)msg.obj);
                     break;
+                case MSG_UPDATE_DISPLAY_ARTWORK_SIZE:
+                    onUpdateDisplayArtworkSize((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
+                    break;
                 default:
                     Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
             }
@@ -963,75 +981,106 @@
     }
 
     //===========================================================
-    // Communication with IRemoteControlDisplay
-
-    private void detachFromDisplay_syncCacheLock() {
-        mRcDisplay = null;
-        mArtworkExpectedWidth = ARTWORK_INVALID_SIZE;
-        mArtworkExpectedHeight = ARTWORK_INVALID_SIZE;
-    }
+    // Communication with the IRemoteControlDisplay (the displays known to the system)
 
     private void sendPlaybackState_syncCacheLock() {
-        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
-            try {
-                mRcDisplay.setPlaybackState(mInternalClientGenId, mPlaybackState,
-                        mPlaybackStateChangeTimeMs);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in setPlaybackState(), dead display "+e);
-                detachFromDisplay_syncCacheLock();
+        if (mCurrentClientGenId == mInternalClientGenId) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                try {
+                    di.mRcDisplay.setPlaybackState(mInternalClientGenId,
+                            mPlaybackState, mPlaybackStateChangeTimeMs);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e);
+                    displayIterator.remove();
+                }
             }
         }
     }
 
     private void sendMetadata_syncCacheLock() {
-        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
-            try {
-                mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in sendPlaybackState(), dead display "+e);
-                detachFromDisplay_syncCacheLock();
+        if (mCurrentClientGenId == mInternalClientGenId) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                try {
+                    di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e);
+                    displayIterator.remove();
+                }
             }
         }
     }
 
     private void sendTransportControlFlags_syncCacheLock() {
-        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
-            try {
-                mRcDisplay.setTransportControlFlags(mInternalClientGenId,
-                        mTransportControlFlags);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in sendTransportControlFlags(), dead display "+e);
-                detachFromDisplay_syncCacheLock();
+        if (mCurrentClientGenId == mInternalClientGenId) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                try {
+                    di.mRcDisplay.setTransportControlFlags(mInternalClientGenId,
+                            mTransportControlFlags);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay,
+                            e);
+                    displayIterator.remove();
+                }
             }
         }
     }
 
     private void sendArtwork_syncCacheLock() {
-        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
-            // even though we have already scaled in setArtwork(), when this client needs to
-            // send the bitmap, there might be newer and smaller expected dimensions, so we have
-            // to check again.
-            mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
-            try {
-                mRcDisplay.setArtwork(mInternalClientGenId, mArtwork);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in sendArtwork(), dead display "+e);
-                detachFromDisplay_syncCacheLock();
+        // FIXME modify to cache all requested sizes?
+        if (mCurrentClientGenId == mInternalClientGenId) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                if (!sendArtworkToDisplay((DisplayInfoForClient) displayIterator.next())) {
+                    displayIterator.remove();
+                }
             }
         }
     }
 
-    private void sendMetadataWithArtwork_syncCacheLock() {
-        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
-            // even though we have already scaled in setArtwork(), when this client needs to
-            // send the bitmap, there might be newer and smaller expected dimensions, so we have
-            // to check again.
-            mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
+    /**
+     * Send artwork to an IRemoteControlDisplay.
+     * @param di encapsulates the IRemoteControlDisplay that will receive the artwork, and its
+     *    dimension requirements.
+     * @return false if there was an error communicating with the IRemoteControlDisplay.
+     */
+    private boolean sendArtworkToDisplay(DisplayInfoForClient di) {
+        if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
+            Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
+                    di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
             try {
-                mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, mArtwork);
+                di.mRcDisplay.setArtwork(mInternalClientGenId, artwork);
             } catch (RemoteException e) {
-                Log.e(TAG, "Error in setAllMetadata(), dead display "+e);
-                detachFromDisplay_syncCacheLock();
+                Log.e(TAG, "Error in sendArtworkToDisplay(), dead display " + di.mRcDisplay, e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void sendMetadataWithArtwork_syncCacheLock() {
+        // FIXME modify to cache all requested sizes?
+        if (mCurrentClientGenId == mInternalClientGenId) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                try {
+                    if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
+                        Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
+                                di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
+                        di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
+                    } else {
+                        di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error when setting metadata, dead display " + di.mRcDisplay, e);
+                    displayIterator.remove();
+                }
             }
         }
     }
@@ -1067,15 +1116,11 @@
     //===========================================================
     // Message handlers
 
-    private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) {
+    private void onNewInternalClientGen(int clientGeneration) {
         synchronized (mCacheLock) {
             // this remote control client is told it is the "focused" one:
             // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true
-            mInternalClientGenId = clientGeneration.intValue();
-            if (artWidth > 0) {
-                mArtworkExpectedWidth = artWidth;
-                mArtworkExpectedHeight = artHeight;
-            }
+            mInternalClientGenId = clientGeneration;
         }
     }
 
@@ -1085,18 +1130,62 @@
         }
     }
 
-    private void onPlugDisplay(IRemoteControlDisplay rcd) {
+    /** pre-condition rcd != null */
+    private void onPlugDisplay(IRemoteControlDisplay rcd, int w, int h) {
         synchronized(mCacheLock) {
-            mRcDisplay = rcd;
+            // do we have this display already?
+            boolean displayKnown = false;
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext() && !displayKnown) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                displayKnown = di.mRcDisplay.asBinder().equals(rcd.asBinder());
+                if (displayKnown) {
+                    // this display was known but the change in artwork size will cause the
+                    // artwork to be refreshed
+                    if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
+                        di.mArtworkExpectedWidth = w;
+                        di.mArtworkExpectedHeight = h;
+                        if (!sendArtworkToDisplay(di)) {
+                            displayIterator.remove();
+                        }
+                    }
+                }
+            }
+            if (!displayKnown) {
+                mRcDisplays.add(new DisplayInfoForClient(rcd, w, h));
+            }
         }
     }
 
+    /** pre-condition rcd != null */
     private void onUnplugDisplay(IRemoteControlDisplay rcd) {
         synchronized(mCacheLock) {
-            if ((mRcDisplay != null) && (mRcDisplay.asBinder().equals(rcd.asBinder()))) {
-                mRcDisplay = null;
-                mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
-                mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    displayIterator.remove();
+                    return;
+                }
+            }
+        }
+    }
+
+    /** pre-condition rcd != null */
+    private void onUpdateDisplayArtworkSize(IRemoteControlDisplay rcd, int w, int h) {
+        synchronized(mCacheLock) {
+            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder()) &&
+                        ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) {
+                    di.mArtworkExpectedWidth = w;
+                    di.mArtworkExpectedHeight = h;
+                    if (!sendArtworkToDisplay(di)) {
+                        displayIterator.remove();
+                    }
+                    break;
+                }
             }
         }
     }
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 6aaab220..ac8fb74 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -6,6 +6,7 @@
     android_media_MediaCodec.cpp \
     android_media_MediaCodecList.cpp \
     android_media_MediaExtractor.cpp \
+    android_media_MediaMuxer.cpp \
     android_media_MediaPlayer.cpp \
     android_media_MediaRecorder.cpp \
     android_media_MediaScanner.cpp \
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
new file mode 100644
index 0000000..30ebb00
--- /dev/null
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaMuxer-JNI"
+#include <utils/Log.h>
+
+#include "android_media_Utils.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaMuxer.h>
+
+namespace android {
+
+struct fields_t {
+    jfieldID context;
+    jmethodID arrayID;
+};
+
+static fields_t gFields;
+
+}
+
+using namespace android;
+
+static jint android_media_MediaMuxer_addTrack(
+        JNIEnv *env, jclass clazz, jint nativeObject, jobjectArray keys,
+        jobjectArray values) {
+    sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
+    if (muxer == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Muxer was not set up correctly");
+        return -1;
+    }
+
+    sp<AMessage> trackformat;
+    status_t err = ConvertKeyValueArraysToMessage(env, keys, values,
+                                                  &trackformat);
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "ConvertKeyValueArraysToMessage got an error");
+        return err;
+    }
+
+    // Return negative value when errors happen in addTrack.
+    jint trackIndex = muxer->addTrack(trackformat);
+
+    if (trackIndex < 0) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Failed to add the track to the muxer");
+        return -1;
+    }
+    return trackIndex;
+}
+
+static void android_media_MediaMuxer_writeSampleData(
+        JNIEnv *env, jclass clazz, jint nativeObject, jint trackIndex,
+        jobject byteBuf, jint offset, jint size, jlong timeUs, jint flags) {
+    sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
+    if (muxer == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Muxer was not set up correctly");
+        return;
+    }
+
+    // Try to convert the incoming byteBuffer into ABuffer
+    void *dst = env->GetDirectBufferAddress(byteBuf);
+
+    jlong dstSize;
+    jbyteArray byteArray = NULL;
+
+    if (dst == NULL) {
+
+        byteArray =
+            (jbyteArray)env->CallObjectMethod(byteBuf, gFields.arrayID);
+
+        if (byteArray == NULL) {
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                              "byteArray is null");
+            return;
+        }
+
+        jboolean isCopy;
+        dst = env->GetByteArrayElements(byteArray, &isCopy);
+
+        dstSize = env->GetArrayLength(byteArray);
+    } else {
+        dstSize = env->GetDirectBufferCapacity(byteBuf);
+    }
+
+    if (dstSize < (offset + size)) {
+        ALOGE("writeSampleData saw wrong dstSize %lld, size  %d, offset %d",
+              dstSize, size, offset);
+        if (byteArray != NULL) {
+            env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
+        }
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "sample has a wrong size");
+        return;
+    }
+
+    sp<ABuffer> buffer = new ABuffer((char *)dst + offset, size);
+
+    status_t err = muxer->writeSampleData(buffer, trackIndex, timeUs, flags);
+
+    if (byteArray != NULL) {
+        env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
+    }
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "writeSampleData returned an error");
+    }
+    return;
+}
+
+// Constructor counterpart.
+static jint android_media_MediaMuxer_native_setup(
+        JNIEnv *env, jclass clazz, jobject fileDescriptor,
+        jint format) {
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    ALOGV("native_setup: fd %d", fd);
+
+    MediaMuxer::OutputFormat fileFormat =
+        static_cast<MediaMuxer::OutputFormat>(format);
+    sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
+    muxer->incStrong(clazz);
+    return int(muxer.get());
+}
+
+static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz,
+                                           jint nativeObject) {
+    sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
+    if (muxer == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Muxer was not set up correctly");
+        return;
+    }
+    status_t err = muxer->start();
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Failed to start the muxer");
+        return;
+    }
+
+}
+
+static void android_media_MediaMuxer_stop(JNIEnv *env, jclass clazz,
+                                          jint nativeObject) {
+    sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
+    if (muxer == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Muxer was not set up correctly");
+        return;
+    }
+
+    status_t err = muxer->stop();
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Failed to stop the muxer");
+        return;
+    }
+}
+
+static void android_media_MediaMuxer_native_release(
+        JNIEnv *env, jclass clazz, jint nativeObject) {
+    sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
+    if (muxer != NULL) {
+        muxer->decStrong(clazz);
+    }
+}
+
+static JNINativeMethod gMethods[] = {
+
+    { "nativeAddTrack", "(I[Ljava/lang/String;[Ljava/lang/Object;)I",
+        (void *)android_media_MediaMuxer_addTrack },
+
+    { "nativeStart", "(I)V", (void *)android_media_MediaMuxer_start},
+
+    { "nativeWriteSampleData", "(IILjava/nio/ByteBuffer;IIJI)V",
+        (void *)android_media_MediaMuxer_writeSampleData },
+
+    { "nativeStop", "(I)V", (void *)android_media_MediaMuxer_stop},
+
+    { "nativeSetup", "(Ljava/io/FileDescriptor;I)I",
+        (void *)android_media_MediaMuxer_native_setup },
+
+    { "nativeRelease", "(I)V",
+        (void *)android_media_MediaMuxer_native_release },
+
+};
+
+// This function only registers the native methods, and is called from
+// JNI_OnLoad in android_media_MediaPlayer.cpp
+int register_android_media_MediaMuxer(JNIEnv *env) {
+    int err = AndroidRuntime::registerNativeMethods(env,
+                "android/media/MediaMuxer", gMethods, NELEM(gMethods));
+
+    jclass clazz = env->FindClass("android/media/MediaMuxer");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+    CHECK(gFields.context != NULL);
+
+    jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
+    CHECK(byteBufClass != NULL);
+
+    gFields.arrayID =
+        env->GetMethodID(byteBufClass, "array", "()[B");
+    CHECK(gFields.arrayID != NULL);
+
+    return err;
+}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 8768e96..e9f6a1e 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -883,6 +883,7 @@
 extern int register_android_media_MediaExtractor(JNIEnv *env);
 extern int register_android_media_MediaCodecList(JNIEnv *env);
 extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
+extern int register_android_media_MediaMuxer(JNIEnv *env);
 extern int register_android_media_MediaRecorder(JNIEnv *env);
 extern int register_android_media_MediaScanner(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
@@ -963,6 +964,11 @@
         goto bail;
     }
 
+    if (register_android_media_MediaMuxer(env) < 0) {
+        ALOGE("ERROR: MediaMuxer native registration failed");
+        goto bail;
+    }
+
     if (register_android_media_MediaCodecList(env) < 0) {
         ALOGE("ERROR: MediaCodec native registration failed");
         goto bail;
diff --git a/opengl/java/android/opengl/EGL14.java b/opengl/java/android/opengl/EGL14.java
index d1e2a9ed..2c9508a 100644
--- a/opengl/java/android/opengl/EGL14.java
+++ b/opengl/java/android/opengl/EGL14.java
@@ -447,7 +447,6 @@
 
     // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
 
-    /** @hide -- TODO(fadden) unhide this */
     public static native boolean eglPresentationTimeANDROID(
         EGLDisplay dpy,
         EGLSurface sur,
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
index fc21df3..b8df88e 100644
--- a/packages/SharedStorageBackup/AndroidManifest.xml
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -24,5 +24,12 @@
     <application android:allowClearUserData="false"
                  android:permission="android.permission.CONFIRM_FULL_BACKUP"
                  android:backupAgent="SharedStorageAgent" >
+
+        <service android:name=".ObbBackupService"
+                 android:enabled="true"
+                 android:exported="true">
+        </service>
+
     </application>
+
 </manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
index f43cb81..6a66a47 100644
--- a/packages/SharedStorageBackup/proguard.flags
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -1 +1,2 @@
 -keep class com.android.sharedstoragebackup.SharedStorageAgent
+-keep class com.android.sharedstoragebackup.ObbBackupService
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
new file mode 100644
index 0000000..7ebe096
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
@@ -0,0 +1,151 @@
+/*
+ * 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.sharedstoragebackup;
+
+import android.app.Service;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.content.Intent;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.android.internal.backup.IObbBackupService;
+
+/**
+ * Service that the Backup Manager Services delegates OBB backup/restore operations to,
+ * because those require accessing external storage.
+ *
+ * {@hide}
+ */
+public class ObbBackupService extends Service {
+    static final String TAG = "ObbBackupService";
+    static final boolean DEBUG = true;
+
+    /**
+     * IObbBackupService interface implementation
+     */
+    IObbBackupService mService = new IObbBackupService.Stub() {
+        /*
+         * Back up a package's OBB directory tree
+         */
+        @Override
+        public void backupObbs(String packageName, ParcelFileDescriptor data,
+                int token, IBackupManager callbackBinder) {
+            final FileDescriptor outFd = data.getFileDescriptor();
+            try {
+                File obbDir = Environment.getExternalStorageAppObbDirectory(packageName);
+                if (obbDir != null) {
+                    if (obbDir.exists()) {
+                        ArrayList<File> obbList = allFileContents(obbDir);
+                        if (obbList != null) {
+                            // okay, there's at least something there
+                            if (DEBUG) {
+                                Log.i(TAG, obbList.size() + " files to back up");
+                            }
+                            final String rootPath = obbDir.getCanonicalPath();
+                            final BackupDataOutput out = new BackupDataOutput(outFd);
+                            for (File f : obbList) {
+                                final String filePath = f.getCanonicalPath();
+                                if (DEBUG) {
+                                    Log.i(TAG, "storing: " + filePath);
+                                }
+                                FullBackup.backupToTar(packageName, FullBackup.OBB_TREE_TOKEN, null,
+                                        rootPath, filePath, out);
+                            }
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                Log.w(TAG, "Exception backing up OBBs for " + packageName, e);
+            } finally {
+                // Send the EOD marker indicating that there is no more data
+                try {
+                    FileOutputStream out = new FileOutputStream(outFd);
+                    byte[] buf = new byte[4];
+                    out.write(buf);
+                } catch (IOException e) {
+                    Log.e(TAG, "Unable to finalize obb backup stream!");
+                }
+
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        /*
+         * Restore an OBB file for the given package from the incoming stream
+         */
+        @Override
+        public void restoreObbFile(String packageName, ParcelFileDescriptor data,
+                long fileSize, int type, String path, long mode, long mtime,
+                int token, IBackupManager callbackBinder) {
+            try {
+                File outFile = Environment.getExternalStorageAppObbDirectory(packageName);
+                if (outFile != null) {
+                    outFile = new File(outFile, path);
+                }
+                // outFile is null here if we couldn't get access to external storage,
+                // in which case restoreFile() will discard the data cleanly and let
+                // us proceed with the next file segment in the stream.  We pass -1
+                // for the file mode to suppress attempts to chmod() on shared storage.
+                FullBackup.restoreFile(data, fileSize, type, -1, mtime, outFile);
+            } catch (IOException e) {
+                Log.i(TAG, "Exception restoring OBB " + path, e);
+            } finally {
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        ArrayList<File> allFileContents(File rootDir) {
+            final ArrayList<File> files = new ArrayList<File>();
+            final ArrayList<File> dirs = new ArrayList<File>();
+
+            dirs.add(rootDir);
+            while (!dirs.isEmpty()) {
+                File dir = dirs.remove(0);
+                File[] contents = dir.listFiles();
+                if (contents != null) {
+                    for (File f : contents) {
+                        if (f.isDirectory()) dirs.add(f);
+                        else if (f.isFile()) files.add(f);
+                    }
+                }
+            }
+            return files;
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mService.asBinder();
+    }
+}
diff --git a/packages/SystemUI/res/layout-land/status_bar_help.xml b/packages/SystemUI/res/layout-land/status_bar_help.xml
index 83b9829..a885b86 100644
--- a/packages/SystemUI/res/layout-land/status_bar_help.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_help.xml
@@ -22,8 +22,8 @@
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/status_bar_cling"
-    android:paddingLeft="40dp"
-    android:paddingRight="40dp"
+    android:paddingStart="40dp"
+    android:paddingEnd="40dp"
     android:background="#DD000000"
     android:focusable="true"
     android:orientation="horizontal" 
@@ -34,7 +34,7 @@
         android:layout_width="wrap_content"
         android:layout_weight="0"
         android:layout_height="wrap_content"
-        android:layout_marginRight="50dp"
+        android:layout_marginEnd="50dp"
         android:gravity="center"
         android:src="@drawable/arrow_dashed"
         tools:ignore="ContentDescription" />
@@ -64,8 +64,8 @@
             style="@style/ClingButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingLeft="50dp"
-            android:paddingRight="50dp"
+            android:paddingStart="50dp"
+            android:paddingEnd="50dp"
             android:text="@android:string/ok" />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 47d628b..1257641 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -22,8 +22,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="match_parent"
     android:layout_width="wrap_content"
-    android:paddingLeft="@dimen/status_bar_recents_item_padding"
-    android:paddingRight="@dimen/status_bar_recents_item_padding"
+    android:paddingStart="@dimen/status_bar_recents_item_padding"
+    android:paddingEnd="@dimen/status_bar_recents_item_padding"
     android:importantForAccessibility="no"
     android:clipChildren="false">
 
@@ -72,7 +72,7 @@
             android:fadingEdge="horizontal"
             android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
             android:scrollHorizontally="true"
-            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_alignStart="@id/app_thumbnail"
             android:layout_below="@id/app_thumbnail"
             android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
             android:layout_marginStart="@dimen/status_bar_recents_app_label_left_margin"
diff --git a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
index 96b0a1f..a109191 100644
--- a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
@@ -36,7 +36,7 @@
             android:id="@+id/search_panel_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_alignParentRight="true">
+            android:layout_alignParentEnd="true">
 
             <com.android.internal.widget.multiwaveview.GlowPadView
                 android:id="@+id/glow_pad_view"
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 0bac993..47db1c7 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -54,7 +54,7 @@
                 android:layout_weight="1"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
                 systemui:keyCode="4"
@@ -63,7 +63,7 @@
                 android:contentDescription="@string/accessibility_back"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
                 systemui:keyCode="3"
@@ -73,7 +73,7 @@
                 android:contentDescription="@string/accessibility_home"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
@@ -112,7 +112,7 @@
                 android:layout_weight="1"
                 />
             <ImageView
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:layout_marginStart="40dp"
                 android:src="@drawable/ic_sysbar_lights_out_dot_small"
@@ -120,14 +120,14 @@
                 android:layout_weight="0"
                 />
             <ImageView
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_lights_out_dot_large"
                 android:scaleType="center"
                 android:layout_weight="0"
                 />
             <ImageView
-                android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"
+                android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_marginEnd="40dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_lights_out_dot_small"
@@ -195,7 +195,7 @@
                 android:layout_weight="1"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
                 systemui:keyCode="4"
@@ -204,7 +204,7 @@
                 android:contentDescription="@string/accessibility_back"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
                 systemui:keyCode="3"
@@ -214,7 +214,7 @@
                 android:contentDescription="@string/accessibility_home"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
@@ -253,7 +253,7 @@
                 android:layout_weight="1"
                 />
             <ImageView
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:layout_marginStart="40dp"
                 android:src="@drawable/ic_sysbar_lights_out_dot_small"
@@ -261,14 +261,14 @@
                 android:layout_weight="0"
                 />
             <ImageView
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_lights_out_dot_large"
                 android:scaleType="center"
                 android:layout_weight="0"
                 />
             <ImageView
-                android:layout_width="162dp" android:paddingLeft="42dp" android:paddingRight="42dp"
+                android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_marginEnd="40dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_lights_out_dot_small"
diff --git a/packages/SystemUI/res/layout/compat_mode_help.xml b/packages/SystemUI/res/layout/compat_mode_help.xml
index c2ed78e..566d07d 100644
--- a/packages/SystemUI/res/layout/compat_mode_help.xml
+++ b/packages/SystemUI/res/layout/compat_mode_help.xml
@@ -52,7 +52,7 @@
         android:background="@drawable/compat_mode_help_divider_bottom"
         android:layout_marginBottom="55dp"
         android:layout_marginEnd="80dp"
-        android:layout_alignLeft="@id/header"
+        android:layout_alignStart="@id/header"
         android:layout_alignParentBottom="true"
         >
         <ImageView
@@ -82,7 +82,7 @@
         android:id="@+id/button"
         android:layout_width="208dp"
         android:layout_height="48dp"
-        android:layout_alignLeft="@id/header"
+        android:layout_alignStart="@id/header"
         android:layout_alignParentBottom="true"
         android:layout_marginBottom="20dp"
         android:textSize="28sp"
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index b547d99..a6f4e9b 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -24,7 +24,7 @@
 	    android:layout_width="wrap_content"
 	    android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:paddingRight="10dp"
+        android:paddingEnd="10dp"
         android:src="@drawable/ic_qs_brightness_auto_off"
         />
     <com.android.systemui.settings.ToggleSlider
@@ -32,7 +32,7 @@
         android:layout_width="0dp"
         android:layout_height="40dp"
         android:layout_weight="1"
-        android:layout_marginRight="2dp"
+        android:layout_marginEnd="2dp"
         android:layout_gravity="center_vertical"
         systemui:text="@string/status_bar_settings_auto_brightness_label"
         />
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8805175..b27536d 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -34,7 +34,7 @@
         android:id="@+id/notification_lights_out"
         android:layout_width="@dimen/status_bar_icon_size"
         android:layout_height="match_parent"
-        android:paddingLeft="6dip"
+        android:paddingStart="6dip"
         android:paddingBottom="2dip"
         android:src="@drawable/ic_sysbar_lights_out_dot_small"
         android:scaleType="center"
@@ -44,8 +44,8 @@
     <LinearLayout android:id="@+id/status_bar_contents"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="6dip"
-        android:paddingRight="6dip"
+        android:paddingStart="6dip"
+        android:paddingEnd="6dip"
         android:orientation="horizontal"
         >
 
@@ -86,7 +86,7 @@
                 android:id="@+id/signal_battery_cluster"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:paddingLeft="2dp"
+                android:paddingStart="2dp"
                 android:orientation="horizontal"
                 android:gravity="center"
                 >
@@ -99,7 +99,7 @@
                     android:id="@+id/battery"
                     android:layout_height="wrap_content"
                     android:layout_width="wrap_content"
-                    android:paddingLeft="4dip"
+                    android:paddingStart="4dip"
                     />
             </LinearLayout>
     
@@ -109,7 +109,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:singleLine="true"
-                android:paddingLeft="6dip"
+                android:paddingStart="6dip"
                 android:gravity="center_vertical|start"
                 />
         </LinearLayout>
@@ -118,7 +118,7 @@
     <LinearLayout android:id="@+id/ticker"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="6dip"
+        android:paddingStart="6dip"
         android:animationCache="false"
         android:orientation="horizontal" >
         <ImageSwitcher android:id="@+id/tickerIcon"
@@ -142,7 +142,7 @@
             android:layout_weight="1"
             android:layout_height="wrap_content"
             android:paddingTop="2dip"
-            android:paddingRight="10dip">
+            android:paddingEnd="10dip">
             <TextView
                 android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 54c63f8..9aa7cfd 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -30,8 +30,8 @@
         android:id="@+id/datetime"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
         android:background="@drawable/ic_notify_button_bg"
         android:enabled="false"
         >
diff --git a/packages/SystemUI/res/layout/status_bar_help.xml b/packages/SystemUI/res/layout/status_bar_help.xml
index 3c004ee..f638767 100644
--- a/packages/SystemUI/res/layout/status_bar_help.xml
+++ b/packages/SystemUI/res/layout/status_bar_help.xml
@@ -22,8 +22,8 @@
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/status_bar_cling"
-    android:paddingLeft="40dp"
-    android:paddingRight="40dp"
+    android:paddingStart="40dp"
+    android:paddingEnd="40dp"
     android:background="#DD000000"
     android:focusable="true"
     android:orientation="vertical" 
@@ -56,8 +56,8 @@
         style="@style/ClingButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingLeft="50dp"
-        android:paddingRight="50dp"
+        android:paddingStart="50dp"
+        android:paddingEnd="50dp"
         android:text="@android:string/ok" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2a93a2b..7a5ff3c 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -20,8 +20,8 @@
         android:gravity="end"
         android:layout_marginEnd="-80dp"
         android:background="@null"
-        android:paddingRight="8dp"
-        android:paddingLeft="8dp"
+        android:paddingEnd="8dp"
+        android:paddingStart="8dp"
         />
 
     <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index e3cd704..834cc2c 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -36,15 +36,15 @@
         android:layout_toEndOf="@id/toggle"
         android:layout_centerVertical="true"
         android:layout_alignParentEnd="true"
-        android:paddingLeft="20dp"
-        android:paddingRight="20dp"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
         />
     <TextView
         android:id="@+id/label"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/toggle"
-        android:layout_alignRight="@id/toggle"
+        android:layout_alignStart="@id/toggle"
+        android:layout_alignEnd="@id/toggle"
         android:layout_centerVertical="true"
         android:gravity="center"
         android:paddingTop="26dp"
diff --git a/packages/SystemUI/res/layout/system_bar.xml b/packages/SystemUI/res/layout/system_bar.xml
index ac62702..28c9dc0 100644
--- a/packages/SystemUI/res/layout/system_bar.xml
+++ b/packages/SystemUI/res/layout/system_bar.xml
@@ -91,8 +91,8 @@
             <com.android.systemui.statusbar.policy.EventHole android:id="@+id/fake_space_bar"
                 android:layout_height="match_parent"
                 android:layout_width="0dp"
-                android:paddingLeft="8dip"
-                android:paddingRight="8dip"
+                android:paddingStart="8dip"
+                android:paddingEnd="8dip"
                 android:layout_toEndOf="@+id/navigationArea"
                 android:layout_toStartOf="@+id/notificationArea"
                 android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml b/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml
index a33741e..9ad9e05 100644
--- a/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml
+++ b/packages/SystemUI/res/layout/system_bar_compat_mode_panel.xml
@@ -22,7 +22,7 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:paddingBottom="@dimen/panel_float"
-    android:paddingRight="20dp"
+    android:paddingEnd="20dp"
     >
     <RadioGroup android:id="@+id/compat_mode_radio_group"
         android:background="@*android:drawable/dialog_full_holo_dark"
diff --git a/packages/SystemUI/res/layout/system_bar_input_methods_item.xml b/packages/SystemUI/res/layout/system_bar_input_methods_item.xml
index 710406c..1a95ec1 100644
--- a/packages/SystemUI/res/layout/system_bar_input_methods_item.xml
+++ b/packages/SystemUI/res/layout/system_bar_input_methods_item.xml
@@ -24,8 +24,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:background="@drawable/status_bar_item_background"
     android:orientation="vertical"
-    android:paddingRight="6dip"
-    android:paddingLeft="6dip"
+    android:paddingEnd="6dip"
+    android:paddingStart="6dip"
     android:paddingTop="5dip"
     android:paddingBottom="5dip"
     android:gravity="center_vertical">
@@ -89,8 +89,8 @@
             android:layout_height="match_parent"
             android:layout_marginStart="5dip"
             android:layout_gravity="center_vertical"
-            android:paddingRight="10dip"
-            android:paddingLeft="10dip"
+            android:paddingEnd="10dip"
+            android:paddingStart="10dip"
             android:src="@drawable/ic_sysbar_quicksettings"
             android:visibility="visible"
             android:clickable="true"
diff --git a/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml b/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml
index ecc4f1e..547f937 100644
--- a/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml
+++ b/packages/SystemUI/res/layout/system_bar_input_methods_panel.xml
@@ -57,8 +57,8 @@
                         android:minHeight="?android:attr/listPreferredItemHeight"
                         android:background="?android:attr/selectableItemBackground"
                         android:orientation="vertical"
-                        android:paddingRight="6dip"
-                        android:paddingLeft="30dip"
+                        android:paddingEnd="6dip"
+                        android:paddingStart="30dip"
                         android:paddingTop="5dip"
                         android:paddingBottom="5dip"
                         android:gravity="center_vertical"
@@ -103,8 +103,8 @@
                 android:minHeight="?android:attr/listPreferredItemHeight"
                 android:background="?android:attr/selectableItemBackground"
                 android:orientation="vertical"
-                android:paddingRight="6dip"
-                android:paddingLeft="30dip"
+                android:paddingEnd="6dip"
+                android:paddingStart="30dip"
                 android:paddingTop="5dip"
                 android:paddingBottom="5dip"
                 android:gravity="center_vertical"
diff --git a/packages/SystemUI/res/layout/system_bar_notification_area.xml b/packages/SystemUI/res/layout/system_bar_notification_area.xml
index 51ffda7..2fd91ef 100644
--- a/packages/SystemUI/res/layout/system_bar_notification_area.xml
+++ b/packages/SystemUI/res/layout/system_bar_notification_area.xml
@@ -84,7 +84,7 @@
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:singleLine="true"
-            android:paddingLeft="6dip"
+            android:paddingStart="6dip"
             android:layout_marginEnd="8dip"
             android:gravity="center_vertical|start"
             />
@@ -119,14 +119,14 @@
                 android:id="@+id/bluetooth"
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
-                android:paddingLeft="4dip"
+                android:paddingStart="4dip"
                 android:visibility="gone"
                 />
             <ImageView
                 android:id="@+id/battery"
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
-                android:paddingLeft="4dip"
+                android:paddingStart="4dip"
                 />
         </LinearLayout>
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
index 97f774a..d08fbce 100644
--- a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
+++ b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
@@ -23,9 +23,9 @@
     android:layout_height="wrap_content"
     android:clickable="true"
     android:orientation="vertical"
-    android:paddingLeft="26dp"
+    android:paddingStart="26dp"
     android:paddingTop="14dp"
-    android:paddingRight="26dp"
+    android:paddingEnd="26dp"
     >
 
     <TableLayout
@@ -52,7 +52,7 @@
                     android:id="@+id/bluetooth"
                     android:layout_height="wrap_content"
                     android:layout_width="wrap_content"
-                    android:paddingRight="16dp"
+                    android:paddingEnd="16dp"
                     android:visibility="gone"
                     android:contentDescription="@null"
                     android:layout_gravity="center_vertical"
@@ -65,7 +65,7 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:layout_gravity="center_vertical"
-                android:paddingRight="6dp"
+                android:paddingEnd="6dp"
                 >
 
                 <ImageView
@@ -89,7 +89,7 @@
                 android:layout_gravity="start|center_vertical"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingRight="12dp"
+                android:paddingEnd="12dp"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:text="@string/status_bar_settings_settings_button"
@@ -101,7 +101,7 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:layout_gravity="center_vertical"
-                android:paddingRight="6dp"
+                android:paddingEnd="6dp"
                 >
 
                 <ImageView
@@ -125,7 +125,7 @@
                 android:layout_gravity="start|center_vertical"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingRight="12dp"
+                android:paddingEnd="12dp"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:text="@string/status_bar_settings_settings_button"
@@ -138,7 +138,7 @@
                 android:scaleType="centerInside"
                 android:layout_gravity="center_vertical"
                 android:layout_alignBaseline="@id/wifi_signal"
-                android:paddingRight="6dp"
+                android:paddingEnd="6dp"
                 android:contentDescription="@null"
                 />
 
@@ -148,7 +148,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="start|center_vertical"
-                android:paddingRight="2dp"
+                android:paddingEnd="2dp"
                 android:singleLine="true"
                 android:text="@string/status_bar_settings_settings_button"
                 />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f0b1d7e..9f54573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -407,6 +407,7 @@
         mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
         mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
         mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
+        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
         mNotificationIcons.setOverflowIndicator(mMoreIcon);
         mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
         mTickerView = mStatusBarView.findViewById(R.id.ticker);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index ffa88d5..fda47d5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -250,6 +250,15 @@
     }
 
     @Override
+    protected void onSizeChanged (int w, int h, int oldw, int oldh) {
+        if (mAttached) {
+            int dim = Math.min(512, Math.max(w, h));
+            if (DEBUG) Log.v(TAG, "TCV uses bitmap size=" + dim);
+            mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
+        }
+    }
+
+    @Override
     public void onDetachedFromWindow() {
         if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
         super.onDetachedFromWindow();
diff --git a/preloaded-classes b/preloaded-classes
index 10c5c9e..126dd15 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -199,16 +199,14 @@
 android.app.backup.FileBackupHelperBase
 android.app.backup.FullBackup
 android.appwidget.AppWidgetManager
-android.bluetooth.BluetoothAudioGateway
 android.bluetooth.BluetoothSocket
-android.bluetooth.HeadsetBase
 android.bluetooth.IBluetooth
 android.bluetooth.IBluetooth$Stub
 android.bluetooth.IBluetoothA2dp
 android.bluetooth.IBluetoothA2dp$Stub
 android.content.AbstractThreadedSyncAdapter
 android.content.AbstractThreadedSyncAdapter$ISyncAdapterImpl
-undroid.content.AbstractThreadedSyncAdapter$SyncThread
+android.content.AbstractThreadedSyncAdapter$SyncThread
 android.content.BroadcastReceiver
 android.content.BroadcastReceiver$PendingResult
 android.content.ComponentCallbacks
@@ -737,9 +735,6 @@
 android.provider.Settings$Secure
 android.provider.Settings$System
 android.renderscript.RenderScript
-android.server.BluetoothA2dpService
-android.server.BluetoothEventLoop
-android.server.BluetoothService
 android.telephony.PhoneNumberUtils
 android.telephony.TelephonyManager
 android.text.AndroidBidi
@@ -844,7 +839,6 @@
 android.util.FinitePool
 android.util.FloatMath
 android.util.FloatProperty
-android.util.LocaleUtil
 android.util.Log
 android.util.Log$1
 android.util.Log$TerribleFailureHandler
@@ -1022,7 +1016,6 @@
 android.view.ViewRootImpl$InputMethodCallback
 android.view.ViewRootImpl$InvalidateOnAnimationRunnable
 android.view.ViewRootImpl$QueuedInputEvent
-android.view.ViewRootImpl$ResizedInfo
 android.view.ViewRootImpl$RunQueue
 android.view.ViewRootImpl$RunQueue$HandlerAction
 android.view.ViewRootImpl$TrackballAxis
@@ -1042,7 +1035,6 @@
 android.view.ViewTreeObserver$OnTouchModeChangeListener
 android.view.Window
 android.view.Window$Callback
-android.view.Window$LocalWindowManager
 android.view.WindowLeaked
 android.view.WindowManager
 android.view.WindowManager$LayoutParams
@@ -1050,7 +1042,6 @@
 android.view.WindowManagerGlobal
 android.view.WindowManagerGlobal$1
 android.view.WindowManagerImpl
-android.view.WindowManagerImpl$CompatModeWrapper
 android.view.accessibility.AccessibilityEvent
 android.view.accessibility.AccessibilityEventSource
 android.view.accessibility.AccessibilityManager
@@ -1843,7 +1834,6 @@
 java.text.DateFormat
 java.text.DateFormatSymbols
 java.text.DecimalFormat
-java.text.DecimalFormat$1
 java.text.DecimalFormatSymbols
 java.text.FieldPosition
 java.text.Format
@@ -1995,7 +1985,6 @@
 java.util.concurrent.Future
 java.util.concurrent.FutureTask
 java.util.concurrent.FutureTask$WaitNode
-java.util.concurrent.FutureTask$Sync
 java.util.concurrent.LinkedBlockingQueue
 java.util.concurrent.LinkedBlockingQueue$Node
 java.util.concurrent.RejectedExecutionHandler
@@ -2015,7 +2004,6 @@
 java.util.concurrent.atomic.AtomicBoolean
 java.util.concurrent.atomic.AtomicInteger
 java.util.concurrent.atomic.AtomicReference
-java.util.concurrent.atomic.UnsafeAccess
 java.util.concurrent.locks.AbstractOwnableSynchronizer
 java.util.concurrent.locks.AbstractQueuedSynchronizer
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject
@@ -2034,7 +2022,6 @@
 java.util.concurrent.locks.ReentrantReadWriteLock$Sync
 java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter
 java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock
-java.util.concurrent.locks.UnsafeAccess
 java.util.jar.Attributes
 java.util.jar.Attributes$Name
 java.util.jar.InitManifest
@@ -2111,7 +2098,6 @@
 javax.net.ssl.X509KeyManager
 javax.net.ssl.X509TrustManager
 javax.security.auth.x500.X500Principal
-libcore.icu.ErrorCode
 libcore.icu.ICU
 libcore.icu.LocaleData
 libcore.icu.NativeBreakIterator
@@ -2122,7 +2108,7 @@
 libcore.icu.NativeIDN
 libcore.icu.NativeNormalizer
 libcore.icu.NativePluralRules
-libcore.icu.TimeZones
+libcore.icu.TimeZoneNames
 libcore.internal.StringPool
 libcore.io.AsynchronousCloseMonitor
 libcore.io.Base64
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index bc8df18..43d76bb 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2788,6 +2788,8 @@
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
         } else if (deviceTypeString == "touchPad") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        } else if (deviceTypeString == "touchNavigation") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
         } else if (deviceTypeString == "pointer") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
         } else if (deviceTypeString != "default") {
@@ -2832,6 +2834,9 @@
     case Parameters::DEVICE_TYPE_TOUCH_PAD:
         dump.append(INDENT4 "DeviceType: touchPad\n");
         break;
+    case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+        dump.append(INDENT4 "DeviceType: touchNavigation\n");
+        break;
     case Parameters::DEVICE_TYPE_POINTER:
         dump.append(INDENT4 "DeviceType: pointer\n");
         break;
@@ -2885,6 +2890,9 @@
         if (hasStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
         }
+    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+        mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+        mDeviceMode = DEVICE_MODE_UNSCALED;
     } else {
         mSource = AINPUT_SOURCE_TOUCHPAD;
         mDeviceMode = DEVICE_MODE_UNSCALED;
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 61b21e2..c596b37 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1192,6 +1192,7 @@
         enum DeviceType {
             DEVICE_TYPE_TOUCH_SCREEN,
             DEVICE_TYPE_TOUCH_PAD,
+            DEVICE_TYPE_TOUCH_NAVIGATION,
             DEVICE_TYPE_POINTER,
         };
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 401a25f..328b503 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -78,6 +78,7 @@
 
 import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.backup.IObbBackupService;
 import com.android.internal.backup.LocalTransport;
 import com.android.server.PackageManagerBackupAgent.Metadata;
 
@@ -363,15 +364,17 @@
 
     class FullBackupParams extends FullParams {
         public boolean includeApks;
+        public boolean includeObbs;
         public boolean includeShared;
         public boolean allApps;
         public boolean includeSystem;
         public String[] packages;
 
-        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
-                boolean doAllApps, boolean doSystem, String[] pkgList) {
+        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
+                boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) {
             fd = output;
             includeApks = saveApks;
+            includeObbs = saveObbs;
             includeShared = saveShared;
             allApps = doAllApps;
             includeSystem = doSystem;
@@ -550,7 +553,7 @@
                 // similar to normal backup/restore.
                 FullBackupParams params = (FullBackupParams)msg.obj;
                 PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
-                        params.observer, params.includeApks,
+                        params.observer, params.includeApks, params.includeObbs,
                         params.includeShared, params.curPassword, params.encryptPassword,
                         params.allApps, params.includeSystem, params.packages, params.latch);
                 (new Thread(task)).start();
@@ -2306,13 +2309,132 @@
     }
 
 
-    // ----- Full backup to a file/socket -----
+    // ----- Full backup/restore to a file/socket -----
 
-    class PerformFullBackupTask implements Runnable {
+    abstract class ObbServiceClient {
+        public IObbBackupService mObbService;
+        public void setObbBinder(IObbBackupService binder) {
+            mObbService = binder;
+        }
+    }
+
+    class FullBackupObbConnection implements ServiceConnection {
+        volatile IObbBackupService mService;
+
+        FullBackupObbConnection() {
+            mService = null;
+        }
+
+        public void establish() {
+            if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
+            Intent obbIntent = new Intent().setComponent(new ComponentName(
+                    "com.android.sharedstoragebackup",
+                    "com.android.sharedstoragebackup.ObbBackupService"));
+            BackupManagerService.this.mContext.bindService(
+                    obbIntent, this, Context.BIND_AUTO_CREATE);
+        }
+
+        public void tearDown() {
+            BackupManagerService.this.mContext.unbindService(this);
+        }
+
+        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
+            boolean success = false;
+            waitForConnection();
+
+            ParcelFileDescriptor[] pipes = null;
+            try {
+                pipes = ParcelFileDescriptor.createPipe();
+                int token = generateToken();
+                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
+                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
+                routeSocketDataToOutput(pipes[0], out);
+                success = waitUntilOperationComplete(token);
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
+            } finally {
+                try {
+                    out.flush();
+                    if (pipes != null) {
+                        if (pipes[0] != null) pipes[0].close();
+                        if (pipes[1] != null) pipes[1].close();
+                    }
+                } catch (IOException e) {
+                    Slog.w(TAG, "I/O error closing down OBB backup", e);
+                }
+            }
+            return success;
+        }
+
+        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
+                long fileSize, int type, String path, long mode, long mtime,
+                int token, IBackupManager callbackBinder) {
+            waitForConnection();
+
+            try {
+                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
+                        token, callbackBinder);
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
+            }
+        }
+
+        private void waitForConnection() {
+            synchronized (this) {
+                while (mService == null) {
+                    if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
+                    try {
+                        this.wait();
+                    } catch (InterruptedException e) { /* never interrupted */ }
+                }
+                if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
+            }
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (this) {
+                mService = IObbBackupService.Stub.asInterface(service);
+                if (DEBUG) Slog.i(TAG, "OBB service connection " + mService
+                        + " connected on " + this);
+                this.notifyAll();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (this) {
+                mService = null;
+                if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
+                this.notifyAll();
+            }
+        }
+        
+    }
+
+    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
+            throws IOException {
+        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
+        DataInputStream in = new DataInputStream(raw);
+
+        byte[] buffer = new byte[32 * 1024];
+        int chunkTotal;
+        while ((chunkTotal = in.readInt()) > 0) {
+            while (chunkTotal > 0) {
+                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
+                int nRead = in.read(buffer, 0, toRead);
+                out.write(buffer, 0, nRead);
+                chunkTotal -= nRead;
+            }
+        }
+    }
+
+    class PerformFullBackupTask extends ObbServiceClient implements Runnable {
         ParcelFileDescriptor mOutputFile;
         DeflaterOutputStream mDeflater;
         IFullBackupRestoreObserver mObserver;
         boolean mIncludeApks;
+        boolean mIncludeObbs;
         boolean mIncludeShared;
         boolean mAllApps;
         final boolean mIncludeSystem;
@@ -2322,6 +2444,7 @@
         AtomicBoolean mLatchObject;
         File mFilesDir;
         File mManifestFile;
+        
 
         class FullBackupRunner implements Runnable {
             PackageInfo mPackage;
@@ -2377,12 +2500,13 @@
         }
 
         PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 
-                boolean includeApks, boolean includeShared, String curPassword,
-                String encryptPassword, boolean doAllApps, boolean doSystem, String[] packages,
-                AtomicBoolean latch) {
+                boolean includeApks, boolean includeObbs, boolean includeShared,
+                String curPassword, String encryptPassword, boolean doAllApps,
+                boolean doSystem, String[] packages, AtomicBoolean latch) {
             mOutputFile = fd;
             mObserver = observer;
             mIncludeApks = includeApks;
+            mIncludeObbs = includeObbs;
             mIncludeShared = includeShared;
             mAllApps = doAllApps;
             mIncludeSystem = doSystem;
@@ -2405,9 +2529,12 @@
 
         @Override
         public void run() {
-            List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
-
             Slog.i(TAG, "--- Performing full-dataset backup ---");
+
+            List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
+            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
+            obbConnection.establish();  // we'll want this later
+
             sendStartBackup();
 
             // doAllApps supersedes the package set if any
@@ -2557,6 +2684,15 @@
                 for (int i = 0; i < N; i++) {
                     pkg = packagesToBackup.get(i);
                     backupOnePackage(pkg, out);
+
+                    // after the app's agent runs to handle its private filesystem
+                    // contents, back up any OBB content it has on its behalf.
+                    if (mIncludeObbs) {
+                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
+                        if (!obbOkay) {
+                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
+                        }
+                    }
                 }
 
                 // Done!
@@ -2581,6 +2717,7 @@
                     mLatchObject.notifyAll();
                 }
                 sendEndBackup();
+                obbConnection.tearDown();
                 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
                 mWakelock.release();
             }
@@ -2688,20 +2825,7 @@
 
                     // Now pull data from the app and stuff it into the compressor
                     try {
-                        FileInputStream raw = new FileInputStream(pipes[0].getFileDescriptor());
-                        DataInputStream in = new DataInputStream(raw);
-
-                        byte[] buffer = new byte[16 * 1024];
-                        int chunkTotal;
-                        while ((chunkTotal = in.readInt()) > 0) {
-                            while (chunkTotal > 0) {
-                                int toRead = (chunkTotal > buffer.length)
-                                        ? buffer.length : chunkTotal;
-                                int nRead = in.read(buffer, 0, toRead);
-                                out.write(buffer, 0, nRead);
-                                chunkTotal -= nRead;
-                            }
-                        }
+                        routeSocketDataToOutput(pipes[0], out);
                     } catch (IOException e) {
                         Slog.i(TAG, "Caught exception reading from agent", e);
                     }
@@ -2900,7 +3024,7 @@
         ACCEPT_IF_APK
     }
 
-    class PerformFullRestoreTask implements Runnable {
+    class PerformFullRestoreTask extends ObbServiceClient implements Runnable {
         ParcelFileDescriptor mInputFile;
         String mCurrentPassword;
         String mDecryptPassword;
@@ -2909,6 +3033,7 @@
         IBackupAgent mAgent;
         String mAgentPackage;
         ApplicationInfo mTargetApp;
+        FullBackupObbConnection mObbConnection = null;
         ParcelFileDescriptor[] mPipes = null;
 
         long mBytes;
@@ -2937,6 +3062,7 @@
             mAgent = null;
             mAgentPackage = null;
             mTargetApp = null;
+            mObbConnection = new FullBackupObbConnection();
 
             // Which packages we've already wiped data on.  We prepopulate this
             // with a whitelist of packages known to be unclearable.
@@ -2980,6 +3106,7 @@
         @Override
         public void run() {
             Slog.i(TAG, "--- Performing full-dataset restore ---");
+            mObbConnection.establish();
             sendStartRestore();
 
             // Are we able to restore shared-storage data?
@@ -3067,6 +3194,7 @@
                     mLatchObject.set(true);
                     mLatchObject.notifyAll();
                 }
+                mObbConnection.tearDown();
                 sendEndRestore();
                 Slog.d(TAG, "Full restore pass complete.");
                 mWakelock.release();
@@ -3319,22 +3447,30 @@
                             long toCopy = info.size;
                             final int token = generateToken();
                             try {
-                                if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
-                                        + info.path);
                                 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
-                                // fire up the app's agent listening on the socket.  If
-                                // the agent is running in the system process we can't
-                                // just invoke it asynchronously, so we provide a thread
-                                // for it here.
-                                if (mTargetApp.processName.equals("system")) {
-                                    Slog.d(TAG, "system process agent - spinning a thread");
-                                    RestoreFileRunnable runner = new RestoreFileRunnable(
-                                            mAgent, info, mPipes[0], token);
-                                    new Thread(runner).start();
+                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
+                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
+                                            + " : " + info.path);
+                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
+                                            info.size, info.type, info.path, info.mode,
+                                            info.mtime, token, mBackupManagerBinder);
                                 } else {
-                                    mAgent.doRestoreFile(mPipes[0], info.size, info.type,
-                                            info.domain, info.path, info.mode, info.mtime,
-                                            token, mBackupManagerBinder);
+                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
+                                            + info.path);
+                                    // fire up the app's agent listening on the socket.  If
+                                    // the agent is running in the system process we can't
+                                    // just invoke it asynchronously, so we provide a thread
+                                    // for it here.
+                                    if (mTargetApp.processName.equals("system")) {
+                                        Slog.d(TAG, "system process agent - spinning a thread");
+                                        RestoreFileRunnable runner = new RestoreFileRunnable(
+                                                mAgent, info, mPipes[0], token);
+                                        new Thread(runner).start();
+                                    } else {
+                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
+                                                info.domain, info.path, info.mode, info.mtime,
+                                                token, mBackupManagerBinder);
+                                    }
                                 }
                             } catch (IOException e) {
                                 // couldn't dup the socket for a process-local restore
@@ -3342,7 +3478,7 @@
                                 agentSuccess = false;
                                 okay = false;
                             } catch (RemoteException e) {
-                                // whoops, remote agent went away.  We'll eat the content
+                                // whoops, remote entity went away.  We'll eat the content
                                 // ourselves, then, and not copy it over.
                                 Slog.e(TAG, "Agent crashed during full restore");
                                 agentSuccess = false;
@@ -3891,18 +4027,6 @@
                             slash = info.path.indexOf('/');
                             if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
                             info.domain = info.path.substring(0, slash);
-                            // validate that it's one of the domains we understand
-                            if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
-                                    && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
-                                throw new IOException("Unrecognized domain " + info.domain);
-                            }
-
                             info.path = info.path.substring(slash + 1);
                         }
                     }
@@ -4989,7 +5113,8 @@
     // Run a *full* backup pass for the given package, writing the resulting data stream
     // to the supplied file descriptor.  This method is synchronous and does not return
     // to the caller until the backup has been completed.
-    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
+            boolean includeObbs, boolean includeShared,
             boolean doAllApps, boolean includeSystem, String[] pkgList) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
 
@@ -5020,12 +5145,12 @@
             }
 
             if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
-                    + " shared=" + includeShared + " all=" + doAllApps
+                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
                     + " pkgs=" + pkgList);
             Slog.i(TAG, "Beginning full backup...");
 
-            FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
-                    doAllApps, includeSystem, pkgList);
+            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
+                    includeShared, doAllApps, includeSystem, pkgList);
             final int token = generateToken();
             synchronized (mFullConfirmations) {
                 mFullConfirmations.put(token, params);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 3257d2c..c83a919 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1413,11 +1413,11 @@
     }
 
     private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
-        return modifyRoute(p.getInterfaceName(), p, r, 0, ADD, toDefaultTable);
+        return modifyRoute(p, r, 0, ADD, toDefaultTable);
     }
 
     private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
-        return modifyRoute(p.getInterfaceName(), p, r, 0, REMOVE, toDefaultTable);
+        return modifyRoute(p, r, 0, REMOVE, toDefaultTable);
     }
 
     private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
@@ -1430,24 +1430,26 @@
 
     private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
             boolean toDefaultTable) {
+        String iface = lp.getInterfaceName();
         RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
         if (bestRoute == null) {
-            bestRoute = RouteInfo.makeHostRoute(addr);
+            bestRoute = RouteInfo.makeHostRoute(addr, iface);
         } else {
             if (bestRoute.getGateway().equals(addr)) {
                 // if there is no better route, add the implied hostroute for our gateway
-                bestRoute = RouteInfo.makeHostRoute(addr);
+                bestRoute = RouteInfo.makeHostRoute(addr, iface);
             } else {
                 // if we will connect to this through another route, add a direct route
                 // to it's gateway
-                bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway());
+                bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
             }
         }
-        return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd, toDefaultTable);
+        return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable);
     }
 
-    private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
-            boolean doAdd, boolean toDefaultTable) {
+    private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
+            boolean toDefaultTable) {
+        String ifaceName = lp.getInterfaceName();
         if ((ifaceName == null) || (lp == null) || (r == null)) {
             if (DBG) log("modifyRoute got unexpected null: " + ifaceName + ", " + lp + ", " + r);
             return false;
@@ -1463,13 +1465,15 @@
             if (bestRoute != null) {
                 if (bestRoute.getGateway().equals(r.getGateway())) {
                     // if there is no better route, add the implied hostroute for our gateway
-                    bestRoute = RouteInfo.makeHostRoute(r.getGateway());
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway(), ifaceName);
                 } else {
                     // if we will connect to our gateway through another route, add a direct
                     // route to it's gateway
-                    bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway());
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway(),
+                                                        bestRoute.getGateway(),
+                                                        ifaceName);
                 }
-                modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd, toDefaultTable);
+                modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable);
             }
         }
         if (doAdd) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 6ba5cff..01f7544 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1243,6 +1243,10 @@
                     unbindCurrentMethodLocked(false, false);
                     return;
                 }
+                // Remove commands relating to the previous service. Otherwise WindowManagerService
+                // will reject the command because the token attached to these messages is invalid.
+                mCaller.removeMessages(MSG_SHOW_SOFT_INPUT);
+                mCaller.removeMessages(MSG_HIDE_SOFT_INPUT);
                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                         MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 9f2685b..9e036d1 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -186,8 +186,14 @@
             this.userid = userid;
         }
 
+        boolean userMatches(StatusBarNotification sbn) {
+            if (this.userid == UserHandle.USER_ALL) return true;
+            int nid = sbn.getUserId();
+            return (nid == UserHandle.USER_ALL || nid == this.userid);
+        }
+
         public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
-            if (this.userid != sbn.getUserId()) return;
+            if (!userMatches(sbn)) return;
             try {
                 listener.onNotificationPosted(sbn);
             } catch (RemoteException ex) {
@@ -196,7 +202,7 @@
         }
 
         public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
-            if (this.userid != sbn.getUserId()) return;
+            if (!userMatches(sbn)) return;
             try {
                 listener.onNotificationRemoved(sbn);
             } catch (RemoteException ex) {
@@ -1249,9 +1255,6 @@
                     sendAccessibilityEvent(notification, pkg);
                 }
 
-                // finally, keep some of this information around for later use
-                mArchive.record(n);
-
                 notifyPostedLocked(r);
             } else {
                 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
@@ -1466,6 +1469,9 @@
         if (mLedNotification == r) {
             mLedNotification = null;
         }
+
+        // Save it for users of getHistoricalNotifications()
+        mArchive.record(r.sbn);
     }
 
     /**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d8bcf2cd..6f092bf 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3499,7 +3499,14 @@
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
-                    Slog.w(TAG, "Invalid packageName:" + packageName);
+                    Slog.w(TAG, "Invalid packageName: " + packageName);
+                    if (observer != null) {
+                        try {
+                            observer.onRemoveCompleted(packageName, false);
+                        } catch (RemoteException e) {
+                            Slog.i(TAG, "Observer no longer exists.");
+                        }
+                    }
                     return false;
                 }
                 if (uid == pkgUid || checkComponentPermission(
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 4ae013b..f955f4f 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -62,13 +62,17 @@
     private static final String MOBILE_IFACE = "rmnet3";
     private static final String WIFI_IFACE = "wlan6";
 
-    private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"));
-    private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"));
+    private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"),
+                                                                             MOBILE_IFACE);
+    private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"),
+                                                                             MOBILE_IFACE);
 
-    private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(
-            parse("192.168.0.66"), parse("192.168.0.1"));
-    private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(
-            parse("fd00::66"), parse("fd00::"));
+    private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(parse("192.168.0.66"),
+                                                                           parse("192.168.0.1"),
+                                                                           WIFI_IFACE);
+    private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::66"),
+                                                                           parse("fd00::"),
+                                                                           WIFI_IFACE);
 
     private INetworkManagementService mNetManager;
     private INetworkStatsService mStatsService;
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
index 9ab2a86..9f97311 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
@@ -33,6 +33,7 @@
     private Path mPath;
 
     private final Random mRandom = new Random();
+    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
     private final ArrayList<Path> mPathList = new ArrayList<Path>();
 
     @Override
@@ -45,19 +46,32 @@
         setContentView(view);
     }
 
-    private Path makePath() {
+    private static Path makePath() {
         Path path = new Path();
         buildPath(path);
         return path;
     }
 
-    private void buildPath(Path path) {
+    private static void buildPath(Path path) {
         path.moveTo(0.0f, 0.0f);
         path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
         path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
         path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
     }
 
+    private static Path makeLargePath() {
+        Path path = new Path();
+        buildLargePath(path);
+        return path;
+    }
+
+    private static void buildLargePath(Path path) {
+        path.moveTo(0.0f, 0.0f);
+        path.cubicTo(0.0f, 0.0f, 10000.0f, 15000.0f, 10000.0f, 20000.0f);
+        path.cubicTo(10000.0f, 20000.0f, 5000.0f, 30000.0f, -8000.0f, 20000.0f);
+        path.cubicTo(-8000.0f, 20000.0f, 10000.0f, 20000.0f, 20000.0f, 0.0f);
+    }
+
     public class PathsView extends View {
         private final Paint mMediumPaint;
 
@@ -97,6 +111,9 @@
                 int r = mRandom.nextInt(10);
                 if (r == 5 || r == 3) {
                     mPathList.add(path);
+                } else if (r == 7) {
+                    path = makeLargePath();
+                    mPathList.add(path);
                 }
     
                 canvas.save();
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/BWFilter.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/BWFilter.java
index 4fd63bf..64be4aa 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/BWFilter.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/BWFilter.java
@@ -18,7 +18,6 @@
 
 import java.lang.Math;
 
-import android.renderscript.Allocation;
 
 public class BWFilter extends TestBase {
     private ScriptC_bwfilter mScript;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index d2139ea..8cf46c2 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -153,20 +153,23 @@
         // more work is ready.  Either way, display the result.
         @Override
         public void handleMessage(Message msg) {
-            mTest.updateBitmap(mBitmapOut);
-            mDisplayView.invalidate();
-
             boolean doTest = false;
             synchronized(this) {
+                if (mRS == null) {
+                    return;
+                }
+                mTest.updateBitmap(mBitmapOut);
+                mDisplayView.invalidate();
                 if (mRunCount > 0) {
                     mRunCount--;
                     if (mRunCount > 0) {
                         doTest = true;
                     }
                 }
-            }
-            if (doTest) {
-                mTest.runTestSendMessage();
+
+                if (doTest) {
+                    mTest.runTestSendMessage();
+                }
             }
         }
 
@@ -382,11 +385,7 @@
                 }
             };
 
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.main);
-
+    void init() {
         mBitmapIn = loadBitmap(R.drawable.img1600x1067);
         mBitmapIn2 = loadBitmap(R.drawable.img1600x1067b);
         mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(),
@@ -434,6 +433,50 @@
         changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
+    void cleanup() {
+        synchronized(this) {
+            RenderScript rs = mRS;
+            mRS = null;
+            while(mDoingBenchmark) {
+                try {
+                    Thread.sleep(1, 0);
+                } catch(InterruptedException e) {
+                }
+
+            }
+            rs.destroy();
+        }
+
+        mInPixelsAllocation = null;
+        mInPixelsAllocation2 = null;
+        mOutPixelsAllocation = null;
+        mBitmapIn = null;
+        mBitmapIn2 = null;
+        mBitmapOut = null;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        init();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        cleanup();
+    }
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        init();
+    }
 
     private Bitmap loadBitmap(int resource) {
         final BitmapFactory.Options options = new BitmapFactory.Options();
@@ -476,8 +519,13 @@
         changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
+
+
     // For benchmark test
     public float getBenchmark() {
+        if (mRS == null) {
+            return 0;
+        }
         mDoingBenchmark = true;
 
         mTest.setupBenchmark();
@@ -504,7 +552,6 @@
 
         mTest.exitBenchmark();
         mDoingBenchmark = false;
-
         return ft;
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs
index 87b56f7..9ec1246 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/blend.rs
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 uchar alpha = 0x0;
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/bwfilter.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/bwfilter.rs
index 03e5a79..e706d44 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/bwfilter.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/bwfilter.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 //#pragma rs_fp_relaxed
 
 static float sr = 0.f;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs
index fda0d1e..4f1e73e 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colorcube.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 #pragma rs_fp_relaxed
 
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colormatrix.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colormatrix.fs
index ba8711b..86fb248 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colormatrix.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/colormatrix.fs
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
-
+#include "ip.rsh"
 
 static rs_matrix4x4 Mat;
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/contrast.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/contrast.rs
index 5fd7be1..d3743d3 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/contrast.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/contrast.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_full
+#include "ip.rsh"
 
 static float brightM = 0.f;
 static float brightC = 0.f;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.fs
index 772503f..177e86e 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 int32_t gWidth;
 int32_t gHeight;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.fs
index a916bfb..922a593 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 int32_t gWidth;
 int32_t gHeight;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.fs
index 5f03483..6595874 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.fs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 uchar4 __attribute__((kernel)) root(uchar4 v_in) {
     return v_in;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/exposure.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/exposure.rs
index adfae4a..0f05cb9 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/exposure.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/exposure.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_full
+#include "ip.rsh"
 
 static float bright = 0.f;
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
index 1ea37db..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.fs
index 3e76368..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_full.rs
index 20f27e2..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_relaxed.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_relaxed.fs
index dc3ffcb..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_relaxed.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.fs
index 4ae095d..2e62cd7 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 uchar __attribute__((kernel)) genRand() {
     return (uchar)rsRand(0xff);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/greyscale.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/greyscale.fs
index d419459..4e13072 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/greyscale.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/greyscale.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh
similarity index 79%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh
index ffdcfe3..01a3346 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -15,8 +15,6 @@
  */
 
 #pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#pragma rs java_package_name(com.android.rs.image)
 
-#include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_full.rs
index da6a291..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_relaxed.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_relaxed.fs
index b115445..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_relaxed.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/levels_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/mandelbrot.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/mandelbrot.rs
index ac2061b..de0bd00 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/mandelbrot.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/mandelbrot.rs
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 uint32_t gMaxIteration = 500;
 uint32_t gDimX = 1024;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/shadows.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/shadows.rs
index 7ee3405..f6c149d 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/shadows.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/shadows.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 //#pragma rs_fp_relaxed
 
 static double shadowFilterMap[] = {
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.fs
index 86e155a..0b2c2e8 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.fs
@@ -1,6 +1,20 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+/*
+ * 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 "ip.rsh"
 
 
 int height;
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vibrance.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vibrance.rs
index fae94a1..ad4de58 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vibrance.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vibrance.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 float vibrance = 0.f;
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_full.rs
index c83c6e1..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_relaxed.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_relaxed.fs
index 9120612..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_relaxed.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_full.rs
index 64942d9..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_relaxed.fs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_relaxed.fs
index 8e47ea9..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_relaxed.fs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vignette_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/wbalance.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/wbalance.rs
index 8c825f2..6650671 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/wbalance.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/wbalance.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
+#include "ip.rsh"
 //#pragma rs_fp_relaxed
 
 static int histR[256] = {0}, histG[256] = {0}, histB[256] = {0};
diff --git a/tests/RenderScriptTests/ImageProcessing2/Android.mk b/tests/RenderScriptTests/ImageProcessing2/Android.mk
index dc6c0d8..52966a3 100644
--- a/tests/RenderScriptTests/ImageProcessing2/Android.mk
+++ b/tests/RenderScriptTests/ImageProcessing2/Android.mk
@@ -26,8 +26,8 @@
 
 LOCAL_PACKAGE_NAME := ImageProcessing2
 LOCAL_SDK_VERSION := 8
-LOCAL_RENDERSCRIPT_TARGET_API := 17
-LOCAL_RENDERSCRIPT_COMPATIBILITY := 17
+LOCAL_RENDERSCRIPT_TARGET_API := 18
+LOCAL_RENDERSCRIPT_COMPATIBILITY := 18
 LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE := $(TOPDIR)external/clang/lib/Headers \
                                         $(TOPDIR)frameworks/rs/scriptc
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/BWFilter.java
similarity index 60%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/BWFilter.java
index e93bef3..4b19856 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/BWFilter.java
@@ -14,25 +14,21 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+package com.android.rs.image2;
+
+import java.lang.Math;
 
 
-static rs_matrix4x4 Mat;
+public class BWFilter extends TestBase {
+    private ScriptC_bwfilter mScript;
 
-void init() {
-    rsMatrixLoadIdentity(&Mat);
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_bwfilter(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareBwFilter(50, 50, 50);
+        mScript.forEach_bwFilterKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
 }
-
-void setMatrix(rs_matrix4x4 m) {
-    Mat = m;
-}
-
-void root(const uchar4 *in, uchar4 *out) {
-    float4 f = convert_float4(*in);
-    f = rsMatrixMultiply(&Mat, f);
-    f = clamp(f, 0.f, 255.f);
-    *out = convert_uchar4(f);
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java
index ac02101..d81ba88 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java
@@ -44,8 +44,10 @@
             new AdapterView.OnItemSelectedListener() {
                 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                     currentIntrinsic = pos;
-                    runTest();
-                    act.updateDisplay();
+                    if (mRS != null) {
+                        runTest();
+                        act.updateDisplay();
+                    }
                 }
 
                 public void onNothingSelected(AdapterView parent) {
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25G.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25G.java
new file mode 100644
index 0000000..19aa9f7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25G.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.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.graphics.Bitmap;
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Blur25G extends TestBase {
+    private final int MAX_RADIUS = 25;
+    private float mRadius = MAX_RADIUS;
+
+    private ScriptIntrinsicBlur mIntrinsic;
+
+    private ScriptC_greyscale mScript;
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
+
+
+    public Blur25G() {
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Radius");
+        b.setProgress(100);
+        return true;
+    }
+
+
+    public void onBar1Changed(int progress) {
+        mRadius = ((float)progress) / 100.0f * MAX_RADIUS;
+        if (mRadius <= 0.10f) {
+            mRadius = 0.10f;
+        }
+        mIntrinsic.setRadius(mRadius);
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+        tb.setX(width);
+        tb.setY(height);
+        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+        mScript = new ScriptC_greyscale(mRS);
+        mScript.forEach_toU8(mInPixelsAllocation, mScratchPixelsAllocation1);
+
+        mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8(mRS));
+        mIntrinsic.setRadius(MAX_RADIUS);
+        mIntrinsic.setInput(mScratchPixelsAllocation1);
+    }
+
+    public void runTest() {
+        mIntrinsic.forEach(mScratchPixelsAllocation2);
+    }
+
+    public void setupBenchmark() {
+        mIntrinsic.setRadius(MAX_RADIUS);
+    }
+
+    public void exitBenchmark() {
+        mIntrinsic.setRadius(mRadius);
+    }
+
+    public void updateBitmap(Bitmap b) {
+        mScript.forEach_toU8_4(mScratchPixelsAllocation2, mOutPixelsAllocation);
+        mOutPixelsAllocation.copyTo(b);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorCube.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorCube.java
new file mode 100644
index 0000000..e960385
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorCube.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class ColorCube extends TestBase {
+    private Allocation mCube;
+    private ScriptC_colorcube mScript;
+    private ScriptIntrinsic3DLUT mIntrinsic;
+    private boolean mUseIntrinsic;
+
+    public ColorCube(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    private void initCube() {
+        final int sx = 32;
+        final int sy = 32;
+        final int sz = 16;
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(sx);
+        tb.setY(sy);
+        tb.setZ(sz);
+        Type t = tb.create();
+        mCube = Allocation.createTyped(mRS, t);
+
+        int dat[] = new int[sx * sy * sz];
+        for (int z = 0; z < sz; z++) {
+            for (int y = 0; y < sy; y++) {
+                for (int x = 0; x < sx; x++ ) {
+                    int v = 0xff000000;
+                    v |= (0xff * x / (sx - 1));
+                    v |= (0xff * y / (sy - 1)) << 8;
+                    v |= (0xff * z / (sz - 1)) << 16;
+                    dat[z*sy*sx + y*sx + x] = v;
+                }
+            }
+        }
+
+        mCube.copyFromUnchecked(dat);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_colorcube(mRS, res, R.raw.colorcube);
+        mIntrinsic = ScriptIntrinsic3DLUT.create(mRS, Element.U8_4(mRS));
+
+        initCube();
+        mScript.invoke_setCube(mCube);
+        mIntrinsic.setLUT(mCube);
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Contrast.java
similarity index 61%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Contrast.java
index e93bef3..3ae5d2a 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Contrast.java
@@ -14,25 +14,21 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+package com.android.rs.image2;
+
+import java.lang.Math;
 
 
-static rs_matrix4x4 Mat;
+public class Contrast extends TestBase {
+    private ScriptC_contrast mScript;
 
-void init() {
-    rsMatrixLoadIdentity(&Mat);
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_contrast(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_contrast(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
 }
-
-void setMatrix(rs_matrix4x4 m) {
-    Mat = m;
-}
-
-void root(const uchar4 *in, uchar4 *out) {
-    float4 f = convert_float4(*in);
-    f = rsMatrixMultiply(&Mat, f);
-    f = clamp(f, 0.f, 255.f);
-    *out = convert_uchar4(f);
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Exposure.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Exposure.java
new file mode 100644
index 0000000..deb6b46e
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Exposure.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+
+public class Exposure extends TestBase {
+    private ScriptC_exposure mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_exposure(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_exposure(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
index 2d85ae7..5b16e24 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
@@ -16,9 +16,6 @@
 
 package com.android.rs.image2;
 
-import java.lang.Math;
-
-import android.support.v8.renderscript.*;
 import android.util.Log;
 
 public class Greyscale extends TestBase {
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
index 19bea43..4b0e2dd 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
@@ -47,12 +47,73 @@
 public class ImageProcessingActivity2 extends Activity
                                        implements SeekBar.OnSeekBarChangeListener {
     private final String TAG = "Img";
-    private final String RESULT_FILE = "image_processing_result.csv";
+    public final String RESULT_FILE = "image_processing_result.csv";
+
+    RenderScript mRS;
+    Allocation mInPixelsAllocation;
+    Allocation mInPixelsAllocation2;
+    Allocation mOutPixelsAllocation;
+
+    /**
+     * Define enum type for test names
+     */
+    public enum TestName {
+        // totally there are 38 test cases
+        LEVELS_VEC3_RELAXED ("Levels Vec3 Relaxed"),
+        LEVELS_VEC4_RELAXED ("Levels Vec4 Relaxed"),
+        LEVELS_VEC3_FULL ("Levels Vec3 Full"),
+        LEVELS_VEC4_FULL ("Levels Vec4 Full"),
+        BLUR_RADIUS_25 ("Blur radius 25"),
+        INTRINSIC_BLUE_RADIUS_25 ("Intrinsic Blur radius 25"),
+        GREYSCALE ("Greyscale"),
+        GRAIN ("Grain"),
+        FISHEYE_FULL ("Fisheye Full"),
+        FISHEYE_RELAXED ("Fisheye Relaxed"),
+        FISHEYE_APPROXIMATE_FULL ("Fisheye Approximate Full"),
+        FISHEYE_APPROXIMATE_RELAXED ("Fisheye Approximate Relaxed"),
+        VIGNETTE_FULL ("Vignette Full"),
+        VIGNETTE_RELAXED ("Vignette Relaxed"),
+        VIGNETTE_APPROXIMATE_FULL ("Vignette Approximate Full"),
+        VIGNETTE_APPROXIMATE_RELAXED ("Vignette Approximate Relaxed"),
+        GROUP_TEST_EMULATED ("Group Test (emulated)"),
+        GROUP_TEST_NATIVE ("Group Test (native)"),
+        CONVOLVE_3X3 ("Convolve 3x3"),
+        INTRINSICS_CONVOLVE_3X3 ("Intrinsics Convolve 3x3"),
+        COLOR_MATRIX ("ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX ("Intrinsics ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX_GREY ("Intrinsics ColorMatrix Grey"),
+        COPY ("Copy"),
+        CROSS_PROCESS_USING_LUT ("CrossProcess (using LUT)"),
+        CONVOLVE_5X5 ("Convolve 5x5"),
+        INTRINSICS_CONVOLVE_5X5 ("Intrinsics Convolve 5x5"),
+        MANDELBROT ("Mandelbrot"),
+        INTRINSICS_BLEND ("Intrinsics Blend"),
+        INTRINSICS_BLUR_25G ("Intrinsics Blur 25 uchar"),
+        VIBRANCE ("Vibrance"),
+        BW_FILTER ("BW Filter"),
+        SHADOWS ("Shadows"),
+        CONTRAST ("Contrast"),
+        EXPOSURE ("Exposure"),
+        WHITE_BALANCE ("White Balance"),
+        COLOR_CUBE ("Color Cube"),
+        COLOR_CUBE_3D_INTRINSIC ("Color Cube (3D LUT intrinsic)");
+
+
+        private final String name;
+
+        private TestName(String s) {
+            name = s;
+        }
+
+        // return quoted string as displayed test name
+        public String toString() {
+            return name;
+        }
+    }
 
     Bitmap mBitmapIn;
     Bitmap mBitmapIn2;
     Bitmap mBitmapOut;
-    String mTestNames[];
 
     private Spinner mSpinner;
     private SeekBar mBar1;
@@ -77,6 +138,7 @@
     private boolean mDoingBenchmark;
 
     private TestBase mTest;
+    private int mRunCount;
 
     public void updateDisplay() {
             mTest.updateBitmap(mBitmapOut);
@@ -135,98 +197,125 @@
     }
 
 
-    void changeTest(int testID) {
+    void changeTest(TestName testName) {
         if (mTest != null) {
             mTest.destroy();
         }
-        switch(testID) {
-        case 0:
+        switch(testName) {
+        case LEVELS_VEC3_RELAXED:
             mTest = new LevelsV4(false, false);
             break;
-        case 1:
+        case LEVELS_VEC4_RELAXED:
             mTest = new LevelsV4(false, true);
             break;
-        case 2:
+        case LEVELS_VEC3_FULL:
             mTest = new LevelsV4(true, false);
             break;
-        case 3:
+        case LEVELS_VEC4_FULL:
             mTest = new LevelsV4(true, true);
             break;
-        case 4:
+        case BLUR_RADIUS_25:
             mTest = new Blur25(false);
             break;
-        case 5:
+        case INTRINSIC_BLUE_RADIUS_25:
             mTest = new Blur25(true);
             break;
-        case 6:
+        case GREYSCALE:
             mTest = new Greyscale();
             break;
-        case 7:
+        case GRAIN:
             mTest = new Grain();
             break;
-        case 8:
+        case FISHEYE_FULL:
             mTest = new Fisheye(false, false);
             break;
-        case 9:
+        case FISHEYE_RELAXED:
             mTest = new Fisheye(false, true);
             break;
-        case 10:
+        case FISHEYE_APPROXIMATE_FULL:
             mTest = new Fisheye(true, false);
             break;
-        case 11:
+        case FISHEYE_APPROXIMATE_RELAXED:
             mTest = new Fisheye(true, true);
             break;
-        case 12:
+        case VIGNETTE_FULL:
             mTest = new Vignette(false, false);
             break;
-        case 13:
+        case VIGNETTE_RELAXED:
             mTest = new Vignette(false, true);
             break;
-        case 14:
+        case VIGNETTE_APPROXIMATE_FULL:
             mTest = new Vignette(true, false);
             break;
-        case 15:
+        case VIGNETTE_APPROXIMATE_RELAXED:
             mTest = new Vignette(true, true);
             break;
-        case 16:
+        case GROUP_TEST_EMULATED:
             mTest = new GroupTest(false);
             break;
-        case 17:
+        case GROUP_TEST_NATIVE:
             mTest = new GroupTest(true);
             break;
-        case 18:
+        case CONVOLVE_3X3:
             mTest = new Convolve3x3(false);
             break;
-        case 19:
+        case INTRINSICS_CONVOLVE_3X3:
             mTest = new Convolve3x3(true);
             break;
-        case 20:
+        case COLOR_MATRIX:
             mTest = new ColorMatrix(false, false);
             break;
-        case 21:
+        case INTRINSICS_COLOR_MATRIX:
             mTest = new ColorMatrix(true, false);
             break;
-        case 22:
+        case INTRINSICS_COLOR_MATRIX_GREY:
             mTest = new ColorMatrix(true, true);
             break;
-        case 23:
+        case COPY:
             mTest = new Copy();
             break;
-        case 24:
+        case CROSS_PROCESS_USING_LUT:
             mTest = new CrossProcess();
             break;
-        case 25:
+        case CONVOLVE_5X5:
             mTest = new Convolve5x5(false);
             break;
-        case 26:
+        case INTRINSICS_CONVOLVE_5X5:
             mTest = new Convolve5x5(true);
             break;
-        case 27:
+        case MANDELBROT:
             mTest = new Mandelbrot();
             break;
-        case 28:
+        case INTRINSICS_BLEND:
             mTest = new Blend();
             break;
+        case INTRINSICS_BLUR_25G:
+            mTest = new Blur25G();
+            break;
+        case VIBRANCE:
+            mTest = new Vibrance();
+            break;
+        case BW_FILTER:
+            mTest = new BWFilter();
+            break;
+        case SHADOWS:
+            mTest = new Shadows();
+            break;
+        case CONTRAST:
+            mTest = new Contrast();
+            break;
+        case EXPOSURE:
+            mTest = new Exposure();
+            break;
+        case WHITE_BALANCE:
+            mTest = new WhiteBalance();
+            break;
+        case COLOR_CUBE:
+            mTest = new ColorCube(false);
+            break;
+        case COLOR_CUBE_3D_INTRINSIC:
+            mTest = new ColorCube(true);
+            break;
         }
 
         mTest.createBaseTest(this, mBitmapIn, mBitmapIn2, mBitmapOut);
@@ -238,45 +327,14 @@
     }
 
     void setupTests() {
-        mTestNames = new String[29];
-        mTestNames[0] = "Levels Vec3 Relaxed";
-        mTestNames[1] = "Levels Vec4 Relaxed";
-        mTestNames[2] = "Levels Vec3 Full";
-        mTestNames[3] = "Levels Vec4 Full";
-        mTestNames[4] = "Blur radius 25";
-        mTestNames[5] = "Intrinsic Blur radius 25";
-        mTestNames[6] = "Greyscale";
-        mTestNames[7] = "Grain";
-        mTestNames[8] = "Fisheye Full";
-        mTestNames[9] = "Fisheye Relaxed";
-        mTestNames[10] = "Fisheye Approximate Full";
-        mTestNames[11] = "Fisheye Approximate Relaxed";
-        mTestNames[12] = "Vignette Full";
-        mTestNames[13] = "Vignette Relaxed";
-        mTestNames[14] = "Vignette Approximate Full";
-        mTestNames[15] = "Vignette Approximate Relaxed";
-        mTestNames[16] = "Group Test (emulated)";
-        mTestNames[17] = "Group Test (native)";
-        mTestNames[18] = "Convolve 3x3";
-        mTestNames[19] = "Intrinsics Convolve 3x3";
-        mTestNames[20] = "ColorMatrix";
-        mTestNames[21] = "Intrinsics ColorMatrix";
-        mTestNames[22] = "Intrinsics ColorMatrix Grey";
-        mTestNames[23] = "Copy";
-        mTestNames[24] = "CrossProcess (using LUT)";
-        mTestNames[25] = "Convolve 5x5";
-        mTestNames[26] = "Intrinsics Convolve 5x5";
-        mTestNames[27] = "Mandelbrot";
-        mTestNames[28] = "Intrinsics Blend";
-
-        mTestSpinner.setAdapter(new ArrayAdapter<String>(
-            this, R.layout.spinner_layout, mTestNames));
+        mTestSpinner.setAdapter(new ArrayAdapter<TestName>(
+            this, R.layout.spinner_layout, TestName.values()));
     }
 
     private AdapterView.OnItemSelectedListener mTestSpinnerListener =
             new AdapterView.OnItemSelectedListener() {
                 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                    changeTest(pos);
+                    changeTest(TestName.values()[pos]);
                 }
 
                 public void onNothingSelected(AdapterView parent) {
@@ -291,7 +349,8 @@
 
         mBitmapIn = loadBitmap(R.drawable.img1600x1067);
         mBitmapIn2 = loadBitmap(R.drawable.img1600x1067b);
-        mBitmapOut = loadBitmap(R.drawable.img1600x1067);
+        mBitmapOut = Bitmap.createBitmap(mBitmapIn.getWidth(), mBitmapIn.getHeight(),
+                                         mBitmapIn.getConfig());
 
         mSurfaceView = (SurfaceView) findViewById(R.id.surface);
 
@@ -324,23 +383,22 @@
         mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
         mBenchmarkResult.setText("Result: not run");
 
+
+        mRS = RenderScript.create(this);
+        mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+        mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, mBitmapIn2);
+        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut);
+
+
         setupTests();
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
 
     private Bitmap loadBitmap(int resource) {
         final BitmapFactory.Options options = new BitmapFactory.Options();
         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options));
-    }
-
-    private static Bitmap copyBitmap(Bitmap source) {
-        Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
-        Canvas c = new Canvas(b);
-        c.drawBitmap(source, 0, 0, null);
-        source.recycle();
-        return b;
+        return BitmapFactory.decodeResource(getResources(), resource, options);
     }
 
     // button hook
@@ -364,10 +422,10 @@
         try {
             BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
             Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
-            for (int i = 0; i < mTestNames.length; i++ ) {
-                changeTest(i);
+            for (TestName tn: TestName.values()) {
+                changeTest(tn);
                 float t = getBenchmark();
-                String s = new String("" + mTestNames[i] + ", " + t);
+                String s = new String("" + tn.toString() + ", " + t);
                 rsWriter.write(s + "\n");
                 Log.v(TAG, "Test " + s + "ms\n");
             }
@@ -375,7 +433,7 @@
         } catch (IOException e) {
             Log.v(TAG, "Unable to write result file " + e.getMessage());
         }
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
     // For benchmark test
@@ -392,7 +450,6 @@
             mTest.finish();
         } while (t > java.lang.System.currentTimeMillis());
 
-
         //Log.v(TAG, "Benchmarking");
         int ct = 0;
         t = java.lang.System.currentTimeMillis();
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java
index 556d797..1780587 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java
@@ -18,7 +18,6 @@
 
 import java.lang.Math;
 
-import android.support.v8.renderscript.*;
 import android.util.Log;
 import android.widget.SeekBar;
 import android.widget.TextView;
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
similarity index 61%
copy from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
index b3a7ef0..353c56d 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+package com.android.rs.image2;
 
-const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
+public class Shadows extends TestBase {
+    private ScriptC_shadows mScript;
 
-    float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_shadows(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareShadows(50.f);
+        mScript.forEach_shadowsKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
 }
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
index dbbc594..eeabc73 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
@@ -90,18 +90,17 @@
 
     public final void createBaseTest(ImageProcessingActivity2 ipact, Bitmap b, Bitmap b2, Bitmap outb) {
         act = ipact;
-        mRS = RenderScript.create(act);
+        mRS = ipact.mRS;
 
-        mInPixelsAllocation = Allocation.createFromBitmap(mRS, b);
-        mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2);
-        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, outb);
+        mInPixelsAllocation = ipact.mInPixelsAllocation;
+        mInPixelsAllocation2 = ipact.mInPixelsAllocation2;
+        mOutPixelsAllocation = ipact.mOutPixelsAllocation;
 
         createTest(act.getResources());
     }
 
     // Must override
     public void createTest(android.content.res.Resources res) {
-        android.util.Log.e("img", "implement createTest");
     }
 
     // Must override
@@ -113,7 +112,6 @@
     }
 
     public void destroy() {
-        mRS.destroy();
     }
 
     public void updateBitmap(Bitmap b) {
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vibrance.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vibrance.java
new file mode 100644
index 0000000..37c0aa7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vibrance.java
@@ -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.
+ */
+
+package com.android.rs.image2;
+
+public class Vibrance extends TestBase {
+    private ScriptC_vibrance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_vibrance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_vibrance(50.f);
+        mScript.invoke_prepareVibrance();
+        mScript.forEach_vibranceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
index 8618d5a..9f6d34d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
@@ -16,7 +16,6 @@
 
 package com.android.rs.image2;
 
-import android.support.v8.renderscript.*;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/WhiteBalance.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/WhiteBalance.java
new file mode 100644
index 0000000..658e3b1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/WhiteBalance.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+
+public class WhiteBalance extends TestBase {
+    private ScriptC_wbalance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_wbalance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_histogramSource(mInPixelsAllocation);
+        mScript.set_histogramWidth(mInPixelsAllocation.getType().getX());
+        mScript.set_histogramHeight(mInPixelsAllocation.getType().getY());
+        mScript.invoke_prepareWhiteBalance();
+        mScript.forEach_whiteBalanceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs
index 4d90725..9ec1246 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 uchar alpha = 0x0;
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/bwfilter.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/bwfilter.rs
new file mode 100644
index 0000000..e706d44
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/bwfilter.rs
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static float sr = 0.f;
+static float sg = 0.f;
+static float sb = 0.f;
+
+void prepareBwFilter(uint32_t rw, uint32_t gw, uint32_t bw) {
+
+    sr = rw;
+    sg = gw;
+    sb = bw;
+
+    float imageMin = min(sg,sb);
+    imageMin = fmin(sr,imageMin);
+    float imageMax = max(sg,sb);
+    imageMax = fmax(sr,imageMax);
+    float avg = (imageMin + imageMax)/2;
+    sb /= avg;
+    sg /= avg;
+    sr /= avg;
+
+}
+
+void bwFilterKernel(const uchar4 *in, uchar4 *out) {
+    float r = in->r * sr;
+    float g = in->g * sg;
+    float b = in->b * sb;
+    float localMin, localMax, avg;
+    localMin = fmin(g,b);
+    localMin = fmin(r,localMin);
+    localMax = fmax(g,b);
+    localMax = fmax(r,localMax);
+    avg = (localMin+localMax) * 0.5f;
+    out->r = out->g = out->b = rsClamp(avg, 0, 255);
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colorcube.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colorcube.rs
new file mode 100644
index 0000000..4f1e73e
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colorcube.rs
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+#pragma rs_fp_relaxed
+
+
+static rs_allocation gCube;
+static int4 gDims;
+static int4 gCoordMul;
+
+
+void setCube(rs_allocation c) {
+    gCube = c;
+    gDims.x = rsAllocationGetDimX(gCube);
+    gDims.y = rsAllocationGetDimY(gCube);
+    gDims.z = rsAllocationGetDimZ(gCube);
+    gDims.w = 0;
+
+    float4 m = (float4)(1.f / 255.f) * convert_float4(gDims - 1);
+    gCoordMul = convert_int4(m * (float4)0x10000);
+
+    rsDebug("dims", gDims);
+    rsDebug("gCoordMul", gCoordMul);
+}
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    //rsDebug("root", in);
+
+    int4 baseCoord = convert_int4(*in) * gCoordMul;
+    int4 coord1 = baseCoord >> (int4)16;
+    int4 coord2 = min(coord1 + 1, gDims - 1);
+
+    int4 weight2 = baseCoord & 0xffff;
+    int4 weight1 = (int4)0x10000 - weight2;
+
+    uint4 v000 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord1.z));
+    uint4 v100 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord1.z));
+    uint4 v010 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord1.z));
+    uint4 v110 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord1.z));
+    uint4 v001 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord1.y, coord2.z));
+    uint4 v101 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord1.y, coord2.z));
+    uint4 v011 = convert_uint4(rsGetElementAt_uchar4(gCube, coord1.x, coord2.y, coord2.z));
+    uint4 v111 = convert_uint4(rsGetElementAt_uchar4(gCube, coord2.x, coord2.y, coord2.z));
+
+    uint4 yz00 = ((v000 * weight1.x) + (v100 * weight2.x)) >> (int4)8;
+    uint4 yz10 = ((v010 * weight1.x) + (v110 * weight2.x)) >> (int4)8;
+    uint4 yz01 = ((v001 * weight1.x) + (v101 * weight2.x)) >> (int4)8;
+    uint4 yz11 = ((v011 * weight1.x) + (v111 * weight2.x)) >> (int4)8;
+
+    uint4 z0 = ((yz00 * weight1.y) + (yz10 * weight2.y)) >> (int4)16;
+    uint4 z1 = ((yz01 * weight1.y) + (yz11 * weight2.y)) >> (int4)16;
+
+    uint4 v = ((z0 * weight1.z) + (z1 * weight2.z)) >> (int4)16;
+    uint4 v2 = (v + 0x7f) >> (int4)8;
+
+    *out = convert_uchar4(v2);
+    out->a = 0xff;
+
+    #if 0
+    if (in->r != out->r) {
+        rsDebug("dr", in->r - out->r);
+        //rsDebug("in", convert_int4(*in));
+        //rsDebug("coord1", coord1);
+        //rsDebug("coord2", coord2);
+        //rsDebug("weight1", weight1);
+        //rsDebug("weight2", weight2);
+        //rsDebug("yz00", yz00);
+        //rsDebug("z0", z0);
+        //rsDebug("v", v);
+        //rsDebug("v2", v2);
+        //rsDebug("out", convert_int4(*out));
+    }
+    #endif
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.fs
similarity index 79%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.fs
index e93bef3..86fb248 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.fs
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
-
+#include "ip.rsh"
 
 static rs_matrix4x4 Mat;
 
@@ -29,10 +26,10 @@
     Mat = m;
 }
 
-void root(const uchar4 *in, uchar4 *out) {
-    float4 f = convert_float4(*in);
+uchar4 __attribute__((kernel)) root(uchar4 in) {
+    float4 f = convert_float4(in);
     f = rsMatrixMultiply(&Mat, f);
     f = clamp(f, 0.f, 255.f);
-    *out = convert_uchar4(f);
+    return convert_uchar4(f);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/contrast.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/contrast.rs
new file mode 100644
index 0000000..d3743d3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/contrast.rs
@@ -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.
+ */
+
+#include "ip.rsh"
+
+static float brightM = 0.f;
+static float brightC = 0.f;
+
+void setBright(float v) {
+    brightM = pow(2.f, v / 100.f);
+    brightC = 127.f - brightM * 127.f;
+}
+
+void contrast(const uchar4 *in, uchar4 *out)
+{
+#if 0
+    out->r = rsClamp((int)(brightM * in->r + brightC), 0, 255);
+    out->g = rsClamp((int)(brightM * in->g + brightC), 0, 255);
+    out->b = rsClamp((int)(brightM * in->b + brightC), 0, 255);
+#else
+    float3 v = convert_float3(in->rgb) * brightM + brightC;
+    out->rgb = convert_uchar3(clamp(v, 0.f, 255.f));
+#endif
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.fs
similarity index 93%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.fs
index b1ad241..922a593 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 int32_t gWidth;
 int32_t gHeight;
@@ -24,7 +22,7 @@
 
 float gCoeffs[25];
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
     uint32_t x0 = max((int32_t)x-2, 0);
     uint32_t x1 = max((int32_t)x-1, 0);
     uint32_t x2 = x;
@@ -68,8 +66,7 @@
               + convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
 
     p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
-    p0.a = 255.f;
-    *out = convert_uchar4(p0);
+    return convert_uchar4(p0);
 }
 
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.fs
similarity index 84%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.fs
index ffdcfe3..6595874 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.fs
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-#include "levels.rsh"
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    return v_in;
+}
+
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs
deleted file mode 100644
index 31e4241..0000000
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs
+++ /dev/null
@@ -1,24 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-
-void root(const uchar4 *v_in, uchar4 *v_out) {
-    *v_out = *v_in;
-}
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
similarity index 66%
rename from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
index b3a7ef0..0f05cb9 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+static float bright = 0.f;
 
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
-
-    float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
+void setBright(float v) {
+    bright = 255.f / (255.f - v);
 }
 
+void exposure(const uchar4 *in, uchar4 *out)
+{
+    out->r = rsClamp((int)(bright * in->r), 0, 255);
+    out->g = rsClamp((int)(bright * in->g), 0, 255);
+    out->b = rsClamp((int)(bright * in->b), 0, 255);
+}
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
index 3809912..2eacb7d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
@@ -26,7 +26,7 @@
     neg_center = -center;
     inv_dimensions.x = 1.f / (float)dim_x;
     inv_dimensions.y = 1.f / (float)dim_y;
-    alpha = k * 2.0 + 0.75;
+    alpha = k * 2.0f + 0.75f;
 
     axis_scale = (float2)1.f;
     if (dim_x > dim_y)
@@ -34,15 +34,15 @@
     else
         axis_scale.x = (float)dim_x / (float)dim_y;
     
-    const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
     const float bound = sqrt(bound2);
-    const float radius = 1.15 * bound;
+    const float radius = 1.15f * bound;
     radius2 = radius*radius;
     const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
     factor = bound / max_radian;
 }
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
     const float2 inCoord = {(float)x, (float)y};
     const float2 coord = mad(inCoord, inv_dimensions, neg_center);
@@ -53,6 +53,6 @@
     const float scalar = radian * factor * inv_dist;
     const float2 new_coord = mad(coord, scalar, center);
     const float4 fout = rsSample(in_alloc, sampler, new_coord);
-    *out = rsPackColorTo8888(fout);
+    return rsPackColorTo8888(fout);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh
index 08b4126..fcf0a3d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh
@@ -26,7 +26,7 @@
     neg_center = -center;
     inv_dimensions.x = 1.f / (float)dim_x;
     inv_dimensions.y = 1.f / (float)dim_y;
-    alpha = k * 2.0 + 0.75;
+    alpha = k * 2.0f + 0.75f;
 
     axis_scale = (float2)1.f;
     if (dim_x > dim_y)
@@ -34,15 +34,15 @@
     else
         axis_scale.x = (float)dim_x / (float)dim_y;
 
-    const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
     const float bound = sqrt(bound2);
-    const float radius = 1.15 * bound;
+    const float radius = 1.15f * bound;
     radius2 = radius*radius;
     const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
     factor = bound / max_radian;
 }
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
     const float2 inCoord = {(float)x, (float)y};
     const float2 coord = mad(inCoord, inv_dimensions, neg_center);
@@ -53,6 +53,6 @@
     const float scalar = radian * factor * inv_dist;
     const float2 new_coord = mad(coord, scalar, center);
     const float4 fout = rsSample(in_alloc, sampler, new_coord);
-    *out = rsPackColorTo8888(fout);
+    return rsPackColorTo8888(fout);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs
index cce42f9..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.fs
index 64d27ed..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
index e42df13..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.fs
index 990310b..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.fs
index 44320a5..2e62cd7 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.fs
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-void genRand(uchar *out) {
-    *out = (uchar)rsRand(0xff);
+uchar __attribute__((kernel)) genRand() {
+    return (uchar)rsRand(0xff);
 }
 
 /*
@@ -42,7 +40,7 @@
 int32_t gHMask;
 
 rs_allocation gBlendSource;
-void blend9(uchar *out, uint32_t x, uint32_t y) {
+uchar __attribute__((kernel)) blend9(uint32_t x, uint32_t y) {
     uint32_t x1 = (x-1) & gWMask;
     uint32_t x2 = (x+1) & gWMask;
     uint32_t y1 = (y-1) & gHMask;
@@ -70,14 +68,14 @@
     p20 += p02;
 
     p20 = min(p20 >> 10, (uint)255);
-    *out = (uchar)p20;
+    return (uchar)p20;
 }
 
 float gNoiseStrength;
 
 rs_allocation gNoise;
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float4 ip = convert_float4(*in);
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    float4 ip = convert_float4(in);
     float pnoise = (float) rsGetElementAt_uchar(gNoise, x & gWMask, y & gHMask);
 
     float energy_level = ip.r + ip.g + ip.b;
@@ -89,5 +87,5 @@
 
     uchar4 p = convert_uchar4(ip);
     p.a = 0xff;
-    *out = p;
+    return p;
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.fs
similarity index 66%
copy from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.fs
index b3a7ef0..4e13072 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.fs
@@ -14,17 +14,23 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    float4 f4 = rsUnpackColor8888(v_in);
 
     float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
+    return rsPackColorTo8888(mono);
 }
 
+uchar __attribute__((kernel)) toU8(uchar4 v_in) {
+    float4 f4 = convert_float4(v_in);
+    return (uchar)dot(f4.rgb, gMonoMult);
+}
+
+uchar4 __attribute__((kernel)) toU8_4(uchar v_in) {
+    return (uchar4)v_in;
+}
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
deleted file mode 100644
index b5abf3f0..0000000
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
+++ /dev/null
@@ -1,30 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
-
-const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
-
-void root(const uchar4 *v_in, uchar4 *v_out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
-
-    float3 mono = dot(f4.rgb, gMonoMult);
-    *v_out = rsPackColorTo8888(mono);
-}
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
index ffdcfe3..34e213c 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -16,7 +16,5 @@
 
 #pragma version(1)
 #pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
 
-#include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs
index 13f9032..177e86e 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 int32_t gWidth;
 int32_t gHeight;
@@ -24,21 +22,21 @@
 
 float gCoeffs[9];
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
     uint32_t x1 = min((int32_t)x+1, gWidth-1);
     uint32_t x2 = max((int32_t)x-1, 0);
     uint32_t y1 = min((int32_t)y+1, gHeight-1);
     uint32_t y2 = max((int32_t)y-1, 0);
 
-    float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
-    float4 p01 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y1))[0]);
-    float4 p02 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y1))[0]);
-    float4 p10 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y))[0]);
-    float4 p11 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y))[0]);
-    float4 p12 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y))[0]);
-    float4 p20 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y2))[0]);
-    float4 p21 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y2))[0]);
-    float4 p22 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y2))[0]);
+    float4 p00 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y1));
+    float4 p01 = convert_float4(rsGetElementAt_uchar4(gIn, x, y1));
+    float4 p02 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y1));
+    float4 p10 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y));
+    float4 p11 = convert_float4(rsGetElementAt_uchar4(gIn, x, y));
+    float4 p12 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y));
+    float4 p20 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y2));
+    float4 p21 = convert_float4(rsGetElementAt_uchar4(gIn, x, y2));
+    float4 p22 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y2));
     p00 *= gCoeffs[0];
     p01 *= gCoeffs[1];
     p02 *= gCoeffs[2];
@@ -61,7 +59,7 @@
     p20 += p02;
 
     p20 = clamp(p20, 0.f, 255.f);
-    *out = convert_uchar4(p20);
+    return convert_uchar4(p20);
 }
 
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
index 7c5d930..e289906 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
@@ -21,24 +21,26 @@
 float overInWMinInB;
 rs_matrix3x3 colorMat;
 
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float3 pixel = convert_float4(in[0]).rgb;
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    uchar4 out;
+    float3 pixel = convert_float4(in).rgb;
     pixel = rsMatrixMultiply(&colorMat, pixel);
     pixel = clamp(pixel, 0.f, 255.f);
     pixel = (pixel - inBlack) * overInWMinInB;
     pixel = pixel * outWMinOutB + outBlack;
     pixel = clamp(pixel, 0.f, 255.f);
-    out->xyz = convert_uchar3(pixel);
-    out->w = 0xff;
+    out.xyz = convert_uchar3(pixel);
+    out.w = 0xff;
+    return out;
 }
 
-void root4(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float4 pixel = convert_float4(in[0]);
+uchar4 __attribute__((kernel)) root4(uchar4 in, uint32_t x, uint32_t y) {
+    float4 pixel = convert_float4(in);
     pixel.rgb = rsMatrixMultiply(&colorMat, pixel.rgb);
     pixel = clamp(pixel, 0.f, 255.f);
     pixel = (pixel - inBlack) * overInWMinInB;
     pixel = pixel * outWMinOutB + outBlack;
     pixel = clamp(pixel, 0.f, 255.f);
-    out->xyzw = convert_uchar4(pixel);
+    return convert_uchar4(pixel);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
index a4aa388..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.fs
index ffdcfe3..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs
index 5b3912d..de0bd00 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 uint32_t gMaxIteration = 500;
 uint32_t gDimX = 1024;
@@ -23,7 +22,7 @@
 float lowerBoundY = -2.f;
 float scaleFactor = 4.f;
 
-void root(uchar4 *v_out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
   float2 p;
   p.x = lowerBoundX + ((float)x / gDimX) * scaleFactor;
   p.y = lowerBoundY + ((float)y / gDimY) * scaleFactor;
@@ -41,16 +40,16 @@
 
   if(iter >= gMaxIteration) {
     // write a non-transparent black pixel
-    *v_out = (uchar4){0, 0, 0, 0xff};
+    return (uchar4){0, 0, 0, 0xff};
   } else {
     float mi3 = gMaxIteration / 3.f;
     if (iter <= (gMaxIteration / 3))
-      *v_out = (uchar4){0xff * (iter / mi3), 0, 0, 0xff};
+      return (uchar4){0xff * (iter / mi3), 0, 0, 0xff};
     else if (iter <= (((gMaxIteration / 3) * 2)))
-      *v_out = (uchar4){0xff - (0xff * ((iter - mi3) / mi3)),
-                        (0xff * ((iter - mi3) / mi3)), 0, 0xff};
+      return (uchar4){0xff - (0xff * ((iter - mi3) / mi3)),
+                      (0xff * ((iter - mi3) / mi3)), 0, 0xff};
     else
-      *v_out = (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)),
-                        (0xff * ((iter - (mi3 * 2)) / mi3)), 0xff};
+      return (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)),
+                      (0xff * ((iter - (mi3 * 2)) / mi3)), 0xff};
   }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/shadows.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/shadows.rs
new file mode 100644
index 0000000..f6c149d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/shadows.rs
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static double shadowFilterMap[] = {
+    -0.00591,  0.0001,
+     1.16488,  0.01668,
+    -0.18027, -0.06791,
+    -0.12625,  0.09001,
+     0.15065, -0.03897
+};
+
+static double poly[] = {
+    0., 0.,
+    0., 0.,
+    0.
+};
+
+static const int ABITS = 4;
+static const int HSCALE = 256;
+static const int k1=255 << ABITS;
+static const int k2=HSCALE << ABITS;
+
+static double fastevalPoly(double *poly,int n, double x){
+
+    double f =x;
+    double sum = poly[0]+poly[1]*f;
+    int i;
+    for (i = 2; i < n; i++) {
+        f*=x;
+        sum += poly[i]*f;
+    }
+    return sum;
+}
+
+static ushort3 rgb2hsv( uchar4 rgb)
+{
+    int iMin,iMax,chroma;
+
+    int ri = rgb.r;
+    int gi = rgb.g;
+    int bi = rgb.b;
+    short rv,rs,rh;
+
+    if (ri > gi) {
+        iMax = max (ri, bi);
+        iMin = min (gi, bi);
+    } else {
+        iMax = max (gi, bi);
+        iMin = min (ri, bi);
+    }
+
+    chroma = iMax - iMin;
+    // set value
+    rv = (short)( iMax << ABITS);
+
+    // set saturation
+    if (rv == 0)
+        rs = 0;
+    else
+        rs = (short)((k1*chroma)/iMax);
+
+    // set hue
+    if (rs == 0)
+        rh = 0;
+    else {
+        if ( ri == iMax ) {
+            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
+            if (rh >= k2) rh -= k2;
+        } else if (gi  == iMax)
+            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
+        else // (bi == iMax )
+                    rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
+    }
+
+    ushort3 out;
+    out.x = rv;
+    out.y = rs;
+    out.z = rh;
+    return out;
+}
+
+static uchar4 hsv2rgb(ushort3 hsv)
+{
+    int ABITS = 4;
+    int HSCALE = 256;
+    int m;
+    int H,X,ih,is,iv;
+    int k1=255<<ABITS;
+    int k2=HSCALE<<ABITS;
+    int k3=1<<(ABITS-1);
+    int rr=0;
+    int rg=0;
+    int rb=0;
+    short cv = hsv.x;
+    short cs = hsv.y;
+    short ch = hsv.z;
+
+    // set chroma and min component value m
+    //chroma = ( cv * cs )/k1;
+    //m = cv - chroma;
+    m = ((int)cv*(k1 - (int)cs ))/k1;
+
+    // chroma  == 0 <-> cs == 0 --> m=cv
+    if (cs == 0) {
+        rb = ( rg = ( rr =( cv >> ABITS) ));
+    } else {
+        ih=(int)ch;
+        is=(int)cs;
+        iv=(int)cv;
+
+        H = (6*ih)/k2;
+        X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+
+        // removing additional bits --> unit8
+        X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
+        m=m >> ABITS;
+
+        // ( chroma + m ) --> cv ;
+        cv=(short) (cv >> ABITS);
+        switch (H) {
+        case 0:
+            rr = cv;
+            rg = X;
+            rb = m;
+            break;
+        case 1:
+            rr = X;
+            rg = cv;
+            rb = m;
+            break;
+        case 2:
+            rr = m;
+            rg = cv;
+            rb = X;
+            break;
+        case 3:
+            rr = m;
+            rg = X;
+            rb = cv;
+            break;
+        case 4:
+            rr = X;
+            rg = m;
+            rb = cv;
+            break;
+        case 5:
+            rr = cv;
+            rg = m ;
+            rb = X;
+            break;
+        }
+    }
+
+    uchar4 rgb;
+
+    rgb.r =  rr;
+    rgb.g =  rg;
+    rgb.b =  rb;
+
+    return rgb;
+}
+
+void prepareShadows(float scale) {
+    double s = (scale>=0)?scale:scale/5;
+    for (int i = 0; i < 5; i++) {
+        poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
+    }
+}
+
+void shadowsKernel(const uchar4 *in, uchar4 *out) {
+    ushort3 hsv = rgb2hsv(*in);
+    double v = (fastevalPoly(poly,5,hsv.x/4080.)*4080);
+    if (v>4080) v = 4080;
+    hsv.x = (unsigned short) ((v>0)?v:0);
+    *out = hsv2rgb(hsv);
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.fs
similarity index 67%
rename from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.fs
index d18117a..0b2c2e8 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.fs
@@ -1,6 +1,20 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+/*
+ * 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 "ip.rsh"
 
 
 int height;
@@ -56,51 +70,49 @@
     }
 }
 
-void copyIn(const uchar4 *in, float4 *out) {
-    *out = convert_float4(*in);
+float4 __attribute__((kernel)) copyIn(uchar4 in) {
+    return convert_float4(in);
 }
 
-static inline float4 GetElementAt_float4(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((float4 *)rsGetElementAt(a, x, y))[0];
-}
-
-void vert(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) vert(uint32_t x, uint32_t y) {
     float3 blurredPixel = 0;
     int gi = 0;
+    uchar4 out;
     if ((y > radius) && (y < (height - radius))) {
         for (int r = -radius; r <= radius; r ++) {
-            float4 i = GetElementAt_float4(ScratchPixel2, x, y + r);
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, y + r);
             blurredPixel += i.xyz * gaussian[gi++];
         }
     } else {
         for (int r = -radius; r <= radius; r ++) {
             int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
-            float4 i = GetElementAt_float4(ScratchPixel2, x, validH);
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, validH);
             blurredPixel += i.xyz * gaussian[gi++];
         }
     }
 
-    out->xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
-    out->w = 0xff;
+    out.xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
+    out.w = 0xff;
+    return out;
 }
 
-void horz(float4 *out, uint32_t x, uint32_t y) {
+float4 __attribute__((kernel)) horz(uint32_t x, uint32_t y) {
     float4 blurredPixel = 0;
     int gi = 0;
     if ((x > radius) && (x < (width - radius))) {
         for (int r = -radius; r <= radius; r ++) {
-            float4 i = GetElementAt_float4(ScratchPixel1, x + r, y);
+            float4 i = rsGetElementAt_float4(ScratchPixel1, x + r, y);
             blurredPixel += i * gaussian[gi++];
         }
     } else {
         for (int r = -radius; r <= radius; r ++) {
             // Stepping left and right away from the pixel
             int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
-            float4 i = GetElementAt_float4(ScratchPixel1, validX, y);
+            float4 i = rsGetElementAt_float4(ScratchPixel1, validX, y);
             blurredPixel += i * gaussian[gi++];
         }
     }
 
-    *out = blurredPixel;
+    return blurredPixel;
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
deleted file mode 100644
index 9ef4898..0000000
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
-
-
-int height;
-int width;
-static int radius;
-
-rs_allocation InPixel;
-rs_allocation ScratchPixel1;
-rs_allocation ScratchPixel2;
-
-const int MAX_RADIUS = 25;
-
-// Store our coefficients here
-static float gaussian[MAX_RADIUS * 2 + 1];
-
-void setRadius(int rad) {
-    radius = rad;
-    // Compute gaussian weights for the blur
-    // e is the euler's number
-    float e = 2.718281828459045f;
-    float pi = 3.1415926535897932f;
-    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
-    // x is of the form [-radius .. 0 .. radius]
-    // and sigma varies with radius.
-    // Based on some experimental radius values and sigma's
-    // we approximately fit sigma = f(radius) as
-    // sigma = radius * 0.4  + 0.6
-    // The larger the radius gets, the more our gaussian blur
-    // will resemble a box blur since with large sigma
-    // the gaussian curve begins to lose its shape
-    float sigma = 0.4f * (float)radius + 0.6f;
-
-    // Now compute the coefficints
-    // We will store some redundant values to save some math during
-    // the blur calculations
-    // precompute some values
-    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
-    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
-
-    float normalizeFactor = 0.0f;
-    float floatR = 0.0f;
-    for (int r = -radius; r <= radius; r ++) {
-        floatR = (float)r;
-        gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
-        normalizeFactor += gaussian[r + radius];
-    }
-
-    //Now we need to normalize the weights because all our coefficients need to add up to one
-    normalizeFactor = 1.0f / normalizeFactor;
-    for (int r = -radius; r <= radius; r ++) {
-        floatR = (float)r;
-        gaussian[r + radius] *= normalizeFactor;
-    }
-}
-
-void copyIn(const uchar4 *in, float4 *out) {
-    *out = convert_float4(*in);
-}
-
-void vert(uchar4 *out, uint32_t x, uint32_t y) {
-    float3 blurredPixel = 0;
-    const float *gPtr = gaussian;
-    if ((y > radius) && (y < (height - radius))) {
-        for (int r = -radius; r <= radius; r ++) {
-            const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel2, x, y + r);
-            blurredPixel += i->xyz * gPtr[0];
-            gPtr++;
-        }
-    } else {
-        for (int r = -radius; r <= radius; r ++) {
-            int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
-            const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel2, x, validH);
-            blurredPixel += i->xyz * gPtr[0];
-            gPtr++;
-        }
-    }
-
-    out->xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
-    out->w = 0xff;
-}
-
-void horz(float4 *out, uint32_t x, uint32_t y) {
-    float3 blurredPixel = 0;
-    const float *gPtr = gaussian;
-    if ((x > radius) && (x < (width - radius))) {
-        for (int r = -radius; r <= radius; r ++) {
-            const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel1, x + r, y);
-            blurredPixel += i->xyz * gPtr[0];
-            gPtr++;
-        }
-    } else {
-        for (int r = -radius; r <= radius; r ++) {
-            // Stepping left and right away from the pixel
-            int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
-            const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel1, validX, y);
-            blurredPixel += i->xyz * gPtr[0];
-            gPtr++;
-        }
-    }
-
-    out->xyz = blurredPixel;
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vibrance.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vibrance.rs
new file mode 100644
index 0000000..ad4de58
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vibrance.rs
@@ -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.
+ */
+
+#include "ip.rsh"
+
+float vibrance = 0.f;
+
+static const float Rf = 0.2999f;
+static const float Gf = 0.587f;
+static const float Bf = 0.114f;
+
+static float S  = 0.f;
+static float MS = 0.f;
+static float Rt = 0.f;
+static float Gt = 0.f;
+static float Bt = 0.f;
+static float Vib = 0.f;
+
+void vibranceKernel(const uchar4 *in, uchar4 *out) {
+
+    float R, G, B;
+
+    int r = in->r;
+    int g = in->g;
+    int b = in->b;
+    float red = (r-max(g, b))/256.f;
+    float sx = (float)(Vib/(1+native_exp(-red*3)));
+    S = sx+1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+    int t = (r + g) / 2;
+    R = r;
+    G = g;
+    B = b;
+
+    float Rc = R * (Rt + S) + G * Gt + B * Bt;
+    float Gc = R * Rt + G * (Gt + S) + B * Bt;
+    float Bc = R * Rt + G * Gt + B * (Bt + S);
+
+    out->r = rsClamp(Rc, 0, 255);
+    out->g = rsClamp(Gc, 0, 255);
+    out->b = rsClamp(Bc, 0, 255);
+
+}
+
+void prepareVibrance() {
+
+    Vib = vibrance/100.f;
+    S  = Vib + 1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
index a1e4ae5..04ca1f1 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
@@ -31,29 +31,29 @@
     else
         axis_scale.x = (float)dim_x / (float)dim_y;
 
-    const float max_dist = 0.5 * length(axis_scale);
+    const float max_dist = 0.5f * length(axis_scale);
     sloped_inv_max_dist = desired_slope * 1.f/max_dist;
 
     // Range needs to be between 1.3 to 0.6. When scale is zero then range is
     // 1.3 which means no vignette at all because the luminousity difference is
     // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
-    const float neg_range = 0.7*sqrt(desired_scale) - 1.3;
+    const float neg_range = 0.7f*sqrt(desired_scale) - 1.3f;
     sloped_neg_range = exp(neg_range * desired_slope);
 
     shade = desired_shade;
     opp_shade = 1.f - desired_shade;
 }
 
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
-    const float4 fin = convert_float4(*in);
+    const float4 fin = convert_float4(in);
     const float2 inCoord = {(float)x, (float)y};
     const float2 coord = mad(inCoord, inv_dimensions, neg_center);
     const float sloped_dist_ratio = length(axis_scale * coord)  * sloped_inv_max_dist;
-    const float lumen = opp_shade + shade / ( 1.0 + sloped_neg_range * exp(sloped_dist_ratio) );
+    const float lumen = opp_shade + shade / ( 1.0f + sloped_neg_range * exp(sloped_dist_ratio) );
     float4 fout;
     fout.rgb = fin.rgb * lumen;
     fout.w = fin.w;
-    *out = convert_uchar4(fout);
+    return convert_uchar4(fout);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh
index 7f7bdcf..5668621 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh
@@ -31,30 +31,29 @@
     else
         axis_scale.x = (float)dim_x / (float)dim_y;
 
-    const float max_dist = 0.5 * length(axis_scale);
+    const float max_dist = 0.5f * length(axis_scale);
     sloped_inv_max_dist = desired_slope * 1.f/max_dist;
 
     // Range needs to be between 1.3 to 0.6. When scale is zero then range is
     // 1.3 which means no vignette at all because the luminousity difference is
     // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
-    const float neg_range = 0.7*sqrt(desired_scale) - 1.3;
+    const float neg_range = 0.7f*sqrt(desired_scale) - 1.3f;
     sloped_neg_range = exp(neg_range * desired_slope);
 
     shade = desired_shade;
     opp_shade = 1.f - desired_shade;
 }
 
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
-    const float4 fin = convert_float4(*in);
+    const float4 fin = convert_float4(in);
     const float2 inCoord = {(float)x, (float)y};
     const float2 coord = mad(inCoord, inv_dimensions, neg_center);
     const float sloped_dist_ratio = fast_length(axis_scale * coord)  * sloped_inv_max_dist;
-    // TODO:  add half_exp once implemented
-    const float lumen = opp_shade + shade * half_recip(1.f + sloped_neg_range * exp(sloped_dist_ratio));
+    const float lumen = opp_shade + shade * half_recip(1.f + sloped_neg_range * native_exp(sloped_dist_ratio));
     float4 fout;
     fout.rgb = fin.rgb * lumen;
     fout.w = fin.w;
-    *out = convert_uchar4(fout);
+    return convert_uchar4(fout);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs
index 3612509..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.fs
index b714e9b..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
index 5fc2dda..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.fs
similarity index 87%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.fs
index 430b685..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/wbalance.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/wbalance.rs
new file mode 100644
index 0000000..6650671
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/wbalance.rs
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static int histR[256] = {0}, histG[256] = {0}, histB[256] = {0};
+
+rs_allocation histogramSource;
+uint32_t histogramHeight;
+uint32_t histogramWidth;
+
+static float scaleR;
+static float scaleG;
+static float scaleB;
+
+static uchar4 estimateWhite() {
+
+    for (int i = 0; i < 256; i++) {
+        histR[i] = 0; histG[i] = 0; histB[i] = 0;
+    }
+
+    for (uint32_t i = 0; i < histogramHeight; i++) {
+        for (uint32_t j = 0; j < histogramWidth; j++) {
+            uchar4 in = rsGetElementAt_uchar4(histogramSource, j, i);
+            histR[in.r]++;
+            histG[in.g]++;
+            histB[in.b]++;
+        }
+    }
+
+    int min_r = -1, min_g = -1, min_b = -1;
+    int max_r =  0, max_g =  0, max_b =  0;
+    int sum_r =  0, sum_g =  0, sum_b =  0;
+
+    for (int i = 1; i < 255; i++) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        sum_r += r;
+        sum_g += g;
+        sum_b += b;
+
+        if (r>0){
+            if (min_r < 0) min_r = i;
+            max_r = i;
+        }
+        if (g>0){
+            if (min_g < 0) min_g = i;
+            max_g = i;
+        }
+        if (b>0){
+            if (min_b < 0) min_b = i;
+            max_b = i;
+        }
+    }
+
+    int sum15r = 0, sum15g = 0, sum15b = 0;
+    int count15r = 0, count15g = 0, count15b = 0;
+    int tmp_r = 0, tmp_g = 0, tmp_b = 0;
+
+    for (int i = 254; i >0; i--) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        tmp_r += r;
+        tmp_g += g;
+        tmp_b += b;
+
+        if ((tmp_r > sum_r/20) && (tmp_r < sum_r/5)) {
+            sum15r += r*i;
+            count15r += r;
+        }
+        if ((tmp_g > sum_g/20) && (tmp_g < sum_g/5)) {
+            sum15g += g*i;
+            count15g += g;
+        }
+        if ((tmp_b > sum_b/20) && (tmp_b < sum_b/5)) {
+            sum15b += b*i;
+            count15b += b;
+        }
+
+    }
+
+    uchar4 out;
+
+    if ((count15r>0) && (count15g>0) && (count15b>0) ){
+        out.r = sum15r/count15r;
+        out.g = sum15g/count15g;
+        out.b = sum15b/count15b;
+    }else {
+        out.r = out.g = out.b = 255;
+    }
+
+    return out;
+
+}
+
+void prepareWhiteBalance() {
+    uchar4 estimation = estimateWhite();
+    int minimum = min(estimation.r, min(estimation.g, estimation.b));
+    int maximum = max(estimation.r, max(estimation.g, estimation.b));
+    float avg = (minimum + maximum) / 2.f;
+
+    scaleR =  avg/estimation.r;
+    scaleG =  avg/estimation.g;
+    scaleB =  avg/estimation.b;
+
+}
+
+static unsigned char contrastClamp(int c)
+{
+    int N = 255;
+    c &= ~(c >> 31);
+    c -= N;
+    c &= (c >> 31);
+    c += N;
+    return  (unsigned char) c;
+}
+
+void whiteBalanceKernel(const uchar4 *in, uchar4 *out) {
+    float Rc =  in->r*scaleR;
+    float Gc =  in->g*scaleG;
+    float Bc =  in->b*scaleB;
+
+    out->r = contrastClamp(Rc);
+    out->g = contrastClamp(Gc);
+    out->b = contrastClamp(Bc);
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/Android.mk b/tests/RenderScriptTests/ImageProcessing_jb/Android.mk
index 6cdd1c0..20d6be7 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/Android.mk
+++ b/tests/RenderScriptTests/ImageProcessing_jb/Android.mk
@@ -24,6 +24,6 @@
 #LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
 
 LOCAL_PACKAGE_NAME := ImageProcessingJB
-LOCAL_SDK_VERSION := 16
+LOCAL_SDK_VERSION := 17
 
 include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/BWFilter.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/BWFilter.java
new file mode 100644
index 0000000..4870ac4
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/BWFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+
+public class BWFilter extends TestBase {
+    private ScriptC_bwfilter mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_bwfilter(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareBwFilter(50, 50, 50);
+        mScript.forEach_bwFilterKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blend.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blend.java
new file mode 100644
index 0000000..302dc31
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blend.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 com.android.rs.imagejb;
+
+import java.lang.Math;
+import java.lang.Short;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicBlend;
+import android.renderscript.Type;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.view.View;
+import android.widget.Spinner;
+
+public class Blend extends TestBase {
+    private ScriptIntrinsicBlend mBlend;
+    private ScriptC_blend mBlendHelper;
+    private short image1Alpha = 128;
+    private short image2Alpha = 128;
+
+    String mIntrinsicNames[];
+
+    private Allocation image1;
+    private Allocation image2;
+    private int currentIntrinsic = 0;
+
+    private AdapterView.OnItemSelectedListener mIntrinsicSpinnerListener =
+            new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                    currentIntrinsic = pos;
+                    if (mRS != null) {
+                        runTest();
+                        act.updateDisplay();
+                    }
+                }
+
+                public void onNothingSelected(AdapterView parent) {
+
+                }
+            };
+
+    public void createTest(android.content.res.Resources res) {
+        mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
+        mBlendHelper = new ScriptC_blend(mRS);
+        mBlendHelper.set_alpha((short)128);
+
+        image1 = Allocation.createTyped(mRS, mInPixelsAllocation.getType());
+        image2 = Allocation.createTyped(mRS, mInPixelsAllocation2.getType());
+
+        mIntrinsicNames = new String[14];
+        mIntrinsicNames[0] = "Source";
+        mIntrinsicNames[1] = "Destination";
+        mIntrinsicNames[2] = "Source Over";
+        mIntrinsicNames[3] = "Destination Over";
+        mIntrinsicNames[4] = "Source In";
+        mIntrinsicNames[5] = "Destination In";
+        mIntrinsicNames[6] = "Source Out";
+        mIntrinsicNames[7] = "Destination Out";
+        mIntrinsicNames[8] = "Source Atop";
+        mIntrinsicNames[9] = "Destination Atop";
+        mIntrinsicNames[10] = "XOR";
+        mIntrinsicNames[11] = "Add";
+        mIntrinsicNames[12] = "Subtract";
+        mIntrinsicNames[13] = "Multiply";
+    }
+
+    public boolean onSpinner1Setup(Spinner s) {
+        s.setAdapter(new ArrayAdapter<String>(
+            act, R.layout.spinner_layout, mIntrinsicNames));
+        s.setOnItemSelectedListener(mIntrinsicSpinnerListener);
+        return true;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Image 1 Alpha");
+        b.setMax(255);
+        b.setProgress(image1Alpha);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        image1Alpha = (short)progress;
+    }
+
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Image 2 Alpha");
+        b.setMax(255);
+        b.setProgress(image2Alpha);
+        return true;
+    }
+
+    public void onBar2Changed(int progress) {
+        image2Alpha = (short)progress;
+    }
+
+    public void runTest() {
+        image1.copy2DRangeFrom(0, 0, mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), mInPixelsAllocation, 0, 0);
+        image2.copy2DRangeFrom(0, 0, mInPixelsAllocation2.getType().getX(), mInPixelsAllocation2.getType().getY(), mInPixelsAllocation2, 0, 0);
+
+        mBlendHelper.set_alpha(image1Alpha);
+        mBlendHelper.forEach_setImageAlpha(image1);
+
+        mBlendHelper.set_alpha(image2Alpha);
+        mBlendHelper.forEach_setImageAlpha(image2);
+
+        switch (currentIntrinsic) {
+        case 0:
+            mBlend.forEachSrc(image1, image2);
+            break;
+        case 1:
+            mBlend.forEachDst(image1, image2);
+            break;
+        case 2:
+            mBlend.forEachSrcOver(image1, image2);
+            break;
+        case 3:
+            mBlend.forEachDstOver(image1, image2);
+            break;
+        case 4:
+            mBlend.forEachSrcIn(image1, image2);
+            break;
+        case 5:
+            mBlend.forEachDstIn(image1, image2);
+            break;
+        case 6:
+            mBlend.forEachSrcOut(image1, image2);
+            break;
+        case 7:
+            mBlend.forEachDstOut(image1, image2);
+            break;
+        case 8:
+            mBlend.forEachSrcAtop(image1, image2);
+            break;
+        case 9:
+            mBlend.forEachDstAtop(image1, image2);
+            break;
+        case 10:
+            mBlend.forEachXor(image1, image2);
+            break;
+        case 11:
+            mBlend.forEachAdd(image1, image2);
+            break;
+        case 12:
+            mBlend.forEachSubtract(image1, image2);
+            break;
+        case 13:
+            mBlend.forEachMultiply(image1, image2);
+            break;
+        }
+
+        mOutPixelsAllocation.copy2DRangeFrom(0, 0, image2.getType().getX(), image2.getType().getY(), image2, 0, 0);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blur25.java
index d7e918b..90acd00 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blur25.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Blur25.java
@@ -21,12 +21,16 @@
 import android.renderscript.Allocation;
 import android.renderscript.Element;
 import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicBlur;
 import android.renderscript.Type;
 import android.util.Log;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 public class Blur25 extends TestBase {
+    private boolean mUseIntrinsic = false;
+    private ScriptIntrinsicBlur mIntrinsic;
+
     private int MAX_RADIUS = 25;
     private ScriptC_threshold mScript;
     private float mRadius = MAX_RADIUS;
@@ -35,7 +39,8 @@
     private Allocation mScratchPixelsAllocation2;
 
 
-    public Blur25() {
+    public Blur25(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
     }
 
     public boolean onBar1Setup(SeekBar b, TextView t) {
@@ -50,7 +55,11 @@
         if (mRadius <= 0.10f) {
             mRadius = 0.10f;
         }
-        mScript.invoke_setRadius((int)mRadius);
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(mRadius);
+        } else {
+            mScript.invoke_setRadius((int)mRadius);
+        }
     }
 
 
@@ -58,33 +67,52 @@
         int width = mInPixelsAllocation.getType().getX();
         int height = mInPixelsAllocation.getType().getY();
 
-        Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
-        tb.setX(width);
-        tb.setY(height);
-        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
-        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setRadius(MAX_RADIUS);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
 
-        mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
-        mScript.set_width(width);
-        mScript.set_height(height);
-        mScript.invoke_setRadius(MAX_RADIUS);
+            Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+            tb.setX(width);
+            tb.setY(height);
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+            mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
 
-        mScript.set_InPixel(mInPixelsAllocation);
-        mScript.set_ScratchPixel1(mScratchPixelsAllocation1);
-        mScript.set_ScratchPixel2(mScratchPixelsAllocation2);
+            mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
+            mScript.set_width(width);
+            mScript.set_height(height);
+            mScript.invoke_setRadius(MAX_RADIUS);
+
+            mScript.set_InPixel(mInPixelsAllocation);
+            mScript.set_ScratchPixel1(mScratchPixelsAllocation1);
+            mScript.set_ScratchPixel2(mScratchPixelsAllocation2);
+        }
     }
 
     public void runTest() {
-        mScript.forEach_copyIn(mInPixelsAllocation, mScratchPixelsAllocation1);
-        mScript.forEach_horz(mScratchPixelsAllocation2);
-        mScript.forEach_vert(mOutPixelsAllocation);
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_copyIn(mInPixelsAllocation, mScratchPixelsAllocation1);
+            mScript.forEach_horz(mScratchPixelsAllocation2);
+            mScript.forEach_vert(mOutPixelsAllocation);
+        }
     }
 
     public void setupBenchmark() {
-        mScript.invoke_setRadius(MAX_RADIUS);
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(MAX_RADIUS);
+        } else {
+            mScript.invoke_setRadius(MAX_RADIUS);
+        }
     }
 
     public void exitBenchmark() {
-        mScript.invoke_setRadius((int)mRadius);
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(mRadius);
+        } else {
+            mScript.invoke_setRadius((int)mRadius);
+        }
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ColorMatrix.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ColorMatrix.java
index 62ca694..5053d4d 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ColorMatrix.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ColorMatrix.java
@@ -24,14 +24,19 @@
 import android.renderscript.RenderScript;
 import android.renderscript.Script;
 import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicColorMatrix;
 import android.renderscript.Type;
 import android.util.Log;
 
 public class ColorMatrix extends TestBase {
     private ScriptC_colormatrix mScript;
+    private ScriptIntrinsicColorMatrix mIntrinsic;
+    private boolean mUseIntrinsic;
     private boolean mUseGrey;
 
-    public ColorMatrix(boolean useGrey) {
+    public ColorMatrix(boolean useIntrinsic, boolean useGrey) {
+        mUseIntrinsic = useIntrinsic;
         mUseGrey = useGrey;
     }
 
@@ -41,12 +46,25 @@
         m.set(1, 1, 0.9f);
         m.set(1, 2, 0.2f);
 
-        mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
-        mScript.invoke_setMatrix(m);
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+            if (mUseGrey) {
+                mIntrinsic.setGreyscale();
+            } else {
+                mIntrinsic.setColorMatrix(m);
+            }
+        } else {
+            mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+            mScript.invoke_setMatrix(m);
+        }
     }
 
     public void runTest() {
-        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        }
     }
 
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Contrast.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Contrast.java
new file mode 100644
index 0000000..20b28ff
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Contrast.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+
+public class Contrast extends TestBase {
+    private ScriptC_contrast mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_contrast(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_contrast(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve3x3.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve3x3.java
index 6673032..d7acf4a 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve3x3.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve3x3.java
@@ -24,16 +24,21 @@
 import android.renderscript.RenderScript;
 import android.renderscript.Script;
 import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
 import android.renderscript.Type;
 import android.util.Log;
 
 public class Convolve3x3 extends TestBase {
     private ScriptC_convolve3x3 mScript;
+    private ScriptIntrinsicConvolve3x3 mIntrinsic;
 
     private int mWidth;
     private int mHeight;
+    private boolean mUseIntrinsic;
 
-    public Convolve3x3() {
+    public Convolve3x3(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
     }
 
     public void createTest(android.content.res.Resources res) {
@@ -45,11 +50,17 @@
         f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
         f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
 
-        mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
-        mScript.set_gCoeffs(f);
-        mScript.set_gIn(mInPixelsAllocation);
-        mScript.set_gWidth(mWidth);
-        mScript.set_gHeight(mHeight);
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setCoefficients(f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+            mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+            mScript.set_gCoeffs(f);
+            mScript.set_gIn(mInPixelsAllocation);
+            mScript.set_gWidth(mWidth);
+            mScript.set_gHeight(mHeight);
+        }
     }
 
     public void runTest() {
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve5x5.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve5x5.java
index 895d459..d1dbb1f 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve5x5.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Convolve5x5.java
@@ -24,16 +24,21 @@
 import android.renderscript.RenderScript;
 import android.renderscript.Script;
 import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicConvolve5x5;
 import android.renderscript.Type;
 import android.util.Log;
 
 public class Convolve5x5 extends TestBase {
     private ScriptC_convolve5x5 mScript;
+    private ScriptIntrinsicConvolve5x5 mIntrinsic;
 
     private int mWidth;
     private int mHeight;
+    private boolean mUseIntrinsic;
 
-    public Convolve5x5() {
+    public Convolve5x5(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
     }
 
     public void createTest(android.content.res.Resources res) {
@@ -59,15 +64,25 @@
         f[15]= -3.f; f[16]=  0.f; f[17]=  6.f; f[18]=  0.f; f[19]= -3.f;
         f[20]= -1.f; f[21]= -3.f; f[22]= -4.f; f[23]= -3.f; f[24]= -1.f;
 
-        mScript = new ScriptC_convolve5x5(mRS, res, R.raw.convolve5x5);
-        mScript.set_gCoeffs(f);
-        mScript.set_gIn(mInPixelsAllocation);
-        mScript.set_gWidth(mWidth);
-        mScript.set_gHeight(mHeight);
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setCoefficients(f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+            mScript = new ScriptC_convolve5x5(mRS, res, R.raw.convolve5x5);
+            mScript.set_gCoeffs(f);
+            mScript.set_gIn(mInPixelsAllocation);
+            mScript.set_gWidth(mWidth);
+            mScript.set_gHeight(mHeight);
+        }
     }
 
     public void runTest() {
-        mScript.forEach_root(mOutPixelsAllocation);
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mOutPixelsAllocation);
+        }
     }
 
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/CrossProcess.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/CrossProcess.java
new file mode 100644
index 0000000..75ee39b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/CrossProcess.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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicLUT;
+import android.util.Log;
+
+public class CrossProcess extends TestBase {
+    private ScriptIntrinsicLUT mIntrinsic;
+
+    public void createTest(android.content.res.Resources res) {
+        mIntrinsic = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+        for (int ct=0; ct < 256; ct++) {
+            float f = ((float)ct) / 255.f;
+
+            float r = f;
+            if (r < 0.5f) {
+                r = 4.0f * r * r * r;
+            } else {
+                r = 1.0f - r;
+                r = 1.0f - (4.0f * r * r * r);
+            }
+            mIntrinsic.setRed(ct, (int)(r * 255.f + 0.5f));
+
+            float g = f;
+            if (g < 0.5f) {
+                g = 2.0f * g * g;
+            } else {
+                g = 1.0f - g;
+                g = 1.0f - (2.0f * g * g);
+            }
+            mIntrinsic.setGreen(ct, (int)(g * 255.f + 0.5f));
+
+            float b = f * 0.5f + 0.25f;
+            mIntrinsic.setBlue(ct, (int)(b * 255.f + 0.5f));
+        }
+
+    }
+
+    public void runTest() {
+        mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Exposure.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Exposure.java
new file mode 100644
index 0000000..ddde96f
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Exposure.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+
+public class Exposure extends TestBase {
+    private ScriptC_exposure mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_exposure(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_setBright(50.f);
+        mScript.forEach_exposure(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Fisheye.java
new file mode 100644
index 0000000..114839c
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Fisheye.java
@@ -0,0 +1,138 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Sampler;
+import android.renderscript.Type;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Fisheye extends TestBase {
+    private ScriptC_fisheye_full mScript_full = null;
+    private ScriptC_fisheye_relaxed mScript_relaxed = null;
+    private ScriptC_fisheye_approx_full mScript_approx_full = null;
+    private ScriptC_fisheye_approx_relaxed mScript_approx_relaxed = null;
+    private final boolean approx;
+    private final boolean relaxed;
+    private float center_x = 0.5f;
+    private float center_y = 0.5f;
+    private float scale = 0.5f;
+
+    public Fisheye(boolean approx, boolean relaxed) {
+        this.approx = approx;
+        this.relaxed = relaxed;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Scale");
+        b.setMax(100);
+        b.setProgress(25);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Shift center X");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Shift center Y");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        scale = progress / 50.0f;
+        do_init();
+    }
+    public void onBar2Changed(int progress) {
+        center_x = progress / 100.0f;
+        do_init();
+    }
+    public void onBar3Changed(int progress) {
+        center_y = progress / 100.0f;
+        do_init();
+    }
+
+    private void do_init() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+            else
+                mScript_approx_full.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+        } else if (relaxed)
+            mScript_relaxed.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+        else
+            mScript_full.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        if (approx) {
+            if (relaxed) {
+                mScript_approx_relaxed = new ScriptC_fisheye_approx_relaxed(mRS,
+                        res, R.raw.fisheye_approx_relaxed);
+                mScript_approx_relaxed.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            } else {
+                mScript_approx_full = new ScriptC_fisheye_approx_full(mRS, res,
+                        R.raw.fisheye_approx_full);
+                mScript_approx_full.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            }
+        } else if (relaxed) {
+            mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
+                    R.raw.fisheye_relaxed);
+            mScript_relaxed.set_in_alloc(mInPixelsAllocation);
+            mScript_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        } else {
+            mScript_full = new ScriptC_fisheye_full(mRS, res,
+                    R.raw.fisheye_full);
+            mScript_full.set_in_alloc(mInPixelsAllocation);
+            mScript_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        }
+        do_init();
+    }
+
+    public void runTest() {
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.forEach_root(mOutPixelsAllocation);
+            else
+                mScript_approx_full.forEach_root(mOutPixelsAllocation);
+        } else if (relaxed)
+            mScript_relaxed.forEach_root(mOutPixelsAllocation);
+        else
+            mScript_full.forEach_root(mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/GroupTest.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/GroupTest.java
new file mode 100644
index 0000000..3e5175a
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/GroupTest.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.
+ */
+
+package com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.Type;
+import android.renderscript.Matrix4f;
+import android.renderscript.ScriptGroup;
+import android.util.Log;
+
+public class GroupTest extends TestBase {
+    private ScriptIntrinsicConvolve3x3 mConvolve;
+    private ScriptIntrinsicColorMatrix mMatrix;
+
+    private Allocation mScratchPixelsAllocation1;
+    private ScriptGroup mGroup;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseNative;
+
+
+    public GroupTest(boolean useNative) {
+        mUseNative = useNative;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        mConvolve = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+        mMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+        mConvolve.setCoefficients(f);
+
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+        mMatrix.setColorMatrix(m);
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(mWidth);
+        tb.setY(mHeight);
+        Type connect = tb.create();
+
+        if (mUseNative) {
+            ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+            b.addKernel(mConvolve.getKernelID());
+            b.addKernel(mMatrix.getKernelID());
+            b.addConnection(connect, mConvolve.getKernelID(), mMatrix.getKernelID());
+            mGroup = b.create();
+        } else {
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect);
+        }
+    }
+
+    public void runTest() {
+        mConvolve.setInput(mInPixelsAllocation);
+        if (mUseNative) {
+            mGroup.setOutput(mMatrix.getKernelID(), mOutPixelsAllocation);
+            mGroup.execute();
+        } else {
+            mConvolve.forEach(mScratchPixelsAllocation1);
+            mMatrix.forEach(mScratchPixelsAllocation1, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ImageProcessingActivityJB.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ImageProcessingActivityJB.java
index b3b06d4..93937ef 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ImageProcessingActivityJB.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ImageProcessingActivityJB.java
@@ -23,14 +23,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.renderscript.ScriptC;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Script;
 import android.view.SurfaceView;
-import android.view.SurfaceHolder;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
@@ -39,13 +32,14 @@
 import android.widget.TextView;
 import android.view.View;
 import android.util.Log;
-import java.lang.Math;
+import android.renderscript.ScriptC;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Script;
 
 import android.os.Environment;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -54,12 +48,70 @@
 public class ImageProcessingActivityJB extends Activity
                                        implements SeekBar.OnSeekBarChangeListener {
     private final String TAG = "Img";
-    private final String RESULT_FILE = "image_processing_result.csv";
+    public final String RESULT_FILE = "image_processing_result.csv";
+
+    RenderScript mRS;
+    Allocation mInPixelsAllocation;
+    Allocation mInPixelsAllocation2;
+    Allocation mOutPixelsAllocation;
+
+    /**
+     * Define enum type for test names
+     */
+    public enum TestName {
+        // totally there are 38 test cases
+        LEVELS_VEC3_RELAXED ("Levels Vec3 Relaxed"),
+        LEVELS_VEC4_RELAXED ("Levels Vec4 Relaxed"),
+        LEVELS_VEC3_FULL ("Levels Vec3 Full"),
+        LEVELS_VEC4_FULL ("Levels Vec4 Full"),
+        BLUR_RADIUS_25 ("Blur radius 25"),
+        INTRINSIC_BLUE_RADIUS_25 ("Intrinsic Blur radius 25"),
+        GREYSCALE ("Greyscale"),
+        GRAIN ("Grain"),
+        FISHEYE_FULL ("Fisheye Full"),
+        FISHEYE_RELAXED ("Fisheye Relaxed"),
+        FISHEYE_APPROXIMATE_FULL ("Fisheye Approximate Full"),
+        FISHEYE_APPROXIMATE_RELAXED ("Fisheye Approximate Relaxed"),
+        VIGNETTE_FULL ("Vignette Full"),
+        VIGNETTE_RELAXED ("Vignette Relaxed"),
+        VIGNETTE_APPROXIMATE_FULL ("Vignette Approximate Full"),
+        VIGNETTE_APPROXIMATE_RELAXED ("Vignette Approximate Relaxed"),
+        GROUP_TEST_EMULATED ("Group Test (emulated)"),
+        GROUP_TEST_NATIVE ("Group Test (native)"),
+        CONVOLVE_3X3 ("Convolve 3x3"),
+        INTRINSICS_CONVOLVE_3X3 ("Intrinsics Convolve 3x3"),
+        COLOR_MATRIX ("ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX ("Intrinsics ColorMatrix"),
+        INTRINSICS_COLOR_MATRIX_GREY ("Intrinsics ColorMatrix Grey"),
+        COPY ("Copy"),
+        CROSS_PROCESS_USING_LUT ("CrossProcess (using LUT)"),
+        CONVOLVE_5X5 ("Convolve 5x5"),
+        INTRINSICS_CONVOLVE_5X5 ("Intrinsics Convolve 5x5"),
+        MANDELBROT ("Mandelbrot"),
+        INTRINSICS_BLEND ("Intrinsics Blend"),
+        VIBRANCE ("Vibrance"),
+        BW_FILTER ("BW Filter"),
+        SHADOWS ("Shadows"),
+        CONTRAST ("Contrast"),
+        EXPOSURE ("Exposure"),
+        WHITE_BALANCE ("White Balance");
+
+
+        private final String name;
+
+        private TestName(String s) {
+            name = s;
+        }
+
+        // return quoted string as displayed test name
+        public String toString() {
+            return name;
+        }
+    }
 
     Bitmap mBitmapIn;
     Bitmap mBitmapIn2;
     Bitmap mBitmapOut;
-    String mTestNames[];
 
     private Spinner mSpinner;
     private SeekBar mBar1;
@@ -91,12 +143,15 @@
     }
 
     private Handler mHandler = new Handler() {
+        // Allow the filter to complete without blocking the UI
+        // thread.  When the message arrives that the op is complete
+        // we will either mark completion or start a new filter if
+        // more work is ready.  Either way, display the result.
         @Override
         public void handleMessage(Message msg) {
             mTest.updateBitmap(mBitmapOut);
             mDisplayView.invalidate();
 
-            android.util.Log.v("Img", "mRunCount hdl " + mRunCount);
             boolean doTest = false;
             synchronized(this) {
                 if (mRunCount > 0) {
@@ -175,56 +230,119 @@
     }
 
 
-    void changeTest(int testID) {
+    void changeTest(TestName testName) {
         if (mTest != null) {
             mTest.destroy();
         }
-        switch(testID) {
-        case 0:
+        switch(testName) {
+        case LEVELS_VEC3_RELAXED:
             mTest = new LevelsV4(false, false);
             break;
-        case 1:
+        case LEVELS_VEC4_RELAXED:
             mTest = new LevelsV4(false, true);
             break;
-        case 2:
+        case LEVELS_VEC3_FULL:
             mTest = new LevelsV4(true, false);
             break;
-        case 3:
+        case LEVELS_VEC4_FULL:
             mTest = new LevelsV4(true, true);
             break;
-        case 4:
-            mTest = new Blur25();
+        case BLUR_RADIUS_25:
+            mTest = new Blur25(false);
             break;
-        case 5:
+        case INTRINSIC_BLUE_RADIUS_25:
+            mTest = new Blur25(true);
+            break;
+        case GREYSCALE:
             mTest = new Greyscale();
             break;
-        case 6:
+        case GRAIN:
             mTest = new Grain();
             break;
-        case 7:
-            mTest = new Vignette(false);
+        case FISHEYE_FULL:
+            mTest = new Fisheye(false, false);
             break;
-        case 8:
-            mTest = new Vignette(true);
+        case FISHEYE_RELAXED:
+            mTest = new Fisheye(false, true);
             break;
-        case 9:
-            mTest = new Convolve3x3();
+        case FISHEYE_APPROXIMATE_FULL:
+            mTest = new Fisheye(true, false);
             break;
-        case 10:
-            mTest = new ColorMatrix(false);
+        case FISHEYE_APPROXIMATE_RELAXED:
+            mTest = new Fisheye(true, true);
             break;
-        case 11:
+        case VIGNETTE_FULL:
+            mTest = new Vignette(false, false);
+            break;
+        case VIGNETTE_RELAXED:
+            mTest = new Vignette(false, true);
+            break;
+        case VIGNETTE_APPROXIMATE_FULL:
+            mTest = new Vignette(true, false);
+            break;
+        case VIGNETTE_APPROXIMATE_RELAXED:
+            mTest = new Vignette(true, true);
+            break;
+        case GROUP_TEST_EMULATED:
+            mTest = new GroupTest(false);
+            break;
+        case GROUP_TEST_NATIVE:
+            mTest = new GroupTest(true);
+            break;
+        case CONVOLVE_3X3:
+            mTest = new Convolve3x3(false);
+            break;
+        case INTRINSICS_CONVOLVE_3X3:
+            mTest = new Convolve3x3(true);
+            break;
+        case COLOR_MATRIX:
+            mTest = new ColorMatrix(false, false);
+            break;
+        case INTRINSICS_COLOR_MATRIX:
+            mTest = new ColorMatrix(true, false);
+            break;
+        case INTRINSICS_COLOR_MATRIX_GREY:
+            mTest = new ColorMatrix(true, true);
+            break;
+        case COPY:
             mTest = new Copy();
             break;
-        case 12:
-            mTest = new Convolve5x5();
+        case CROSS_PROCESS_USING_LUT:
+            mTest = new CrossProcess();
             break;
-        case 13:
+        case CONVOLVE_5X5:
+            mTest = new Convolve5x5(false);
+            break;
+        case INTRINSICS_CONVOLVE_5X5:
+            mTest = new Convolve5x5(true);
+            break;
+        case MANDELBROT:
             mTest = new Mandelbrot();
             break;
+        case INTRINSICS_BLEND:
+            mTest = new Blend();
+            break;
+        case VIBRANCE:
+            mTest = new Vibrance();
+            break;
+        case BW_FILTER:
+            mTest = new BWFilter();
+            break;
+        case SHADOWS:
+            mTest = new Shadows();
+            break;
+        case CONTRAST:
+            mTest = new Contrast();
+            break;
+        case EXPOSURE:
+            mTest = new Exposure();
+            break;
+        case WHITE_BALANCE:
+            mTest = new WhiteBalance();
+            break;
         }
 
-        mTest.createBaseTest(this, mBitmapIn, mBitmapIn2);
+        mTest.createBaseTest(this, mBitmapIn, mBitmapIn2, mBitmapOut);
         setupBars();
 
         mTest.runTest();
@@ -233,30 +351,14 @@
     }
 
     void setupTests() {
-        mTestNames = new String[14];
-        mTestNames[0] = "Levels Vec3 Relaxed";
-        mTestNames[1] = "Levels Vec4 Relaxed";
-        mTestNames[2] = "Levels Vec3 Full";
-        mTestNames[3] = "Levels Vec4 Full";
-        mTestNames[4] = "Blur radius 25";
-        mTestNames[5] = "Greyscale";
-        mTestNames[6] = "Grain";
-        mTestNames[7] = "Vignette Full";
-        mTestNames[8] = "Vignette Relaxed";
-        mTestNames[9] = "Convolve 3x3";
-        mTestNames[10] = "ColorMatrix";
-        mTestNames[11] = "Copy";
-        mTestNames[12] = "Convolve 5x5";
-        mTestNames[13] = "Mandelbrot";
-
-        mTestSpinner.setAdapter(new ArrayAdapter<String>(
-            this, R.layout.spinner_layout, mTestNames));
+        mTestSpinner.setAdapter(new ArrayAdapter<TestName>(
+            this, R.layout.spinner_layout, TestName.values()));
     }
 
     private AdapterView.OnItemSelectedListener mTestSpinnerListener =
             new AdapterView.OnItemSelectedListener() {
                 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
-                    changeTest(pos);
+                    changeTest(TestName.values()[pos]);
                 }
 
                 public void onNothingSelected(AdapterView parent) {
@@ -305,8 +407,15 @@
         mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
         mBenchmarkResult.setText("Result: not run");
 
+
+        mRS = RenderScript.create(this);
+        mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+        mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, mBitmapIn2);
+        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut);
+
+
         setupTests();
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
 
@@ -337,10 +446,10 @@
         try {
             BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
             Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
-            for (int i = 0; i < mTestNames.length; i++ ) {
-                changeTest(i);
+            for (TestName tn: TestName.values()) {
+                changeTest(tn);
                 float t = getBenchmark();
-                String s = new String("" + mTestNames[i] + ", " + t);
+                String s = new String("" + tn.toString() + ", " + t);
                 rsWriter.write(s + "\n");
                 Log.v(TAG, "Test " + s + "ms\n");
             }
@@ -348,7 +457,7 @@
         } catch (IOException e) {
             Log.v(TAG, "Unable to write result file " + e.getMessage());
         }
-        changeTest(0);
+        changeTest(TestName.LEVELS_VEC3_RELAXED);
     }
 
     // For benchmark test
@@ -365,7 +474,6 @@
             mTest.finish();
         } while (t > java.lang.System.currentTimeMillis());
 
-
         //Log.v(TAG, "Benchmarking");
         int ct = 0;
         t = java.lang.System.currentTimeMillis();
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Shadows.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Shadows.java
new file mode 100644
index 0000000..d246d59
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Shadows.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+
+public class Shadows extends TestBase {
+    private ScriptC_shadows mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_shadows(mRS);
+    }
+
+    public void runTest() {
+        mScript.invoke_prepareShadows(50.f);
+        mScript.forEach_shadowsKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/TestBase.java
index ed22578..9ae366a 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/TestBase.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/TestBase.java
@@ -106,26 +106,20 @@
         return false;
     }
 
-    public final void createBaseTest(ImageProcessingActivityJB ipact, Bitmap b, Bitmap b2) {
+    public final void createBaseTest(ImageProcessingActivityJB ipact, Bitmap b, Bitmap b2, Bitmap outb) {
         act = ipact;
-        mRS = RenderScript.create(act);
+        mRS = ipact.mRS;
         mRS.setMessageHandler(new MessageProcessor(act));
-        mMessageScript = new ScriptC_msg(mRS);
-        mInPixelsAllocation = Allocation.createFromBitmap(mRS, b,
-                                                          Allocation.MipmapControl.MIPMAP_NONE,
-                                                          Allocation.USAGE_SCRIPT);
-        mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2,
-                                                          Allocation.MipmapControl.MIPMAP_NONE,
-                                                          Allocation.USAGE_SCRIPT);
-        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, b,
-                                                           Allocation.MipmapControl.MIPMAP_NONE,
-                                                           Allocation.USAGE_SCRIPT);
+
+        mInPixelsAllocation = ipact.mInPixelsAllocation;
+        mInPixelsAllocation2 = ipact.mInPixelsAllocation2;
+        mOutPixelsAllocation = ipact.mOutPixelsAllocation;
+
         createTest(act.getResources());
     }
 
     // Must override
     public void createTest(android.content.res.Resources res) {
-        android.util.Log.e("img", "implement createTest");
     }
 
     // Must override
@@ -133,7 +127,6 @@
     }
 
     final public void runTestSendMessage() {
-        android.util.Log.v("Img", "run");
         runTest();
         mMessageScript.invoke_sendMsg();
     }
@@ -143,8 +136,7 @@
     }
 
     public void destroy() {
-        mRS.destroy();
-        mRS = null;
+        mRS.setMessageHandler(null);
     }
 
     public void updateBitmap(Bitmap b) {
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vibrance.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vibrance.java
new file mode 100644
index 0000000..09822a9
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vibrance.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+
+public class Vibrance extends TestBase {
+    private ScriptC_vibrance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_vibrance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_vibrance(50.f);
+        mScript.invoke_prepareVibrance();
+        mScript.forEach_vibranceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vignette.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vignette.java
index 487cd63..9451757 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vignette.java
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/Vignette.java
@@ -26,6 +26,9 @@
 public class Vignette extends TestBase {
     private ScriptC_vignette_full mScript_full = null;
     private ScriptC_vignette_relaxed mScript_relaxed = null;
+    private ScriptC_vignette_approx_full mScript_approx_full = null;
+    private ScriptC_vignette_approx_relaxed mScript_approx_relaxed = null;
+    private final boolean approx;
     private final boolean relaxed;
     private float center_x = 0.5f;
     private float center_y = 0.5f;
@@ -33,7 +36,8 @@
     private float shade = 0.5f;
     private float slope = 20.0f;
 
-    public Vignette(boolean relaxed) {
+    public Vignette(boolean approx, boolean relaxed) {
+        this.approx = approx;
         this.relaxed = relaxed;
     }
 
@@ -90,7 +94,18 @@
     }
 
     private void do_init() {
-        if (relaxed)
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.invoke_init_vignette(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale, shade, slope);
+            else
+                mScript_approx_full.invoke_init_vignette(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale, shade, slope);
+        } else if (relaxed)
             mScript_relaxed.invoke_init_vignette(
                     mInPixelsAllocation.getType().getX(),
                     mInPixelsAllocation.getType().getY(), center_x, center_y,
@@ -103,7 +118,14 @@
     }
 
     public void createTest(android.content.res.Resources res) {
-        if (relaxed)
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed = new ScriptC_vignette_approx_relaxed(
+                        mRS, res, R.raw.vignette_approx_relaxed);
+            else
+                mScript_approx_full = new ScriptC_vignette_approx_full(
+                        mRS, res, R.raw.vignette_approx_full);
+        } else if (relaxed)
             mScript_relaxed = new ScriptC_vignette_relaxed(mRS, res,
                     R.raw.vignette_relaxed);
         else
@@ -113,7 +135,14 @@
     }
 
     public void runTest() {
-        if (relaxed)
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.forEach_root(mInPixelsAllocation,
+                        mOutPixelsAllocation);
+            else
+                mScript_approx_full.forEach_root(mInPixelsAllocation,
+                        mOutPixelsAllocation);
+        } else if (relaxed)
             mScript_relaxed.forEach_root(mInPixelsAllocation,
                     mOutPixelsAllocation);
         else
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/WhiteBalance.java b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/WhiteBalance.java
new file mode 100644
index 0000000..f15aaf5b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/WhiteBalance.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.rs.imagejb;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+
+public class WhiteBalance extends TestBase {
+    private ScriptC_wbalance mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_wbalance(mRS);
+    }
+
+    public void runTest() {
+        mScript.set_histogramSource(mInPixelsAllocation);
+        mScript.set_histogramWidth(mInPixelsAllocation.getType().getX());
+        mScript.set_histogramHeight(mInPixelsAllocation.getType().getY());
+        mScript.invoke_prepareWhiteBalance();
+        mScript.forEach_whiteBalanceKernel(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/blend.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/blend.rs
new file mode 100644
index 0000000..9ec1246
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/blend.rs
@@ -0,0 +1,23 @@
+// 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.
+
+#include "ip.rsh"
+
+uchar alpha = 0x0;
+
+void setImageAlpha(uchar4 *v_out, uint32_t x, uint32_t y) {
+  v_out->rgba = convert_uchar4((convert_uint4(v_out->rgba) * alpha) >> (uint4)8);
+  v_out->a = alpha;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/bwfilter.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/bwfilter.rs
new file mode 100644
index 0000000..e706d44
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/bwfilter.rs
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static float sr = 0.f;
+static float sg = 0.f;
+static float sb = 0.f;
+
+void prepareBwFilter(uint32_t rw, uint32_t gw, uint32_t bw) {
+
+    sr = rw;
+    sg = gw;
+    sb = bw;
+
+    float imageMin = min(sg,sb);
+    imageMin = fmin(sr,imageMin);
+    float imageMax = max(sg,sb);
+    imageMax = fmax(sr,imageMax);
+    float avg = (imageMin + imageMax)/2;
+    sb /= avg;
+    sg /= avg;
+    sr /= avg;
+
+}
+
+void bwFilterKernel(const uchar4 *in, uchar4 *out) {
+    float r = in->r * sr;
+    float g = in->g * sg;
+    float b = in->b * sb;
+    float localMin, localMax, avg;
+    localMin = fmin(g,b);
+    localMin = fmin(r,localMin);
+    localMax = fmax(g,b);
+    localMax = fmax(r,localMax);
+    avg = (localMin+localMax) * 0.5f;
+    out->r = out->g = out->b = rsClamp(avg, 0, 255);
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.fs
similarity index 79%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.fs
index e93bef3..86fb248 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.fs
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
-
+#include "ip.rsh"
 
 static rs_matrix4x4 Mat;
 
@@ -29,10 +26,10 @@
     Mat = m;
 }
 
-void root(const uchar4 *in, uchar4 *out) {
-    float4 f = convert_float4(*in);
+uchar4 __attribute__((kernel)) root(uchar4 in) {
+    float4 f = convert_float4(in);
     f = rsMatrixMultiply(&Mat, f);
     f = clamp(f, 0.f, 255.f);
-    *out = convert_uchar4(f);
+    return convert_uchar4(f);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.rs
deleted file mode 100644
index 772cb83..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/colormatrix.rs
+++ /dev/null
@@ -1,38 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
-
-
-static rs_matrix4x4 Mat;
-
-void init() {
-    rsMatrixLoadIdentity(&Mat);
-}
-
-void setMatrix(rs_matrix4x4 m) {
-    Mat = m;
-}
-
-void root(const uchar4 *in, uchar4 *out) {
-    float4 f = convert_float4(*in);
-    f = rsMatrixMultiply(&Mat, f);
-    f = clamp(f, 0.f, 255.f);
-    *out = convert_uchar4(f);
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/contrast.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/contrast.rs
new file mode 100644
index 0000000..d3743d3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/contrast.rs
@@ -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.
+ */
+
+#include "ip.rsh"
+
+static float brightM = 0.f;
+static float brightC = 0.f;
+
+void setBright(float v) {
+    brightM = pow(2.f, v / 100.f);
+    brightC = 127.f - brightM * 127.f;
+}
+
+void contrast(const uchar4 *in, uchar4 *out)
+{
+#if 0
+    out->r = rsClamp((int)(brightM * in->r + brightC), 0, 255);
+    out->g = rsClamp((int)(brightM * in->g + brightC), 0, 255);
+    out->b = rsClamp((int)(brightM * in->b + brightC), 0, 255);
+#else
+    float3 v = convert_float3(in->rgb) * brightM + brightC;
+    out->rgb = convert_uchar3(clamp(v, 0.f, 255.f));
+#endif
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.fs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.fs
new file mode 100644
index 0000000..177e86e
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.fs
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[9];
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    uint32_t x1 = min((int32_t)x+1, gWidth-1);
+    uint32_t x2 = max((int32_t)x-1, 0);
+    uint32_t y1 = min((int32_t)y+1, gHeight-1);
+    uint32_t y2 = max((int32_t)y-1, 0);
+
+    float4 p00 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y1));
+    float4 p01 = convert_float4(rsGetElementAt_uchar4(gIn, x, y1));
+    float4 p02 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y1));
+    float4 p10 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y));
+    float4 p11 = convert_float4(rsGetElementAt_uchar4(gIn, x, y));
+    float4 p12 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y));
+    float4 p20 = convert_float4(rsGetElementAt_uchar4(gIn, x1, y2));
+    float4 p21 = convert_float4(rsGetElementAt_uchar4(gIn, x, y2));
+    float4 p22 = convert_float4(rsGetElementAt_uchar4(gIn, x2, y2));
+    p00 *= gCoeffs[0];
+    p01 *= gCoeffs[1];
+    p02 *= gCoeffs[2];
+    p10 *= gCoeffs[3];
+    p11 *= gCoeffs[4];
+    p12 *= gCoeffs[5];
+    p20 *= gCoeffs[6];
+    p21 *= gCoeffs[7];
+    p22 *= gCoeffs[8];
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    p20 = clamp(p20, 0.f, 255.f);
+    return convert_uchar4(p20);
+}
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.rs
deleted file mode 100644
index f12f6ce..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve3x3.rs
+++ /dev/null
@@ -1,71 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
-
-int32_t gWidth;
-int32_t gHeight;
-rs_allocation gIn;
-
-float gCoeffs[9];
-
-static inline uchar4 GetElementAt_uchar4(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((uchar4 *)rsGetElementAt(a, x, y))[0];
-}
-
-void root(uchar4 *out, uint32_t x, uint32_t y) {
-    uint32_t x1 = min((int32_t)x+1, gWidth-1);
-    uint32_t x2 = max((int32_t)x-1, 0);
-    uint32_t y1 = min((int32_t)y+1, gHeight-1);
-    uint32_t y2 = max((int32_t)y-1, 0);
-
-    float4 p00 = convert_float4(GetElementAt_uchar4(gIn, x1, y1));
-    float4 p01 = convert_float4(GetElementAt_uchar4(gIn, x, y1));
-    float4 p02 = convert_float4(GetElementAt_uchar4(gIn, x2, y1));
-    float4 p10 = convert_float4(GetElementAt_uchar4(gIn, x1, y));
-    float4 p11 = convert_float4(GetElementAt_uchar4(gIn, x, y));
-    float4 p12 = convert_float4(GetElementAt_uchar4(gIn, x2, y));
-    float4 p20 = convert_float4(GetElementAt_uchar4(gIn, x1, y2));
-    float4 p21 = convert_float4(GetElementAt_uchar4(gIn, x, y2));
-    float4 p22 = convert_float4(GetElementAt_uchar4(gIn, x2, y2));
-    p00 *= gCoeffs[0];
-    p01 *= gCoeffs[1];
-    p02 *= gCoeffs[2];
-    p10 *= gCoeffs[3];
-    p11 *= gCoeffs[4];
-    p12 *= gCoeffs[5];
-    p20 *= gCoeffs[6];
-    p21 *= gCoeffs[7];
-    p22 *= gCoeffs[8];
-
-    p00 += p01;
-    p02 += p10;
-    p11 += p12;
-    p20 += p21;
-
-    p22 += p00;
-    p02 += p11;
-
-    p20 += p22;
-    p20 += p02;
-
-    p20 = clamp(p20, 0.f, 255.f);
-    *out = convert_uchar4(p20);
-}
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.fs
similarity index 93%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.fs
index b1ad241..922a593 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 int32_t gWidth;
 int32_t gHeight;
@@ -24,7 +22,7 @@
 
 float gCoeffs[25];
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
     uint32_t x0 = max((int32_t)x-2, 0);
     uint32_t x1 = max((int32_t)x-1, 0);
     uint32_t x2 = x;
@@ -68,8 +66,7 @@
               + convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
 
     p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
-    p0.a = 255.f;
-    *out = convert_uchar4(p0);
+    return convert_uchar4(p0);
 }
 
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.rs
deleted file mode 100644
index 6e23d79..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/convolve5x5.rs
+++ /dev/null
@@ -1,78 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
-
-int32_t gWidth;
-int32_t gHeight;
-rs_allocation gIn;
-
-float gCoeffs[25];
-
-static inline uchar4 GetElementAt_uchar4(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((uchar4 *)rsGetElementAt(a, x, y))[0];
-}
-
-void root(uchar4 *out, uint32_t x, uint32_t y) {
-    uint32_t x0 = max((int32_t)x-2, 0);
-    uint32_t x1 = max((int32_t)x-1, 0);
-    uint32_t x2 = x;
-    uint32_t x3 = min((int32_t)x+1, gWidth-1);
-    uint32_t x4 = min((int32_t)x+2, gWidth-1);
-
-    uint32_t y0 = max((int32_t)y-2, 0);
-    uint32_t y1 = max((int32_t)y-1, 0);
-    uint32_t y2 = y;
-    uint32_t y3 = min((int32_t)y+1, gHeight-1);
-    uint32_t y4 = min((int32_t)y+2, gHeight-1);
-
-    float4 p0 = convert_float4(GetElementAt_uchar4(gIn, x0, y0)) * gCoeffs[0]
-              + convert_float4(GetElementAt_uchar4(gIn, x1, y0)) * gCoeffs[1]
-              + convert_float4(GetElementAt_uchar4(gIn, x2, y0)) * gCoeffs[2]
-              + convert_float4(GetElementAt_uchar4(gIn, x3, y0)) * gCoeffs[3]
-              + convert_float4(GetElementAt_uchar4(gIn, x4, y0)) * gCoeffs[4];
-
-    float4 p1 = convert_float4(GetElementAt_uchar4(gIn, x0, y1)) * gCoeffs[5]
-              + convert_float4(GetElementAt_uchar4(gIn, x1, y1)) * gCoeffs[6]
-              + convert_float4(GetElementAt_uchar4(gIn, x2, y1)) * gCoeffs[7]
-              + convert_float4(GetElementAt_uchar4(gIn, x3, y1)) * gCoeffs[8]
-              + convert_float4(GetElementAt_uchar4(gIn, x4, y1)) * gCoeffs[9];
-
-    float4 p2 = convert_float4(GetElementAt_uchar4(gIn, x0, y2)) * gCoeffs[10]
-              + convert_float4(GetElementAt_uchar4(gIn, x1, y2)) * gCoeffs[11]
-              + convert_float4(GetElementAt_uchar4(gIn, x2, y2)) * gCoeffs[12]
-              + convert_float4(GetElementAt_uchar4(gIn, x3, y2)) * gCoeffs[13]
-              + convert_float4(GetElementAt_uchar4(gIn, x4, y2)) * gCoeffs[14];
-
-    float4 p3 = convert_float4(GetElementAt_uchar4(gIn, x0, y3)) * gCoeffs[15]
-              + convert_float4(GetElementAt_uchar4(gIn, x1, y3)) * gCoeffs[16]
-              + convert_float4(GetElementAt_uchar4(gIn, x2, y3)) * gCoeffs[17]
-              + convert_float4(GetElementAt_uchar4(gIn, x3, y3)) * gCoeffs[18]
-              + convert_float4(GetElementAt_uchar4(gIn, x4, y3)) * gCoeffs[19];
-
-    float4 p4 = convert_float4(GetElementAt_uchar4(gIn, x0, y4)) * gCoeffs[20]
-              + convert_float4(GetElementAt_uchar4(gIn, x1, y4)) * gCoeffs[21]
-              + convert_float4(GetElementAt_uchar4(gIn, x2, y4)) * gCoeffs[22]
-              + convert_float4(GetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23]
-              + convert_float4(GetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
-
-    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
-    *out = convert_uchar4(p0);
-}
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.fs
similarity index 83%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.fs
index ffdcfe3..6595874 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.fs
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-#include "levels.rsh"
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    return v_in;
+}
+
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.rs
deleted file mode 100644
index 17f7cff..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.rs
+++ /dev/null
@@ -1,24 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-
-void root(const uchar4 *in, uchar4 *out) {
-    *out = *in;
-}
-
-
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/exposure.rs
similarity index 66%
copy from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/exposure.rs
index b3a7ef0..0f05cb9 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/exposure.rs
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+static float bright = 0.f;
 
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
-
-    float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
+void setBright(float v) {
+    bright = 255.f / (255.f - v);
 }
 
+void exposure(const uchar4 *in, uchar4 *out)
+{
+    out->r = rsClamp((int)(bright * in->r), 0, 255);
+    out->g = rsClamp((int)(bright * in->g), 0, 255);
+    out->b = rsClamp((int)(bright * in->b), 0, 255);
+}
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye.rsh
new file mode 100644
index 0000000..2eacb7d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+    alpha = k * 2.0f + 0.75f;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+    
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound = sqrt(bound2);
+    const float radius = 1.15f * bound;
+    radius2 = radius*radius;
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = rsqrt(dist2);
+    const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    return rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx.rsh b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx.rsh
new file mode 100644
index 0000000..fcf0a3d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+    alpha = k * 2.0f + 0.75f;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float bound2 = 0.25f * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound = sqrt(bound2);
+    const float radius = 1.15f * bound;
+    radius2 = radius*radius;
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = half_rsqrt(dist2);
+    const float radian = M_PI_2 - atan((alpha * half_sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    return rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_full.rs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_full.rs
index 64d27ed..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_full.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_relaxed.fs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_relaxed.fs
index 64d27ed..ed69ff4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_full.rs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_full.rs
index 990310b..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_full.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_relaxed.fs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_relaxed.fs
index 990310b..f986b5d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/fisheye_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "fisheye.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.fs
similarity index 87%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.fs
index 44320a5..2e62cd7 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.fs
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
-void genRand(uchar *out) {
-    *out = (uchar)rsRand(0xff);
+uchar __attribute__((kernel)) genRand() {
+    return (uchar)rsRand(0xff);
 }
 
 /*
@@ -42,7 +40,7 @@
 int32_t gHMask;
 
 rs_allocation gBlendSource;
-void blend9(uchar *out, uint32_t x, uint32_t y) {
+uchar __attribute__((kernel)) blend9(uint32_t x, uint32_t y) {
     uint32_t x1 = (x-1) & gWMask;
     uint32_t x2 = (x+1) & gWMask;
     uint32_t y1 = (y-1) & gHMask;
@@ -70,14 +68,14 @@
     p20 += p02;
 
     p20 = min(p20 >> 10, (uint)255);
-    *out = (uchar)p20;
+    return (uchar)p20;
 }
 
 float gNoiseStrength;
 
 rs_allocation gNoise;
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float4 ip = convert_float4(*in);
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    float4 ip = convert_float4(in);
     float pnoise = (float) rsGetElementAt_uchar(gNoise, x & gWMask, y & gHMask);
 
     float energy_level = ip.r + ip.g + ip.b;
@@ -89,5 +87,5 @@
 
     uchar4 p = convert_uchar4(ip);
     p.a = 0xff;
-    *out = p;
+    return p;
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.rs
deleted file mode 100644
index 885ef49..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/grain.rs
+++ /dev/null
@@ -1,101 +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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
-
-void genRand(uchar *out) {
-    *out = (uchar)rsRand(0xff);
-}
-
-static inline uchar GetElementAt_uchar(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((uchar *)rsGetElementAt(a, x, y))[0];
-}
-
-static inline float4 GetElementAt_float4(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((float4 *)rsGetElementAt(a, x, y))[0];
-}
-
-/*
- * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
- * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
- * small values are not calculated to gain efficiency.
- * The order ot pixels represented in this matrix is:
- *  1  2  3
- *  4  0  5
- *  6  7  8
- *  and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
- *  However, since most of the valus are identical, we only use the first three
- *  entries and the entries corresponding to the pixels is:
- *  1  2  1
- *  2  0  2
- *  1  2  1
- */
-
-int32_t gWMask;
-int32_t gHMask;
-
-rs_allocation gBlendSource;
-void blend9(uchar *out, uint32_t x, uint32_t y) {
-    uint32_t x1 = (x-1) & gWMask;
-    uint32_t x2 = (x+1) & gWMask;
-    uint32_t y1 = (y-1) & gHMask;
-    uint32_t y2 = (y+1) & gHMask;
-
-    uint p00 = 56 *  GetElementAt_uchar(gBlendSource, x1, y1);
-    uint p01 = 114 * GetElementAt_uchar(gBlendSource, x, y1);
-    uint p02 = 56 *  GetElementAt_uchar(gBlendSource, x2, y1);
-    uint p10 = 114 * GetElementAt_uchar(gBlendSource, x1, y);
-    uint p11 = 230 * GetElementAt_uchar(gBlendSource, x, y);
-    uint p12 = 114 * GetElementAt_uchar(gBlendSource, x2, y);
-    uint p20 = 56 *  GetElementAt_uchar(gBlendSource, x1, y2);
-    uint p21 = 114 * GetElementAt_uchar(gBlendSource, x, y2);
-    uint p22 = 56 *  GetElementAt_uchar(gBlendSource, x2, y2);
-
-    p00 += p01;
-    p02 += p10;
-    p11 += p12;
-    p20 += p21;
-
-    p22 += p00;
-    p02 += p11;
-
-    p20 += p22;
-    p20 += p02;
-
-    p20 = min(p20 >> 10, (uint)255);
-    *out = (uchar)p20;
-}
-
-float gNoiseStrength;
-
-rs_allocation gNoise;
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float4 ip = convert_float4(*in);
-    float pnoise = (float) GetElementAt_uchar(gNoise, x & gWMask, y & gHMask);
-
-    float energy_level = ip.r + ip.g + ip.b;
-    float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f;
-    pnoise = (pnoise - 128.f) * energy_mask;
-
-    ip += pnoise * gNoiseStrength;
-    ip = clamp(ip, 0.f, 255.f);
-
-    uchar4 p = convert_uchar4(ip);
-    p.a = 0xff;
-    *out = p;
-}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.fs
similarity index 66%
copy from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.fs
index b3a7ef0..4e13072 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.fs
@@ -14,17 +14,23 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    float4 f4 = rsUnpackColor8888(v_in);
 
     float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
+    return rsPackColorTo8888(mono);
 }
 
+uchar __attribute__((kernel)) toU8(uchar4 v_in) {
+    float4 f4 = convert_float4(v_in);
+    return (uchar)dot(f4.rgb, gMonoMult);
+}
+
+uchar4 __attribute__((kernel)) toU8_4(uchar v_in) {
+    return (uchar4)v_in;
+}
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ip.rsh
similarity index 86%
rename from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ip.rsh
index b2564ef..8124211 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/ip.rsh
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -16,7 +16,5 @@
 
 #pragma version(1)
 #pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
 
-#include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels.rsh b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels.rsh
index a3a2775..e289906 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels.rsh
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels.rsh
@@ -21,24 +21,26 @@
 float overInWMinInB;
 rs_matrix3x3 colorMat;
 
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float3 pixel = convert_float4(*in).rgb;
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    uchar4 out;
+    float3 pixel = convert_float4(in).rgb;
     pixel = rsMatrixMultiply(&colorMat, pixel);
     pixel = clamp(pixel, 0.f, 255.f);
     pixel = (pixel - inBlack) * overInWMinInB;
     pixel = pixel * outWMinOutB + outBlack;
     pixel = clamp(pixel, 0.f, 255.f);
-    out->xyz = convert_uchar3(pixel);
-    out->w = 0xff;
+    out.xyz = convert_uchar3(pixel);
+    out.w = 0xff;
+    return out;
 }
 
-void root4(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
-    float4 pixel = convert_float4(*in);
+uchar4 __attribute__((kernel)) root4(uchar4 in, uint32_t x, uint32_t y) {
+    float4 pixel = convert_float4(in);
     pixel.rgb = rsMatrixMultiply(&colorMat, pixel.rgb);
     pixel = clamp(pixel, 0.f, 255.f);
     pixel = (pixel - inBlack) * overInWMinInB;
     pixel = pixel * outWMinOutB + outBlack;
     pixel = clamp(pixel, 0.f, 255.f);
-    *out = convert_uchar4(pixel);
+    return convert_uchar4(pixel);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_full.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_full.rs
index dead169..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.fs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.fs
index ffdcfe3..28596ba 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/levels_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "levels.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/mandelbrot.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/mandelbrot.rs
index 7cd8488..de0bd00 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/mandelbrot.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/mandelbrot.rs
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
+#include "ip.rsh"
 
 uint32_t gMaxIteration = 500;
 uint32_t gDimX = 1024;
@@ -23,7 +22,7 @@
 float lowerBoundY = -2.f;
 float scaleFactor = 4.f;
 
-void root(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uint32_t x, uint32_t y) {
   float2 p;
   p.x = lowerBoundX + ((float)x / gDimX) * scaleFactor;
   p.y = lowerBoundY + ((float)y / gDimY) * scaleFactor;
@@ -41,16 +40,16 @@
 
   if(iter >= gMaxIteration) {
     // write a non-transparent black pixel
-    *out = (uchar4){0, 0, 0, 0xff};
+    return (uchar4){0, 0, 0, 0xff};
   } else {
     float mi3 = gMaxIteration / 3.f;
     if (iter <= (gMaxIteration / 3))
-      *out = (uchar4){0xff * (iter / mi3), 0, 0, 0xff};
+      return (uchar4){0xff * (iter / mi3), 0, 0, 0xff};
     else if (iter <= (((gMaxIteration / 3) * 2)))
-      *out = (uchar4){0xff - (0xff * ((iter - mi3) / mi3)),
+      return (uchar4){0xff - (0xff * ((iter - mi3) / mi3)),
                       (0xff * ((iter - mi3) / mi3)), 0, 0xff};
     else
-      *out = (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)),
+      return (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)),
                       (0xff * ((iter - (mi3 * 2)) / mi3)), 0xff};
   }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/shadows.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/shadows.rs
new file mode 100644
index 0000000..f6c149d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/shadows.rs
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static double shadowFilterMap[] = {
+    -0.00591,  0.0001,
+     1.16488,  0.01668,
+    -0.18027, -0.06791,
+    -0.12625,  0.09001,
+     0.15065, -0.03897
+};
+
+static double poly[] = {
+    0., 0.,
+    0., 0.,
+    0.
+};
+
+static const int ABITS = 4;
+static const int HSCALE = 256;
+static const int k1=255 << ABITS;
+static const int k2=HSCALE << ABITS;
+
+static double fastevalPoly(double *poly,int n, double x){
+
+    double f =x;
+    double sum = poly[0]+poly[1]*f;
+    int i;
+    for (i = 2; i < n; i++) {
+        f*=x;
+        sum += poly[i]*f;
+    }
+    return sum;
+}
+
+static ushort3 rgb2hsv( uchar4 rgb)
+{
+    int iMin,iMax,chroma;
+
+    int ri = rgb.r;
+    int gi = rgb.g;
+    int bi = rgb.b;
+    short rv,rs,rh;
+
+    if (ri > gi) {
+        iMax = max (ri, bi);
+        iMin = min (gi, bi);
+    } else {
+        iMax = max (gi, bi);
+        iMin = min (ri, bi);
+    }
+
+    chroma = iMax - iMin;
+    // set value
+    rv = (short)( iMax << ABITS);
+
+    // set saturation
+    if (rv == 0)
+        rs = 0;
+    else
+        rs = (short)((k1*chroma)/iMax);
+
+    // set hue
+    if (rs == 0)
+        rh = 0;
+    else {
+        if ( ri == iMax ) {
+            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
+            if (rh >= k2) rh -= k2;
+        } else if (gi  == iMax)
+            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
+        else // (bi == iMax )
+                    rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
+    }
+
+    ushort3 out;
+    out.x = rv;
+    out.y = rs;
+    out.z = rh;
+    return out;
+}
+
+static uchar4 hsv2rgb(ushort3 hsv)
+{
+    int ABITS = 4;
+    int HSCALE = 256;
+    int m;
+    int H,X,ih,is,iv;
+    int k1=255<<ABITS;
+    int k2=HSCALE<<ABITS;
+    int k3=1<<(ABITS-1);
+    int rr=0;
+    int rg=0;
+    int rb=0;
+    short cv = hsv.x;
+    short cs = hsv.y;
+    short ch = hsv.z;
+
+    // set chroma and min component value m
+    //chroma = ( cv * cs )/k1;
+    //m = cv - chroma;
+    m = ((int)cv*(k1 - (int)cs ))/k1;
+
+    // chroma  == 0 <-> cs == 0 --> m=cv
+    if (cs == 0) {
+        rb = ( rg = ( rr =( cv >> ABITS) ));
+    } else {
+        ih=(int)ch;
+        is=(int)cs;
+        iv=(int)cv;
+
+        H = (6*ih)/k2;
+        X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+
+        // removing additional bits --> unit8
+        X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
+        m=m >> ABITS;
+
+        // ( chroma + m ) --> cv ;
+        cv=(short) (cv >> ABITS);
+        switch (H) {
+        case 0:
+            rr = cv;
+            rg = X;
+            rb = m;
+            break;
+        case 1:
+            rr = X;
+            rg = cv;
+            rb = m;
+            break;
+        case 2:
+            rr = m;
+            rg = cv;
+            rb = X;
+            break;
+        case 3:
+            rr = m;
+            rg = X;
+            rb = cv;
+            break;
+        case 4:
+            rr = X;
+            rg = m;
+            rb = cv;
+            break;
+        case 5:
+            rr = cv;
+            rg = m ;
+            rb = X;
+            break;
+        }
+    }
+
+    uchar4 rgb;
+
+    rgb.r =  rr;
+    rgb.g =  rg;
+    rgb.b =  rb;
+
+    return rgb;
+}
+
+void prepareShadows(float scale) {
+    double s = (scale>=0)?scale:scale/5;
+    for (int i = 0; i < 5; i++) {
+        poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
+    }
+}
+
+void shadowsKernel(const uchar4 *in, uchar4 *out) {
+    ushort3 hsv = rgb2hsv(*in);
+    double v = (fastevalPoly(poly,5,hsv.x/4080.)*4080);
+    if (v>4080) v = 4080;
+    hsv.x = (unsigned short) ((v>0)?v:0);
+    *out = hsv2rgb(hsv);
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.fs
similarity index 67%
copy from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.fs
index d18117a..0b2c2e8 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/threshold.fs
@@ -1,6 +1,20 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+/*
+ * 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 "ip.rsh"
 
 
 int height;
@@ -56,51 +70,49 @@
     }
 }
 
-void copyIn(const uchar4 *in, float4 *out) {
-    *out = convert_float4(*in);
+float4 __attribute__((kernel)) copyIn(uchar4 in) {
+    return convert_float4(in);
 }
 
-static inline float4 GetElementAt_float4(rs_allocation a, uint32_t x, uint32_t y) {
-    return ((float4 *)rsGetElementAt(a, x, y))[0];
-}
-
-void vert(uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) vert(uint32_t x, uint32_t y) {
     float3 blurredPixel = 0;
     int gi = 0;
+    uchar4 out;
     if ((y > radius) && (y < (height - radius))) {
         for (int r = -radius; r <= radius; r ++) {
-            float4 i = GetElementAt_float4(ScratchPixel2, x, y + r);
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, y + r);
             blurredPixel += i.xyz * gaussian[gi++];
         }
     } else {
         for (int r = -radius; r <= radius; r ++) {
             int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
-            float4 i = GetElementAt_float4(ScratchPixel2, x, validH);
+            float4 i = rsGetElementAt_float4(ScratchPixel2, x, validH);
             blurredPixel += i.xyz * gaussian[gi++];
         }
     }
 
-    out->xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
-    out->w = 0xff;
+    out.xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f));
+    out.w = 0xff;
+    return out;
 }
 
-void horz(float4 *out, uint32_t x, uint32_t y) {
+float4 __attribute__((kernel)) horz(uint32_t x, uint32_t y) {
     float4 blurredPixel = 0;
     int gi = 0;
     if ((x > radius) && (x < (width - radius))) {
         for (int r = -radius; r <= radius; r ++) {
-            float4 i = GetElementAt_float4(ScratchPixel1, x + r, y);
+            float4 i = rsGetElementAt_float4(ScratchPixel1, x + r, y);
             blurredPixel += i * gaussian[gi++];
         }
     } else {
         for (int r = -radius; r <= radius; r ++) {
             // Stepping left and right away from the pixel
             int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
-            float4 i = GetElementAt_float4(ScratchPixel1, validX, y);
+            float4 i = rsGetElementAt_float4(ScratchPixel1, validX, y);
             blurredPixel += i * gaussian[gi++];
         }
     }
 
-    *out = blurredPixel;
+    return blurredPixel;
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vibrance.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vibrance.rs
new file mode 100644
index 0000000..8db113c
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vibrance.rs
@@ -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.
+ */
+
+#include "ip.rsh"
+
+float vibrance = 0.f;
+
+static const float Rf = 0.2999f;
+static const float Gf = 0.587f;
+static const float Bf = 0.114f;
+
+static float S  = 0.f;
+static float MS = 0.f;
+static float Rt = 0.f;
+static float Gt = 0.f;
+static float Bt = 0.f;
+static float Vib = 0.f;
+
+void vibranceKernel(const uchar4 *in, uchar4 *out) {
+
+    float R, G, B;
+
+    int r = in->r;
+    int g = in->g;
+    int b = in->b;
+    float red = (r-max(g, b))/256.f;
+    float sx = (float)(Vib/(1+exp(-red*3)));
+    S = sx+1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+    int t = (r + g) / 2;
+    R = r;
+    G = g;
+    B = b;
+
+    float Rc = R * (Rt + S) + G * Gt + B * Bt;
+    float Gc = R * Rt + G * (Gt + S) + B * Bt;
+    float Bc = R * Rt + G * Gt + B * (Bt + S);
+
+    out->r = rsClamp(Rc, 0, 255);
+    out->g = rsClamp(Gc, 0, 255);
+    out->b = rsClamp(Bc, 0, 255);
+
+}
+
+void prepareVibrance() {
+
+    Vib = vibrance/100.f;
+    S  = Vib + 1;
+    MS = 1.0f - S;
+    Rt = Rf * MS;
+    Gt = Gf * MS;
+    Bt = Bf * MS;
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette.rsh b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette.rsh
index 3fef0c3..04ca1f1 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette.rsh
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette.rsh
@@ -44,9 +44,9 @@
     opp_shade = 1.f - desired_shade;
 }
 
-void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
-    const float4 fin = convert_float4(*in);
+    const float4 fin = convert_float4(in);
     const float2 inCoord = {(float)x, (float)y};
     const float2 coord = mad(inCoord, inv_dimensions, neg_center);
     const float sloped_dist_ratio = length(axis_scale * coord)  * sloped_inv_max_dist;
@@ -54,6 +54,6 @@
     float4 fout;
     fout.rgb = fin.rgb * lumen;
     fout.w = fin.w;
-    *out = convert_uchar4(fout);
+    return convert_uchar4(fout);
 }
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx.rsh b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx.rsh
new file mode 100644
index 0000000..0eacdc8
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx.rsh
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+static float2 neg_center, axis_scale, inv_dimensions;
+static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade;
+
+void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y,
+        float desired_scale, float desired_shade, float desired_slope) {
+
+    neg_center.x = -center_x;
+    neg_center.y = -center_y;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float max_dist = 0.5f * length(axis_scale);
+    sloped_inv_max_dist = desired_slope * 1.f/max_dist;
+
+    // Range needs to be between 1.3 to 0.6. When scale is zero then range is
+    // 1.3 which means no vignette at all because the luminousity difference is
+    // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
+    const float neg_range = 0.7f*sqrt(desired_scale) - 1.3f;
+    sloped_neg_range = exp(neg_range * desired_slope);
+
+    shade = desired_shade;
+    opp_shade = 1.f - desired_shade;
+}
+
+uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float4 fin = convert_float4(in);
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float sloped_dist_ratio = fast_length(axis_scale * coord)  * sloped_inv_max_dist;
+    const float lumen = opp_shade + shade * half_recip(1.f + sloped_neg_range * exp(sloped_dist_ratio));
+    float4 fout;
+    fout.rgb = fin.rgb * lumen;
+    fout.w = fin.w;
+    return convert_uchar4(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_full.rs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_full.rs
index b714e9b..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_full.rs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_relaxed.fs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_relaxed.fs
index b714e9b..00cbbc4 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_approx_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette_approx.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_full.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_full.rs
index c5b08a9..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_full.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_full.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.fs
similarity index 86%
rename from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.rs
rename to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.fs
index 339b747..8202c5c 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.fs
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 #include "vignette.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/wbalance.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/wbalance.rs
new file mode 100644
index 0000000..6650671
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/wbalance.rs
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include "ip.rsh"
+//#pragma rs_fp_relaxed
+
+static int histR[256] = {0}, histG[256] = {0}, histB[256] = {0};
+
+rs_allocation histogramSource;
+uint32_t histogramHeight;
+uint32_t histogramWidth;
+
+static float scaleR;
+static float scaleG;
+static float scaleB;
+
+static uchar4 estimateWhite() {
+
+    for (int i = 0; i < 256; i++) {
+        histR[i] = 0; histG[i] = 0; histB[i] = 0;
+    }
+
+    for (uint32_t i = 0; i < histogramHeight; i++) {
+        for (uint32_t j = 0; j < histogramWidth; j++) {
+            uchar4 in = rsGetElementAt_uchar4(histogramSource, j, i);
+            histR[in.r]++;
+            histG[in.g]++;
+            histB[in.b]++;
+        }
+    }
+
+    int min_r = -1, min_g = -1, min_b = -1;
+    int max_r =  0, max_g =  0, max_b =  0;
+    int sum_r =  0, sum_g =  0, sum_b =  0;
+
+    for (int i = 1; i < 255; i++) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        sum_r += r;
+        sum_g += g;
+        sum_b += b;
+
+        if (r>0){
+            if (min_r < 0) min_r = i;
+            max_r = i;
+        }
+        if (g>0){
+            if (min_g < 0) min_g = i;
+            max_g = i;
+        }
+        if (b>0){
+            if (min_b < 0) min_b = i;
+            max_b = i;
+        }
+    }
+
+    int sum15r = 0, sum15g = 0, sum15b = 0;
+    int count15r = 0, count15g = 0, count15b = 0;
+    int tmp_r = 0, tmp_g = 0, tmp_b = 0;
+
+    for (int i = 254; i >0; i--) {
+        int r = histR[i];
+        int g = histG[i];
+        int b = histB[i];
+        tmp_r += r;
+        tmp_g += g;
+        tmp_b += b;
+
+        if ((tmp_r > sum_r/20) && (tmp_r < sum_r/5)) {
+            sum15r += r*i;
+            count15r += r;
+        }
+        if ((tmp_g > sum_g/20) && (tmp_g < sum_g/5)) {
+            sum15g += g*i;
+            count15g += g;
+        }
+        if ((tmp_b > sum_b/20) && (tmp_b < sum_b/5)) {
+            sum15b += b*i;
+            count15b += b;
+        }
+
+    }
+
+    uchar4 out;
+
+    if ((count15r>0) && (count15g>0) && (count15b>0) ){
+        out.r = sum15r/count15r;
+        out.g = sum15g/count15g;
+        out.b = sum15b/count15b;
+    }else {
+        out.r = out.g = out.b = 255;
+    }
+
+    return out;
+
+}
+
+void prepareWhiteBalance() {
+    uchar4 estimation = estimateWhite();
+    int minimum = min(estimation.r, min(estimation.g, estimation.b));
+    int maximum = max(estimation.r, max(estimation.g, estimation.b));
+    float avg = (minimum + maximum) / 2.f;
+
+    scaleR =  avg/estimation.r;
+    scaleG =  avg/estimation.g;
+    scaleB =  avg/estimation.b;
+
+}
+
+static unsigned char contrastClamp(int c)
+{
+    int N = 255;
+    c &= ~(c >> 31);
+    c -= N;
+    c &= (c >> 31);
+    c += N;
+    return  (unsigned char) c;
+}
+
+void whiteBalanceKernel(const uchar4 *in, uchar4 *out) {
+    float Rc =  in->r*scaleR;
+    float Gc =  in->g*scaleG;
+    float Bc =  in->b*scaleB;
+
+    out->r = contrastClamp(Rc);
+    out->g = contrastClamp(Gc);
+    out->b = contrastClamp(Bc);
+}
diff --git a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_alloc.java b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_alloc.java
index da42b29..079fcce 100644
--- a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_alloc.java
+++ b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_alloc.java
@@ -39,6 +39,7 @@
         typeBuilder.setX(X).setY(Y);
         Allocation A = Allocation.createTyped(RS, typeBuilder.create());
         s.bind_a(A);
+        s.set_aRaw(A);
 
         typeBuilder = new Type.Builder(RS, Element.I32(RS));
         typeBuilder.setX(X).setY(Y).setFaces(true);
@@ -56,9 +57,10 @@
 
     public void run() {
         RenderScript pRS = RenderScript.create(mCtx);
-        ScriptC_alloc s = new ScriptC_alloc(pRS, mRes, R.raw.alloc);
+        ScriptC_alloc s = new ScriptC_alloc(pRS);
         pRS.setMessageHandler(mRsMessage);
         initializeGlobals(pRS, s);
+        s.forEach_root(s.get_aRaw());
         s.invoke_alloc_test();
         pRS.finish();
         waitForMessage();
diff --git a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_foreach.java b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_foreach.java
index aeb5bb7..31f4114 100644
--- a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_foreach.java
+++ b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/UT_foreach.java
@@ -37,17 +37,18 @@
         s.set_dimY(Y);
         typeBuilder.setX(X).setY(Y);
         A = Allocation.createTyped(RS, typeBuilder.create());
-        s.bind_a(A);
+        s.set_aRaw(A);
 
         return;
     }
 
     public void run() {
         RenderScript pRS = RenderScript.create(mCtx);
-        ScriptC_foreach s = new ScriptC_foreach(pRS, mRes, R.raw.foreach);
+        ScriptC_foreach s = new ScriptC_foreach(pRS);
         pRS.setMessageHandler(mRsMessage);
         initializeGlobals(pRS, s);
         s.forEach_root(A);
+        s.invoke_verify_root();
         s.invoke_foreach_test();
         pRS.finish();
         waitForMessage();
diff --git a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/alloc.rs b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/alloc.rs
index 3116e5a..1b5e2ac 100644
--- a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/alloc.rs
+++ b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/alloc.rs
@@ -5,39 +5,37 @@
 int dimY;
 int dimZ;
 
+rs_allocation aRaw;
 rs_allocation aFaces;
 rs_allocation aLOD;
 rs_allocation aFacesLOD;
 
+void root(int *o, uint32_t x, uint32_t y) {
+    *o = x + y * dimX;
+}
+
 static bool test_alloc_dims() {
     bool failed = false;
     int i, j;
 
-    for (j = 0; j < dimY; j++) {
-        for (i = 0; i < dimX; i++) {
-            a[i + j * dimX] = i + j * dimX;
-        }
-    }
-
-    rs_allocation alloc = rsGetAllocation(a);
-    _RS_ASSERT(rsAllocationGetDimX(alloc) == dimX);
-    _RS_ASSERT(rsAllocationGetDimY(alloc) == dimY);
-    _RS_ASSERT(rsAllocationGetDimZ(alloc) == dimZ);
+    _RS_ASSERT(rsAllocationGetDimX(aRaw) == dimX);
+    _RS_ASSERT(rsAllocationGetDimY(aRaw) == dimY);
+    _RS_ASSERT(rsAllocationGetDimZ(aRaw) == dimZ);
 
     // Test 2D addressing
     for (j = 0; j < dimY; j++) {
         for (i = 0; i < dimX; i++) {
             rsDebug("Verifying ", i + j * dimX);
-            const void *p = rsGetElementAt(alloc, i, j);
+            const void *p = rsGetElementAt(aRaw, i, j);
             int val = *(const int *)p;
             _RS_ASSERT(val == (i + j * dimX));
         }
     }
 
     // Test 1D addressing
-    for (i = 0; i < dimX * dimY; i++) {
+    for (i = 0; i < dimX; i++) {
         rsDebug("Verifying ", i);
-        const void *p = rsGetElementAt(alloc, i);
+        const void *p = rsGetElementAt(aRaw, i);
         int val = *(const int *)p;
         _RS_ASSERT(val == i);
     }
@@ -46,7 +44,7 @@
     for (j = 0; j < dimY; j++) {
         for (i = 0; i < dimX; i++) {
             rsDebug("Verifying ", i + j * dimX);
-            const void *p = rsGetElementAt(alloc, i, j, 0);
+            const void *p = rsGetElementAt(aRaw, i, j, 0);
             int val = *(const int *)p;
             _RS_ASSERT(val == (i + j * dimX));
         }
diff --git a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/foreach.rs b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/foreach.rs
index 3ba3eef..3fa8f85 100644
--- a/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/foreach.rs
+++ b/tests/RenderScriptTests/tests_v14/src/com/android/rs/test/foreach.rs
@@ -1,37 +1,40 @@
 #include "shared.rsh"
 
-int *a;
+rs_allocation aRaw;
 int dimX;
 int dimY;
+static bool failed = false;
 
 void root(int *out, uint32_t x, uint32_t y) {
     *out = x + y * dimX;
 }
 
-static bool test_foreach_output() {
+static bool test_root_output() {
     bool failed = false;
     int i, j;
 
     for (j = 0; j < dimY; j++) {
         for (i = 0; i < dimX; i++) {
-            _RS_ASSERT(a[i + j * dimX] == (i + j * dimX));
+            int v = rsGetElementAt_int(aRaw, i, j);
+            _RS_ASSERT(v == (i + j * dimX));
         }
     }
 
     if (failed) {
-        rsDebug("test_foreach_output FAILED", 0);
+        rsDebug("test_root_output FAILED", 0);
     }
     else {
-        rsDebug("test_foreach_output PASSED", 0);
+        rsDebug("test_root_output PASSED", 0);
     }
 
     return failed;
 }
 
-void foreach_test() {
-    bool failed = false;
-    failed |= test_foreach_output();
+void verify_root() {
+    failed |= test_root_output();
+}
 
+void foreach_test() {
     if (failed) {
         rsSendToClientBlocking(RS_MSG_TEST_FAILED);
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index d4ec289..ed5d22c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -3319,9 +3319,14 @@
                     }
                 case CMD_RECONNECT:
                 case CMD_REASSOCIATE:
-                    // Drop a third party reconnect/reassociate if we are
-                    // tempoarily disconnected for p2p
-                    if (mTemporarilyDisconnectWifi) ret = NOT_HANDLED;
+                    if (mTemporarilyDisconnectWifi) {
+                        // Drop a third party reconnect/reassociate if STA is
+                        // temporarily disconnected for p2p
+                        break;
+                    } else {
+                        // ConnectModeState handles it
+                        ret = NOT_HANDLED;
+                    }
                     break;
                 default:
                     ret = NOT_HANDLED;